Skip to content

Commit da5d854

Browse files
authored
feat(query): add catalog apis (#17493)
* z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z * z
1 parent 791e3a8 commit da5d854

File tree

10 files changed

+657
-1
lines changed

10 files changed

+657
-1
lines changed

src/query/service/src/servers/http/middleware/session.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub enum EndpointKind {
9393
Verify,
9494
UploadToStage,
9595
SystemInfo,
96+
Catalog,
9697
}
9798

9899
impl EndpointKind {
@@ -107,6 +108,7 @@ impl EndpointKind {
107108
| EndpointKind::PollQuery
108109
| EndpointKind::Logout
109110
| EndpointKind::HeartBeat
111+
| EndpointKind::Catalog
110112
)
111113
}
112114
pub fn require_databend_token_type(&self) -> Result<Option<TokenType>> {
@@ -118,7 +120,8 @@ impl EndpointKind {
118120
| EndpointKind::Logout
119121
| EndpointKind::SystemInfo
120122
| EndpointKind::HeartBeat
121-
| EndpointKind::UploadToStage => {
123+
| EndpointKind::UploadToStage
124+
| EndpointKind::Catalog => {
122125
if GlobalConfig::instance().query.management_mode {
123126
Ok(None)
124127
} else {
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2021 Datafuse Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use chrono::DateTime;
16+
use chrono::Utc;
17+
use databend_common_ast::parser::Dialect;
18+
use databend_common_catalog::catalog::CatalogManager;
19+
use databend_common_exception::ErrorCode;
20+
use databend_common_exception::Result;
21+
use poem::error::InternalServerError;
22+
use poem::error::NotFound;
23+
use poem::error::Result as PoemResult;
24+
use poem::web::Json;
25+
use poem::web::Path;
26+
use poem::IntoResponse;
27+
use serde::Deserialize;
28+
use serde::Serialize;
29+
30+
use crate::interpreters::ShowCreateQuerySettings;
31+
use crate::interpreters::ShowCreateTableInterpreter;
32+
use crate::servers::http::v1::HttpQueryContext;
33+
34+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
35+
pub struct GetDatabaseTableResponse {
36+
pub table: Option<TableDetails>,
37+
pub warnings: Vec<String>,
38+
}
39+
40+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
41+
pub struct TableDetails {
42+
pub name: String,
43+
pub database: String,
44+
pub catalog: String,
45+
pub engine: String,
46+
pub create_time: DateTime<Utc>,
47+
pub num_rows: u64,
48+
pub data_size: u64,
49+
pub data_compressed_size: u64,
50+
pub index_size: u64,
51+
pub create_query: String,
52+
}
53+
54+
#[async_backtrace::framed]
55+
async fn handle(
56+
ctx: &HttpQueryContext,
57+
database: String,
58+
table: String,
59+
) -> Result<GetDatabaseTableResponse> {
60+
let tenant = ctx.session.get_current_tenant();
61+
let visibility_checker = ctx.session.get_visibility_checker(false).await?;
62+
63+
let catalog = CatalogManager::instance().get_default_catalog(Default::default())?;
64+
65+
let db = catalog.get_database(&tenant, &database).await?;
66+
if !visibility_checker.check_database_visibility(
67+
catalog.name().as_str(),
68+
db.name(),
69+
db.get_db_info().database_id.db_id,
70+
) {
71+
return Err(ErrorCode::UnknownDatabase(format!(
72+
"Unknown database '{}'",
73+
database
74+
)));
75+
}
76+
77+
let tbl = db.get_table(&table).await?;
78+
if !visibility_checker.check_table_visibility(
79+
catalog.name().as_str(),
80+
db.name(),
81+
tbl.name(),
82+
db.get_db_info().database_id.db_id,
83+
tbl.get_table_info().ident.table_id,
84+
) {
85+
return Err(ErrorCode::UnknownTable(format!(
86+
"Unknown table '{}'",
87+
table
88+
)));
89+
}
90+
91+
let info = tbl.get_table_info();
92+
let mut warnings = vec![];
93+
94+
let settings = ShowCreateQuerySettings {
95+
sql_dialect: Dialect::PostgreSQL,
96+
force_quoted_ident: false,
97+
quoted_ident_case_sensitive: true,
98+
hide_options_in_show_create_table: false,
99+
};
100+
let create_query = ShowCreateTableInterpreter::show_create_query(
101+
catalog.as_ref(),
102+
db.name(),
103+
tbl.as_ref(),
104+
&settings,
105+
)
106+
.await
107+
.unwrap_or_else(|e| {
108+
let msg = format!(
109+
"show create query of {}.{}.{} failed(ignored): {}",
110+
catalog.name(),
111+
database,
112+
table,
113+
e
114+
);
115+
log::warn!("{}", msg);
116+
warnings.push(msg);
117+
"".to_owned()
118+
});
119+
120+
Ok(GetDatabaseTableResponse {
121+
table: Some(TableDetails {
122+
name: tbl.name().to_string(),
123+
database: db.name().to_string(),
124+
catalog: catalog.name().to_string(),
125+
engine: info.meta.engine.clone(),
126+
create_time: info.meta.created_on,
127+
num_rows: info.meta.statistics.number_of_rows,
128+
data_size: info.meta.statistics.data_bytes,
129+
data_compressed_size: info.meta.statistics.compressed_data_bytes,
130+
index_size: info.meta.statistics.index_data_bytes,
131+
create_query,
132+
}),
133+
warnings,
134+
})
135+
}
136+
137+
#[poem::handler]
138+
#[async_backtrace::framed]
139+
pub async fn get_database_table_handler(
140+
ctx: &HttpQueryContext,
141+
Path((database, table)): Path<(String, String)>,
142+
) -> PoemResult<impl IntoResponse> {
143+
let resp = handle(ctx, database, table)
144+
.await
145+
.map_err(|e| match e.code() {
146+
ErrorCode::UNKNOWN_DATABASE | ErrorCode::UNKNOWN_TABLE => NotFound(e),
147+
_ => InternalServerError(e),
148+
})?;
149+
Ok(Json(resp))
150+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2021 Datafuse Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use databend_common_catalog::catalog::CatalogManager;
16+
use databend_common_exception::ErrorCode;
17+
use databend_common_exception::Result;
18+
use poem::error::InternalServerError;
19+
use poem::error::NotFound;
20+
use poem::error::Result as PoemResult;
21+
use poem::web::Json;
22+
use poem::web::Path;
23+
use poem::IntoResponse;
24+
use serde::Deserialize;
25+
use serde::Serialize;
26+
27+
use crate::servers::http::v1::HttpQueryContext;
28+
29+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
30+
pub struct ListDatabaseTableFieldsResponse {
31+
pub fields: Vec<FieldInfo>,
32+
pub warnings: Vec<String>,
33+
}
34+
35+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
36+
pub struct FieldInfo {
37+
pub name: String,
38+
pub r#type: String,
39+
pub nullable: bool,
40+
pub default: Option<String>,
41+
}
42+
43+
#[async_backtrace::framed]
44+
async fn handle(
45+
ctx: &HttpQueryContext,
46+
database: String,
47+
table: String,
48+
) -> Result<ListDatabaseTableFieldsResponse> {
49+
let tenant = ctx.session.get_current_tenant();
50+
let visibility_checker = ctx.session.get_visibility_checker(false).await?;
51+
52+
let catalog = CatalogManager::instance().get_default_catalog(Default::default())?;
53+
let db = catalog.get_database(&tenant, &database).await?;
54+
if !visibility_checker.check_database_visibility(
55+
catalog.name().as_str(),
56+
db.name(),
57+
db.get_db_info().database_id.db_id,
58+
) {
59+
return Err(ErrorCode::UnknownDatabase(format!(
60+
"Unknown database '{}'",
61+
database
62+
)));
63+
}
64+
65+
let tbl = db.get_table(&table).await?;
66+
if !visibility_checker.check_table_visibility(
67+
catalog.name().as_str(),
68+
db.name(),
69+
tbl.name(),
70+
db.get_db_info().database_id.db_id,
71+
tbl.get_table_info().ident.table_id,
72+
) {
73+
return Err(ErrorCode::UnknownTable(format!(
74+
"Unknown table '{}'",
75+
table
76+
)));
77+
}
78+
79+
let warnings = vec![];
80+
let mut fields = vec![];
81+
for field in &tbl.get_table_info().schema().fields {
82+
fields.push(FieldInfo {
83+
name: field.name.clone(),
84+
r#type: field.data_type.to_string(),
85+
nullable: field.is_nullable(),
86+
default: field.default_expr.clone(),
87+
});
88+
}
89+
Ok(ListDatabaseTableFieldsResponse { fields, warnings })
90+
}
91+
92+
#[poem::handler]
93+
#[async_backtrace::framed]
94+
pub async fn list_database_table_fields_handler(
95+
ctx: &HttpQueryContext,
96+
Path((database, table)): Path<(String, String)>,
97+
) -> PoemResult<impl IntoResponse> {
98+
let resp = handle(ctx, database, table)
99+
.await
100+
.map_err(|e| match e.code() {
101+
ErrorCode::UNKNOWN_DATABASE | ErrorCode::UNKNOWN_TABLE => NotFound(e),
102+
_ => InternalServerError(e),
103+
})?;
104+
Ok(Json(resp))
105+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2021 Datafuse Labs
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use chrono::DateTime;
16+
use chrono::Utc;
17+
use databend_common_catalog::catalog::CatalogManager;
18+
use databend_common_exception::ErrorCode;
19+
use databend_common_exception::Result;
20+
use poem::error::InternalServerError;
21+
use poem::error::NotFound;
22+
use poem::error::Result as PoemResult;
23+
use poem::web::Json;
24+
use poem::web::Path;
25+
use poem::IntoResponse;
26+
use serde::Deserialize;
27+
use serde::Serialize;
28+
29+
use crate::servers::http::v1::HttpQueryContext;
30+
31+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
32+
pub struct ListDatabaseTablesResponse {
33+
pub tables: Vec<TableInfo>,
34+
pub warnings: Vec<String>,
35+
}
36+
37+
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Default)]
38+
pub struct TableInfo {
39+
pub name: String,
40+
pub database: String,
41+
pub catalog: String,
42+
pub engine: String,
43+
pub create_time: DateTime<Utc>,
44+
pub num_rows: u64,
45+
pub data_size: u64,
46+
pub data_compressed_size: u64,
47+
pub index_size: u64,
48+
}
49+
50+
#[async_backtrace::framed]
51+
async fn handle(ctx: &HttpQueryContext, database: String) -> Result<ListDatabaseTablesResponse> {
52+
let tenant = ctx.session.get_current_tenant();
53+
let visibility_checker = ctx.session.get_visibility_checker(false).await?;
54+
55+
let catalog = CatalogManager::instance().get_default_catalog(Default::default())?;
56+
let db = catalog.get_database(&tenant, &database).await?;
57+
58+
if !visibility_checker.check_database_visibility(
59+
catalog.name().as_str(),
60+
db.name(),
61+
db.get_db_info().database_id.db_id,
62+
) {
63+
return Err(ErrorCode::UnknownDatabase(format!(
64+
"Unknown database '{}'",
65+
database
66+
)));
67+
}
68+
69+
let warnings = vec![];
70+
let tables = db
71+
.list_tables()
72+
.await?
73+
.into_iter()
74+
.filter(|tbl| {
75+
visibility_checker.check_table_visibility(
76+
catalog.name().as_str(),
77+
db.name(),
78+
tbl.name(),
79+
db.get_db_info().database_id.db_id,
80+
tbl.get_table_info().ident.table_id,
81+
)
82+
})
83+
.map(|tbl| {
84+
let info = tbl.get_table_info();
85+
TableInfo {
86+
name: tbl.name().to_string(),
87+
database: db.name().to_string(),
88+
catalog: catalog.name().clone(),
89+
engine: info.meta.engine.clone(),
90+
create_time: info.meta.created_on,
91+
num_rows: info.meta.statistics.number_of_rows,
92+
data_size: info.meta.statistics.data_bytes,
93+
data_compressed_size: info.meta.statistics.compressed_data_bytes,
94+
index_size: info.meta.statistics.index_data_bytes,
95+
}
96+
})
97+
.collect::<Vec<_>>();
98+
99+
Ok(ListDatabaseTablesResponse { tables, warnings })
100+
}
101+
102+
#[poem::handler]
103+
#[async_backtrace::framed]
104+
pub async fn list_database_tables_handler(
105+
ctx: &HttpQueryContext,
106+
Path(database): Path<String>,
107+
) -> PoemResult<impl IntoResponse> {
108+
let resp = handle(ctx, database).await.map_err(|e| match e.code() {
109+
ErrorCode::UNKNOWN_DATABASE => NotFound(e),
110+
_ => InternalServerError(e),
111+
})?;
112+
Ok(Json(resp))
113+
}

0 commit comments

Comments
 (0)