diff --git a/src/query/ast/src/ast/statements/dictionary.rs b/src/query/ast/src/ast/statements/dictionary.rs index 88508fb75c19..2ddf086c7e11 100644 --- a/src/query/ast/src/ast/statements/dictionary.rs +++ b/src/query/ast/src/ast/statements/dictionary.rs @@ -19,6 +19,7 @@ use std::fmt::Formatter; use derive_visitor::Drive; use derive_visitor::DriveMut; +use super::ShowLimit; use crate::ast::write_comma_separated_list; use crate::ast::write_dot_separated_list; use crate::ast::write_space_separated_string_map; @@ -123,3 +124,22 @@ impl Display for ShowCreateDictionaryStmt { ) } } + +#[derive(Debug, Clone, PartialEq, Drive, DriveMut)] +pub struct ShowDictionariesStmt { + pub database: Option, + pub limit: Option, +} + +impl Display for ShowDictionariesStmt { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "SHOW DICTIONARIES")?; + if let Some(database) = &self.database { + write!(f, " FROM {database}")?; + } + if let Some(limit) = &self.limit { + write!(f, " {limit}")?; + } + Ok(()) + } +} diff --git a/src/query/ast/src/ast/statements/statement.rs b/src/query/ast/src/ast/statements/statement.rs index 0607f1682d76..945679be3a69 100644 --- a/src/query/ast/src/ast/statements/statement.rs +++ b/src/query/ast/src/ast/statements/statement.rs @@ -158,9 +158,7 @@ pub enum Statement { CreateDictionary(CreateDictionaryStmt), DropDictionary(DropDictionaryStmt), ShowCreateDictionary(ShowCreateDictionaryStmt), - ShowDictionaries { - show_options: Option, - }, + ShowDictionaries(ShowDictionariesStmt), // Columns ShowColumns(ShowColumnsStmt), @@ -613,12 +611,7 @@ impl Display for Statement { Statement::CreateDictionary(stmt) => write!(f, "{stmt}")?, Statement::DropDictionary(stmt) => write!(f, "{stmt}")?, Statement::ShowCreateDictionary(stmt) => write!(f, "{stmt}")?, - Statement::ShowDictionaries { show_options } => { - write!(f, "SHOW DICTIONARIES")?; - if let Some(show_options) = show_options { - write!(f, " {show_options}")?; - } - } + Statement::ShowDictionaries(stmt) => write!(f, "{stmt}")?, Statement::CreateView(stmt) => write!(f, "{stmt}")?, Statement::AlterView(stmt) => write!(f, "{stmt}")?, Statement::DropView(stmt) => write!(f, "{stmt}")?, diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index f2e50528b5e9..8c547eba458e 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -957,9 +957,15 @@ pub fn statement_body(i: Input) -> IResult { ); let show_dictionaries = map( rule! { - SHOW ~ DICTIONARIES ~ #show_options? + SHOW ~ DICTIONARIES ~ ((FROM|IN) ~ #ident)? ~ #show_limit? + }, + |(_, _, db, limit)| { + let database = match db { + Some((_, d)) => Some(d), + _ => None, + }; + Statement::ShowDictionaries(ShowDictionariesStmt { database, limit }) }, - |(_, _, show_options)| Statement::ShowDictionaries { show_options }, ); let show_create_dictionary = map( rule! { diff --git a/src/query/service/src/databases/system/system_database.rs b/src/query/service/src/databases/system/system_database.rs index 0ae6e34ee9dc..93702af95b02 100644 --- a/src/query/service/src/databases/system/system_database.rs +++ b/src/query/service/src/databases/system/system_database.rs @@ -35,6 +35,7 @@ use databend_common_storages_system::ConfigsTable; use databend_common_storages_system::ContributorsTable; use databend_common_storages_system::CreditsTable; use databend_common_storages_system::DatabasesTable; +use databend_common_storages_system::DictionariesTable; use databend_common_storages_system::EnginesTable; use databend_common_storages_system::FullStreamsTable; use databend_common_storages_system::FunctionsTable; @@ -144,6 +145,7 @@ impl SystemDatabase { ViewsTableWithoutHistory::create(sys_db_meta.next_table_id()), TemporaryTablesTable::create(sys_db_meta.next_table_id()), ProceduresTable::create(sys_db_meta.next_table_id()), + DictionariesTable::create(sys_db_meta.next_table_id()), ]; let disable_tables = Self::disable_system_tables(); diff --git a/src/query/service/src/interpreters/access/privilege_access.rs b/src/query/service/src/interpreters/access/privilege_access.rs index 009e2a8154e4..0eaa433bc235 100644 --- a/src/query/service/src/interpreters/access/privilege_access.rs +++ b/src/query/service/src/interpreters/access/privilege_access.rs @@ -61,10 +61,11 @@ enum ObjectId { // some statements like `SELECT 1`, `SHOW USERS`, `SHOW ROLES`, `SHOW TABLES` will be // rewritten to the queries on the system tables, we need to skip the privilege check on // these tables. -const SYSTEM_TABLES_ALLOW_LIST: [&str; 19] = [ +const SYSTEM_TABLES_ALLOW_LIST: [&str; 20] = [ "catalogs", "columns", "databases", + "dictionaries", "tables", "views", "tables_with_history", @@ -709,7 +710,8 @@ impl AccessChecker for PrivilegeAccess { Some(RewriteKind::ShowDatabases) | Some(RewriteKind::ShowEngines) | Some(RewriteKind::ShowFunctions) - | Some(RewriteKind::ShowUserFunctions) => { + | Some(RewriteKind::ShowUserFunctions) + | Some(RewriteKind::ShowDictionaries(_)) => { return Ok(()); } | Some(RewriteKind::ShowTableFunctions) => { diff --git a/src/query/service/tests/it/storages/testdata/columns_table.txt b/src/query/service/tests/it/storages/testdata/columns_table.txt index 614f58070a07..df5879922802 100644 --- a/src/query/service/tests/it/storages/testdata/columns_table.txt +++ b/src/query/service/tests/it/storages/testdata/columns_table.txt @@ -15,6 +15,8 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'arguments' | 'system' | 'procedures' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'arguments' | 'system' | 'user_functions' | 'Variant' | 'VARIANT' | '' | '' | 'NO' | '' | | 'attempt_number' | 'system' | 'task_history' | 'Int32' | 'INT' | '' | '' | 'NO' | '' | +| 'attribute_names' | 'system' | 'dictionaries' | 'Array(String)' | 'ARRAY(STRING)' | '' | '' | 'NO' | '' | +| 'attribute_types' | 'system' | 'dictionaries' | 'Array(String)' | 'ARRAY(STRING)' | '' | '' | 'NO' | '' | | 'auth_type' | 'system' | 'users' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'auto_increment' | 'information_schema' | 'tables' | 'NULL' | 'NULL' | '' | '' | 'NO' | '' | | 'byte_size' | 'system' | 'clustering_history' | 'UInt64' | 'BIGINT UNSIGNED' | '' | '' | 'NO' | '' | @@ -59,6 +61,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'command' | 'system' | 'processes' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'comment' | 'information_schema' | 'statistics' | 'NULL' | 'NULL' | '' | '' | 'NO' | '' | | 'comment' | 'system' | 'columns' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | +| 'comment' | 'system' | 'dictionaries' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'comment' | 'system' | 'notifications' | 'Nullable(String)' | 'VARCHAR' | '' | '' | 'YES' | '' | | 'comment' | 'system' | 'password_policies' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'comment' | 'system' | 'procedures' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | @@ -82,6 +85,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'create_time' | 'information_schema' | 'tables' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'created_on' | 'system' | 'background_jobs' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'created_on' | 'system' | 'background_tasks' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | +| 'created_on' | 'system' | 'dictionaries' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'created_on' | 'system' | 'indexes' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'created_on' | 'system' | 'locks' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'created_on' | 'system' | 'notification_history' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | @@ -116,6 +120,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'data_write_bytes' | 'system' | 'processes' | 'UInt64' | 'BIGINT UNSIGNED' | '' | '' | 'NO' | '' | | 'database' | 'system' | 'clustering_history' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'database' | 'system' | 'columns' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | +| 'database' | 'system' | 'dictionaries' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'database' | 'system' | 'processes' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'database' | 'system' | 'streams' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'database' | 'system' | 'streams_terse' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | @@ -231,6 +236,8 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'job_type' | 'system' | 'background_jobs' | 'Nullable(String)' | 'VARCHAR' | '' | '' | 'YES' | '' | | 'join_spilled_bytes' | 'system' | 'query_log' | 'UInt64' | 'BIGINT UNSIGNED' | '' | '' | 'NO' | '' | | 'join_spilled_rows' | 'system' | 'query_log' | 'UInt64' | 'BIGINT UNSIGNED' | '' | '' | 'NO' | '' | +| 'key_names' | 'system' | 'dictionaries' | 'Array(String)' | 'ARRAY(STRING)' | '' | '' | 'NO' | '' | +| 'key_types' | 'system' | 'dictionaries' | 'Array(String)' | 'ARRAY(STRING)' | '' | '' | 'NO' | '' | | 'keywords' | 'information_schema' | 'keywords' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'kind' | 'system' | 'metrics' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'labels' | 'system' | 'metrics' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | @@ -266,6 +273,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'name' | 'system' | 'contributors' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'name' | 'system' | 'credits' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'name' | 'system' | 'databases' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | +| 'name' | 'system' | 'dictionaries' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'name' | 'system' | 'functions' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'name' | 'system' | 'indexes' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'name' | 'system' | 'malloc_stats_totals' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | @@ -386,6 +394,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'session_settings' | 'system' | 'query_log' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'size' | 'system' | 'caches' | 'UInt64' | 'BIGINT UNSIGNED' | '' | '' | 'NO' | '' | | 'snapshot_location' | 'system' | 'streams' | 'Nullable(String)' | 'VARCHAR' | '' | '' | 'YES' | '' | +| 'source' | 'system' | 'dictionaries' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'sql' | 'system' | 'query_cache' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | | 'sql_path' | 'information_schema' | 'schemata' | 'NULL' | 'NULL' | '' | '' | 'NO' | '' | | 'sql_user' | 'system' | 'query_log' | 'String' | 'VARCHAR' | '' | '' | 'NO' | '' | @@ -465,6 +474,7 @@ DB.Table: 'system'.'columns', Table: columns-table_id:1, ver:0, Engine: SystemCo | 'update_on' | 'system' | 'roles' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'update_on' | 'system' | 'users' | 'Nullable(Timestamp)' | 'TIMESTAMP' | '' | '' | 'YES' | '' | | 'updated_on' | 'system' | 'background_tasks' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | +| 'updated_on' | 'system' | 'dictionaries' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | | 'updated_on' | 'system' | 'indexes' | 'Nullable(Timestamp)' | 'TIMESTAMP' | '' | '' | 'YES' | '' | | 'updated_on' | 'system' | 'password_policies' | 'Nullable(Timestamp)' | 'TIMESTAMP' | '' | '' | 'YES' | '' | | 'updated_on' | 'system' | 'streams' | 'Timestamp' | 'TIMESTAMP' | '' | '' | 'NO' | '' | diff --git a/src/query/sql/src/planner/binder/binder.rs b/src/query/sql/src/planner/binder/binder.rs index 03b90fd00bb2..b7023e6a4e2e 100644 --- a/src/query/sql/src/planner/binder/binder.rs +++ b/src/query/sql/src/planner/binder/binder.rs @@ -288,7 +288,7 @@ impl<'a> Binder { Statement::CreateDictionary(stmt) => self.bind_create_dictionary(stmt).await?, Statement::DropDictionary(stmt) => self.bind_drop_dictionary(stmt).await?, Statement::ShowCreateDictionary(stmt) => self.bind_show_create_dictionary(stmt).await?, - Statement::ShowDictionaries { show_options: _ } => todo!(), + Statement::ShowDictionaries(stmt) => self.bind_show_dictionaries(bind_context, stmt).await?, // Views Statement::CreateView(stmt) => self.bind_create_view(stmt).await?, Statement::AlterView(stmt) => self.bind_alter_view(stmt).await?, diff --git a/src/query/sql/src/planner/binder/ddl/dictionary.rs b/src/query/sql/src/planner/binder/ddl/dictionary.rs index 6b54019fd5ee..800b16ef5bff 100644 --- a/src/query/sql/src/planner/binder/ddl/dictionary.rs +++ b/src/query/sql/src/planner/binder/ddl/dictionary.rs @@ -19,6 +19,8 @@ use std::sync::LazyLock; use databend_common_ast::ast::CreateDictionaryStmt; use databend_common_ast::ast::DropDictionaryStmt; use databend_common_ast::ast::ShowCreateDictionaryStmt; +use databend_common_ast::ast::ShowDictionariesStmt; +use databend_common_ast::ast::ShowLimit; use databend_common_exception::ErrorCode; use databend_common_exception::Result; use databend_common_expression::types::DataType; @@ -28,12 +30,16 @@ use databend_common_expression::TableDataType; use databend_common_expression::TableSchema; use databend_common_meta_app::schema::DictionaryMeta; use itertools::Itertools; +use log::debug; use crate::plans::CreateDictionaryPlan; use crate::plans::DropDictionaryPlan; use crate::plans::Plan; +use crate::plans::RewriteKind; use crate::plans::ShowCreateDictionaryPlan; +use crate::BindContext; use crate::Binder; +use crate::SelectBuilder; pub const DICT_OPT_KEY_SQL_HOST: &str = "host"; pub const DICT_OPT_KEY_SQL_PORT: &str = "port"; @@ -383,4 +389,50 @@ impl Binder { }, ))) } + + #[async_backtrace::framed] + pub(in crate::planner::binder) async fn bind_show_dictionaries( + &mut self, + bind_context: &mut BindContext, + stmt: &ShowDictionariesStmt, + ) -> Result { + let ShowDictionariesStmt { database, limit } = stmt; + + let mut select_builder = SelectBuilder::from("system.dictionaries"); + + select_builder + .with_column("database AS Database") + .with_column("name AS Dictionary") + .with_column("key_names AS Key_Names") + .with_column("key_types AS key_Types") + .with_column("attribute_names AS Attribute_Names") + .with_column("attribute_types AS Attribute_Types") + .with_column("source AS Source") + .with_column("comment AS Comment"); + + select_builder + .with_order_by("database") + .with_order_by("name"); + + let database = self.check_database_exist(&None, database).await?; + select_builder.with_filter(format!("database = '{}'", database.clone())); + + match limit { + None => (), + Some(ShowLimit::Like { pattern }) => { + select_builder.with_filter(format!("name LIKE '{pattern}'")); + } + Some(ShowLimit::Where { selection }) => { + select_builder.with_filter(format!("({selection})")); + } + }; + let query = select_builder.build(); + debug!("show dictionaries rewrite to: {:?}", query); + self.bind_rewrite_to_query( + bind_context, + query.as_str(), + RewriteKind::ShowDictionaries(database.clone()), + ) + .await + } } diff --git a/src/query/sql/src/planner/plans/plan.rs b/src/query/sql/src/planner/plans/plan.rs index 245fa278e83e..d2df531f5be7 100644 --- a/src/query/sql/src/planner/plans/plan.rs +++ b/src/query/sql/src/planner/plans/plan.rs @@ -390,6 +390,7 @@ pub enum RewriteKind { ShowColumns(String, String, String), ShowTablesStatus, ShowVirtualColumns, + ShowDictionaries(String), ShowStreams(String), diff --git a/src/query/storages/system/src/dictionaries_table.rs b/src/query/storages/system/src/dictionaries_table.rs new file mode 100644 index 000000000000..9cc0c61fafcd --- /dev/null +++ b/src/query/storages/system/src/dictionaries_table.rs @@ -0,0 +1,200 @@ +// Copyright 2021 Datafuse Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use databend_common_catalog::plan::PushDownInfo; +use databend_common_catalog::table::Table; +use databend_common_catalog::table_context::TableContext; +use databend_common_exception::Result; +use databend_common_expression::types::DataType; +use databend_common_expression::types::StringType; +use databend_common_expression::types::TimestampType; +use databend_common_expression::ColumnBuilder; +use databend_common_expression::DataBlock; +use databend_common_expression::FromData; +use databend_common_expression::ScalarRef; +use databend_common_expression::TableDataType; +use databend_common_expression::TableField; +use databend_common_expression::TableSchemaRefExt; +use databend_common_meta_app::schema::ListDictionaryReq; +use databend_common_meta_app::schema::TableIdent; +use databend_common_meta_app::schema::TableInfo; +use databend_common_meta_app::schema::TableMeta; + +use crate::table::AsyncOneBlockSystemTable; +use crate::table::AsyncSystemTable; + +pub struct DictionariesTable { + table_info: TableInfo, +} + +#[async_trait::async_trait] +impl AsyncSystemTable for DictionariesTable { + const NAME: &'static str = "system.dictionaries"; + + fn get_table_info(&self) -> &TableInfo { + &self.table_info + } + + #[async_backtrace::framed] + async fn get_full_data( + &self, + ctx: Arc, + _push_downs: Option, + ) -> Result { + let tenant = ctx.get_tenant(); + + let mut db_names = vec![]; + let mut names = vec![]; + + let mut key_names_builder = + ColumnBuilder::with_capacity(&DataType::Array(Box::new(DataType::String)), 0); + let mut attribute_names_builder = + ColumnBuilder::with_capacity(&DataType::Array(Box::new(DataType::String)), 0); + let mut key_types_builder = + ColumnBuilder::with_capacity(&DataType::Array(Box::new(DataType::String)), 0); + let mut attribute_types_builder = + ColumnBuilder::with_capacity(&DataType::Array(Box::new(DataType::String)), 0); + + let mut sources = vec![]; + let mut comments = vec![]; + let mut created_ons = vec![]; + let mut updated_ons = vec![]; + + let catalog = ctx.get_default_catalog().unwrap(); + let databases = catalog.list_databases(&tenant).await?; + for database in databases { + let db_id = database.get_db_info().database_id.db_id; + let req = ListDictionaryReq { + tenant: tenant.clone(), + db_id, + }; + let dictionaries = catalog.list_dictionaries(req).await?; + for (dict_name, dict_meta) in dictionaries { + db_names.push(database.get_db_name().to_string()); + + names.push(dict_name.clone()); + + let comment = dict_meta.comment; + comments.push(comment); + + let created_on = dict_meta.created_on.timestamp_micros(); + created_ons.push(created_on); + let updated_on = match dict_meta.updated_on { + Some(updated_on) => updated_on.timestamp_micros(), + None => created_on, + }; + updated_ons.push(updated_on); + + let schema = dict_meta.schema; + let fields = &schema.fields; + let primary_column_ids = dict_meta.primary_column_ids; + + let mut key_names = vec![]; + let mut attribute_names = vec![]; + let mut key_types = vec![]; + let mut attribute_types = vec![]; + + for field in fields { + if primary_column_ids.contains(&field.column_id) { + key_names.push(field.name.clone()); + key_types.push(field.data_type.sql_name()); + } else { + attribute_names.push(field.name.clone()); + attribute_types.push(field.data_type.sql_name()); + } + } + let key_names_column = ScalarRef::Array(StringType::from_data(key_names)); + key_names_builder.push(key_names_column); + let attribute_names_column = + ScalarRef::Array(StringType::from_data(attribute_names)); + attribute_names_builder.push(attribute_names_column); + let key_types_column = ScalarRef::Array(StringType::from_data(key_types)); + key_types_builder.push(key_types_column); + let attribute_types_column = + ScalarRef::Array(StringType::from_data(attribute_types)); + attribute_types_builder.push(attribute_types_column); + + let dict_source = dict_meta.source; + let mut options = dict_meta.options; + if let Some(password) = options.get_mut("password") { + *password = "[hidden]".to_string(); + } + let options_str: Vec = options + .iter() + .map(|(k, v)| format!("{}={}", k, v)) + .collect(); + let options_joined = options_str.join(" "); + let source = format!("{}({})", dict_source, options_joined); + sources.push(source); + } + } + return Ok(DataBlock::new_from_columns(vec![ + StringType::from_data(db_names), + StringType::from_data(names), + key_names_builder.build(), + key_types_builder.build(), + attribute_names_builder.build(), + attribute_types_builder.build(), + StringType::from_data(sources), + StringType::from_data(comments), + TimestampType::from_data(created_ons), + TimestampType::from_data(updated_ons), + ])); + } +} + +impl DictionariesTable { + pub fn create(table_id: u64) -> Arc { + let schema = TableSchemaRefExt::create(vec![ + TableField::new("database", TableDataType::String), + TableField::new("name", TableDataType::String), + TableField::new( + "key_names", + TableDataType::Array(Box::new(TableDataType::String)), + ), + TableField::new( + "key_types", + TableDataType::Array(Box::new(TableDataType::String)), + ), + TableField::new( + "attribute_names", + TableDataType::Array(Box::new(TableDataType::String)), + ), + TableField::new( + "attribute_types", + TableDataType::Array(Box::new(TableDataType::String)), + ), + TableField::new("source", TableDataType::String), + TableField::new("comment", TableDataType::String), + TableField::new("created_on", TableDataType::Timestamp), + TableField::new("updated_on", TableDataType::Timestamp), + ]); + + let table_info = TableInfo { + desc: "'system'.'dictionaries'".to_string(), + name: "dictionaries".to_string(), + ident: TableIdent::new(table_id, 0), + meta: TableMeta { + schema, + engine: "SystemDictionaries".to_string(), + ..Default::default() + }, + ..Default::default() + }; + + AsyncOneBlockSystemTable::create(DictionariesTable { table_info }) + } +} diff --git a/src/query/storages/system/src/lib.rs b/src/query/storages/system/src/lib.rs index 8cbaee5ac4db..85c14c4045c3 100644 --- a/src/query/storages/system/src/lib.rs +++ b/src/query/storages/system/src/lib.rs @@ -33,6 +33,7 @@ mod configs_table; mod contributors_table; mod credits_table; mod databases_table; +mod dictionaries_table; mod engines_table; mod functions_table; mod indexes_table; @@ -80,6 +81,7 @@ pub use configs_table::ConfigsTable; pub use contributors_table::ContributorsTable; pub use credits_table::CreditsTable; pub use databases_table::DatabasesTable; +pub use dictionaries_table::DictionariesTable; pub use engines_table::EnginesTable; pub use functions_table::FunctionsTable; pub use indexes_table::IndexesTable; diff --git a/tests/sqllogictests/suites/base/06_show/06_0024_show_dictionaries.test b/tests/sqllogictests/suites/base/06_show/06_0024_show_dictionaries.test new file mode 100644 index 000000000000..914bc37fdba2 --- /dev/null +++ b/tests/sqllogictests/suites/base/06_show/06_0024_show_dictionaries.test @@ -0,0 +1,65 @@ +statement ok +CREATE OR REPLACE DICTIONARY d1(c1 int NOT NULL, c2 Varchar NOT NULL) PRIMARY KEY c1 SOURCE(mysql(host='localhost' port='3306' username='root' password='1234' db='db1' table='test_table')) + +statement ok +CREATE OR REPLACE DICTIONARY d2(a int NOT NULL, b int NOT NULL) PRIMARY KEY a SOURCE(mysql(host='localhost' port='3306' username='root' password='1234' db='db1' table='test_table')) + +query T +show dictionaries +---- +default d1 ['c1'] ['INT'] ['c2'] ['VARCHAR'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +default d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) + +statement ok +DROP DATABASE IF EXISTS show_dictionary + +statement ok +CREATE DATABASE show_dictionary + +statement ok +use show_dictionary + +statement ok +CREATE OR REPLACE DICTIONARY show_dictionary.d1(c1 VARCHAR NOT NULL, c2 VARCHAR NOT NULL) PRIMARY KEY c1 SOURCE(mysql(host='localhost' port='3306' username='root' password='1234' db='db1' table='test_table')) + +statement ok +CREATE OR REPLACE DICTIONARY show_dictionary.d2(a int NOT NULL, b int NOT NULL) PRIMARY KEY a SOURCE(mysql(host='localhost' port='3306' username='root' password='1234' db='db1' table='test_table')) + +statement ok +CREATE OR REPLACE DICTIONARY show_dictionary.d3(`a` int NOT NULL, b int NOT NULL) PRIMARY KEY a SOURCE(mysql(host='localhost' port='3306' username='root' password='1234' db='db1' table='test_table')) + +query T +show dictionaries from show_dictionary +---- +show_dictionary d1 ['c1'] ['VARCHAR'] ['c2'] ['VARCHAR'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d3 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) + +query T +show dictionaries from show_dictionary like 'd%' +---- +show_dictionary d1 ['c1'] ['VARCHAR'] ['c2'] ['VARCHAR'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d3 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) + +query T +show dictionaries from show_dictionary WHERE name = 'd2' OR 1 = 1 +---- +show_dictionary d1 ['c1'] ['VARCHAR'] ['c2'] ['VARCHAR'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d3 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) + +query T +show dictionaries from show_dictionary WHERE name = 'd2' AND 1 = 1 +---- +show_dictionary d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) + +statement ok +show dictionaries WHERE name='d2' AND 1=0 + +query T +show dictionaries +---- +show_dictionary d1 ['c1'] ['VARCHAR'] ['c2'] ['VARCHAR'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d2 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty) +show_dictionary d3 ['a'] ['INT'] ['b'] ['INT'] mysql(db=db1 host=localhost password=[hidden] port=3306 table=test_table username=root) (empty)