From 1b3ee8e8ed4ce09726e1ba48ca7740b9ac950998 Mon Sep 17 00:00:00 2001 From: Dennis Zhuang Date: Mon, 25 Mar 2024 17:45:17 -0700 Subject: [PATCH] feat: impl show index and show columns --- src/catalog/src/information_schema.rs | 4 +- src/catalog/src/information_schema/columns.rs | 18 +- .../information_schema/key_column_usage.rs | 33 +- src/frontend/src/instance.rs | 23 +- src/operator/src/statement.rs | 4 + src/operator/src/statement/show.rs | 24 +- src/query/src/sql.rs | 228 +++++++++++- src/sql/src/error.rs | 13 + src/sql/src/parsers/show_parser.rs | 333 +++++++++++++++++- src/sql/src/statements/show.rs | 17 + src/sql/src/statements/statement.rs | 6 +- .../common/show/show_columns.result | 78 ++++ .../standalone/common/show/show_columns.sql | 24 ++ .../standalone/common/show/show_index.result | 48 +++ .../standalone/common/show/show_index.sql | 22 ++ .../common/system/information_schema.result | 54 +-- 16 files changed, 866 insertions(+), 63 deletions(-) create mode 100644 tests/cases/standalone/common/show/show_columns.result create mode 100644 tests/cases/standalone/common/show/show_columns.sql create mode 100644 tests/cases/standalone/common/show/show_index.result create mode 100644 tests/cases/standalone/common/show/show_index.sql diff --git a/src/catalog/src/information_schema.rs b/src/catalog/src/information_schema.rs index e3c54ee4d462..ec109b8252ee 100644 --- a/src/catalog/src/information_schema.rs +++ b/src/catalog/src/information_schema.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod columns; -mod key_column_usage; +pub mod columns; +pub mod key_column_usage; mod memory_table; mod partitions; mod predicate; diff --git a/src/catalog/src/information_schema/columns.rs b/src/catalog/src/information_schema/columns.rs index f96cba8f901e..8b25ad08ddbf 100644 --- a/src/catalog/src/information_schema/columns.rs +++ b/src/catalog/src/information_schema/columns.rs @@ -48,16 +48,16 @@ pub(super) struct InformationSchemaColumns { catalog_manager: Weak, } -const TABLE_CATALOG: &str = "table_catalog"; -const TABLE_SCHEMA: &str = "table_schema"; -const TABLE_NAME: &str = "table_name"; -const COLUMN_NAME: &str = "column_name"; -const DATA_TYPE: &str = "data_type"; -const SEMANTIC_TYPE: &str = "semantic_type"; -const COLUMN_DEFAULT: &str = "column_default"; -const IS_NULLABLE: &str = "is_nullable"; +pub const TABLE_CATALOG: &str = "table_catalog"; +pub const TABLE_SCHEMA: &str = "table_schema"; +pub const TABLE_NAME: &str = "table_name"; +pub const COLUMN_NAME: &str = "column_name"; +pub const DATA_TYPE: &str = "data_type"; +pub const SEMANTIC_TYPE: &str = "semantic_type"; +pub const COLUMN_DEFAULT: &str = "column_default"; +pub const IS_NULLABLE: &str = "is_nullable"; const COLUMN_TYPE: &str = "column_type"; -const COLUMN_COMMENT: &str = "column_comment"; +pub const COLUMN_COMMENT: &str = "column_comment"; const INIT_CAPACITY: usize = 42; impl InformationSchemaColumns { diff --git a/src/catalog/src/information_schema/key_column_usage.rs b/src/catalog/src/information_schema/key_column_usage.rs index f12167ddc4c8..d35ba8e6ce2b 100644 --- a/src/catalog/src/information_schema/key_column_usage.rs +++ b/src/catalog/src/information_schema/key_column_usage.rs @@ -37,13 +37,16 @@ use crate::error::{ use crate::information_schema::{InformationTable, Predicates}; use crate::CatalogManager; -const CONSTRAINT_SCHEMA: &str = "constraint_schema"; -const CONSTRAINT_NAME: &str = "constraint_name"; -const TABLE_CATALOG: &str = "table_catalog"; -const TABLE_SCHEMA: &str = "table_schema"; -const TABLE_NAME: &str = "table_name"; -const COLUMN_NAME: &str = "column_name"; -const ORDINAL_POSITION: &str = "ordinal_position"; +pub const CONSTRAINT_SCHEMA: &str = "constraint_schema"; +pub const CONSTRAINT_NAME: &str = "constraint_name"; +// It's always `def` in MySQL +pub const TABLE_CATALOG: &str = "table_catalog"; +// The real catalog name for this key column. +pub const REAL_TABLE_CATALOG: &str = "real_table_catalog"; +pub const TABLE_SCHEMA: &str = "table_schema"; +pub const TABLE_NAME: &str = "table_name"; +pub const COLUMN_NAME: &str = "column_name"; +pub const ORDINAL_POSITION: &str = "ordinal_position"; const INIT_CAPACITY: usize = 42; /// The virtual table implementation for `information_schema.KEY_COLUMN_USAGE`. @@ -76,6 +79,11 @@ impl InformationSchemaKeyColumnUsage { ), ColumnSchema::new(CONSTRAINT_NAME, ConcreteDataType::string_datatype(), false), ColumnSchema::new(TABLE_CATALOG, ConcreteDataType::string_datatype(), false), + ColumnSchema::new( + REAL_TABLE_CATALOG, + ConcreteDataType::string_datatype(), + false, + ), ColumnSchema::new(TABLE_SCHEMA, ConcreteDataType::string_datatype(), false), ColumnSchema::new(TABLE_NAME, ConcreteDataType::string_datatype(), false), ColumnSchema::new(COLUMN_NAME, ConcreteDataType::string_datatype(), false), @@ -158,6 +166,7 @@ struct InformationSchemaKeyColumnUsageBuilder { constraint_schema: StringVectorBuilder, constraint_name: StringVectorBuilder, table_catalog: StringVectorBuilder, + real_table_catalog: StringVectorBuilder, table_schema: StringVectorBuilder, table_name: StringVectorBuilder, column_name: StringVectorBuilder, @@ -179,6 +188,7 @@ impl InformationSchemaKeyColumnUsageBuilder { constraint_schema: StringVectorBuilder::with_capacity(INIT_CAPACITY), constraint_name: StringVectorBuilder::with_capacity(INIT_CAPACITY), table_catalog: StringVectorBuilder::with_capacity(INIT_CAPACITY), + real_table_catalog: StringVectorBuilder::with_capacity(INIT_CAPACITY), table_schema: StringVectorBuilder::with_capacity(INIT_CAPACITY), table_name: StringVectorBuilder::with_capacity(INIT_CAPACITY), column_name: StringVectorBuilder::with_capacity(INIT_CAPACITY), @@ -223,6 +233,7 @@ impl InformationSchemaKeyColumnUsageBuilder { &predicates, &schema_name, "TIME INDEX", + &catalog_name, &schema_name, &table_name, &column.name, @@ -231,6 +242,7 @@ impl InformationSchemaKeyColumnUsageBuilder { } if keys.contains(&idx) { primary_constraints.push(( + catalog_name.clone(), schema_name.clone(), table_name.clone(), column.name.clone(), @@ -244,13 +256,14 @@ impl InformationSchemaKeyColumnUsageBuilder { } } - for (i, (schema_name, table_name, column_name)) in + for (i, (catalog_name, schema_name, table_name, column_name)) in primary_constraints.into_iter().enumerate() { self.add_key_column_usage( &predicates, &schema_name, "PRIMARY", + &catalog_name, &schema_name, &table_name, &column_name, @@ -269,6 +282,7 @@ impl InformationSchemaKeyColumnUsageBuilder { predicates: &Predicates, constraint_schema: &str, constraint_name: &str, + table_catalog: &str, table_schema: &str, table_name: &str, column_name: &str, @@ -277,6 +291,7 @@ impl InformationSchemaKeyColumnUsageBuilder { let row = [ (CONSTRAINT_SCHEMA, &Value::from(constraint_schema)), (CONSTRAINT_NAME, &Value::from(constraint_name)), + (REAL_TABLE_CATALOG, &Value::from(table_catalog)), (TABLE_SCHEMA, &Value::from(table_schema)), (TABLE_NAME, &Value::from(table_name)), (COLUMN_NAME, &Value::from(column_name)), @@ -291,6 +306,7 @@ impl InformationSchemaKeyColumnUsageBuilder { self.constraint_schema.push(Some(constraint_schema)); self.constraint_name.push(Some(constraint_name)); self.table_catalog.push(Some("def")); + self.real_table_catalog.push(Some(table_catalog)); self.table_schema.push(Some(table_schema)); self.table_name.push(Some(table_name)); self.column_name.push(Some(column_name)); @@ -310,6 +326,7 @@ impl InformationSchemaKeyColumnUsageBuilder { Arc::new(self.constraint_schema.finish()), Arc::new(self.constraint_name.finish()), Arc::new(self.table_catalog.finish()), + Arc::new(self.real_table_catalog.finish()), Arc::new(self.table_schema.finish()), Arc::new(self.table_name.finish()), Arc::new(self.column_name.finish()), diff --git a/src/frontend/src/instance.rs b/src/frontend/src/instance.rs index fb0d5f991391..27d7aa1359e1 100644 --- a/src/frontend/src/instance.rs +++ b/src/frontend/src/instance.rs @@ -455,6 +455,17 @@ impl PrometheusHandler for Instance { } } +/// Validate `stmt.database` permission if it's presented. +macro_rules! validate_db_permission { + ($stmt: expr, $query_ctx: expr) => { + if let Some(database) = &$stmt.database { + validate_catalog_and_schema($query_ctx.current_catalog(), database, $query_ctx) + .map_err(BoxedError::new) + .context(SqlExecInterceptedSnafu)?; + } + }; +} + pub fn check_permission( plugins: Plugins, stmt: &Statement, @@ -494,11 +505,13 @@ pub fn check_permission( validate_param(drop_stmt.table_name(), query_ctx)?; } Statement::ShowTables(stmt) => { - if let Some(database) = &stmt.database { - validate_catalog_and_schema(query_ctx.current_catalog(), database, query_ctx) - .map_err(BoxedError::new) - .context(SqlExecInterceptedSnafu)?; - } + validate_db_permission!(stmt, query_ctx); + } + Statement::ShowColumns(stmt) => { + validate_db_permission!(stmt, query_ctx); + } + Statement::ShowIndex(stmt) => { + validate_db_permission!(stmt, query_ctx); } Statement::DescribeTable(stmt) => { validate_param(stmt.name(), query_ctx)?; diff --git a/src/operator/src/statement.rs b/src/operator/src/statement.rs index 5231f99a58ae..1fc577f529a8 100644 --- a/src/operator/src/statement.rs +++ b/src/operator/src/statement.rs @@ -234,6 +234,10 @@ impl StatementExecutor { Ok(Output::new_with_affected_rows(0)) } Statement::ShowVariables(show_variable) => self.show_variable(show_variable, query_ctx), + Statement::ShowColumns(show_columns) => { + self.show_columns(show_columns, query_ctx).await + } + Statement::ShowIndex(show_index) => self.show_index(show_index, query_ctx).await, } } diff --git a/src/operator/src/statement/show.rs b/src/operator/src/statement/show.rs index 85bc439751f7..694ba1951e2b 100644 --- a/src/operator/src/statement/show.rs +++ b/src/operator/src/statement/show.rs @@ -21,7 +21,7 @@ use session::context::QueryContextRef; use snafu::ResultExt; use sql::ast::Ident; use sql::statements::create::Partitions; -use sql::statements::show::{ShowDatabases, ShowTables, ShowVariables}; +use sql::statements::show::{ShowColumns, ShowDatabases, ShowIndex, ShowTables, ShowVariables}; use table::TableRef; use crate::error::{self, ExecuteStatementSnafu, Result}; @@ -50,6 +50,28 @@ impl StatementExecutor { .context(ExecuteStatementSnafu) } + #[tracing::instrument(skip_all)] + pub(super) async fn show_columns( + &self, + stmt: ShowColumns, + query_ctx: QueryContextRef, + ) -> Result { + query::sql::show_columns(stmt, &self.query_engine, &self.catalog_manager, query_ctx) + .await + .context(ExecuteStatementSnafu) + } + + #[tracing::instrument(skip_all)] + pub(super) async fn show_index( + &self, + stmt: ShowIndex, + query_ctx: QueryContextRef, + ) -> Result { + query::sql::show_index(stmt, &self.query_engine, &self.catalog_manager, query_ctx) + .await + .context(ExecuteStatementSnafu) + } + #[tracing::instrument(skip_all)] pub async fn show_create_table( &self, diff --git a/src/query/src/sql.rs b/src/query/src/sql.rs index da1ab58cde1b..1c0ce69ed967 100644 --- a/src/query/src/sql.rs +++ b/src/query/src/sql.rs @@ -17,7 +17,9 @@ mod show_create_table; use std::collections::HashMap; use std::sync::Arc; -use catalog::information_schema::{schemata, tables, SCHEMATA, TABLES}; +use catalog::information_schema::{ + columns, key_column_usage, schemata, tables, COLUMNS, KEY_COLUMN_USAGE, SCHEMATA, TABLES, +}; use catalog::CatalogManagerRef; use common_catalog::consts::{ INFORMATION_SCHEMA_NAME, SEMANTIC_TYPE_FIELD, SEMANTIC_TYPE_PRIMARY_KEY, @@ -34,8 +36,9 @@ use common_recordbatch::adapter::RecordBatchStreamAdapter; use common_recordbatch::RecordBatches; use common_time::timezone::get_timezone; use common_time::Timestamp; +use datafusion::common::ScalarValue; use datafusion::prelude::SessionContext; -use datafusion_expr::{col, lit, Expr}; +use datafusion_expr::{case, col, lit, Expr}; use datatypes::prelude::*; use datatypes::schema::{ColumnDefaultConstraint, ColumnSchema, RawSchema, Schema}; use datatypes::vectors::StringVector; @@ -46,7 +49,9 @@ use session::context::QueryContextRef; pub use show_create_table::create_table_stmt; use snafu::{ensure, OptionExt, ResultExt}; use sql::statements::create::Partitions; -use sql::statements::show::{ShowDatabases, ShowKind, ShowTables, ShowVariables}; +use sql::statements::show::{ + ShowColumns, ShowDatabases, ShowIndex, ShowKind, ShowTables, ShowVariables, +}; use table::requests::{FILE_TABLE_LOCATION_KEY, FILE_TABLE_PATTERN_KEY}; use table::TableRef; @@ -57,17 +62,37 @@ use crate::QueryEngineRef; const SCHEMAS_COLUMN: &str = "Database"; const TABLES_COLUMN: &str = "Tables"; +const FIELD_COLUMN: &str = "Field"; const TABLE_TYPE_COLUMN: &str = "Table_type"; const COLUMN_NAME_COLUMN: &str = "Column"; const COLUMN_TYPE_COLUMN: &str = "Type"; const COLUMN_KEY_COLUMN: &str = "Key"; +const COLUMN_EXTRA_COLUMN: &str = "Extra"; +const COLUMN_PRIVILEGES_COLUMN: &str = "Privileges"; +const COLUMN_COLLATION_COLUMN: &str = "Collation"; const COLUMN_NULLABLE_COLUMN: &str = "Null"; const COLUMN_DEFAULT_COLUMN: &str = "Default"; +const COLUMN_COMMENT_COLUMN: &str = "Comment"; const COLUMN_SEMANTIC_TYPE_COLUMN: &str = "Semantic Type"; -const NULLABLE_YES: &str = "YES"; -const NULLABLE_NO: &str = "NO"; +const YES_STR: &str = "YES"; +const NO_STR: &str = "NO"; const PRI_KEY: &str = "PRI"; +const TIME_INDEX: &str = "TIME INDEX"; + +/// SHOW index columns +const INDEX_TABLE_COLUMN: &str = "Table"; +const INDEX_NONT_UNIQUE_COLUMN: &str = "Non_unique"; +const INDEX_CARDINALITY_COLUMN: &str = "Cardinality"; +const INDEX_SUB_PART_COLUMN: &str = "Sub_part"; +const INDEX_PACKED_COLUMN: &str = "Packed"; +const INDEX_INDEX_TYPE_COLUMN: &str = "Index_type"; +const INDEX_COMMENT_COLUMN: &str = "Index_comment"; +const INDEX_VISIBLE_COLUMN: &str = "Visible"; +const INDEX_EXPRESSION_COLUMN: &str = "Expression"; +const INDEX_KEY_NAME_COLUMN: &str = "Key_name"; +const INDEX_SEQ_IN_INDEX_COLUMN: &str = "Seq_in_index"; +const INDEX_COLUMN_NAME_COLUMN: &str = "Column_name"; static DESCRIBE_TABLE_OUTPUT_SCHEMA: Lazy> = Lazy::new(|| { Arc::new(Schema::new(vec![ @@ -107,6 +132,10 @@ static SHOW_CREATE_TABLE_OUTPUT_SCHEMA: Lazy> = Lazy::new(|| { ])) }); +fn null() -> Expr { + lit(ScalarValue::Null) +} + pub async fn show_databases( stmt: ShowDatabases, query_engine: &QueryEngineRef, @@ -124,6 +153,7 @@ pub async fn show_databases( catalog_manager, query_ctx, SCHEMATA, + vec![], projects, filters, like_field, @@ -146,6 +176,7 @@ async fn query_from_information_schema_table( catalog_manager: &CatalogManagerRef, query_ctx: QueryContextRef, table_name: &str, + select: Vec, projects: Vec<(&str, &str)>, filters: Vec, like_field: Option<&str>, @@ -170,6 +201,13 @@ async fn query_from_information_schema_table( let DataFrame::DataFusion(dataframe) = query_engine.read_table(table)?; + // Apply select + let dataframe = if select.is_empty() { + dataframe + } else { + dataframe.select(select).context(error::PlanSqlSnafu)? + }; + // Apply filters let dataframe = filters.into_iter().try_fold(dataframe, |df, expr| { df.filter(expr).context(error::PlanSqlSnafu) @@ -242,6 +280,175 @@ async fn query_from_information_schema_table( ))) } +/// Execute `SHOW COLUMNS` statement. +pub async fn show_columns( + stmt: ShowColumns, + query_engine: &QueryEngineRef, + catalog_manager: &CatalogManagerRef, + query_ctx: QueryContextRef, +) -> Result { + let schema_name = if let Some(database) = stmt.database { + database + } else { + query_ctx.current_schema().to_owned() + }; + + let select = vec![ + // '' as `Extra` + lit("").alias(COLUMN_EXTRA_COLUMN), + // 'select,insert,update,references' as `Privileges` + lit("select,insert,update,references").alias(COLUMN_PRIVILEGES_COLUMN), + // case `datatype` + // when 'String' then 'utf8_bin' + // else NULL + // end + case(col(columns::DATA_TYPE)) + .when(lit("String"), lit("utf8_bin")) + .otherwise(null()) + .context(error::PlanSqlSnafu)? + .alias(COLUMN_COLLATION_COLUMN), + // case `semantic_type` + // when 'TAG' then 'PRI' + // when 'TIMESTAMP' then 'TIME INDEX' + // else '' + // end as `Key` + case(col(columns::SEMANTIC_TYPE)) + .when(lit(SEMANTIC_TYPE_PRIMARY_KEY), lit(PRI_KEY)) + .when(lit(SEMANTIC_TYPE_TIME_INDEX), lit(TIME_INDEX)) + .otherwise(lit("")) + .context(error::PlanSqlSnafu)? + .alias(COLUMN_KEY_COLUMN), + Expr::Wildcard, + ]; + + let projects = if stmt.full { + vec![ + (columns::COLUMN_NAME, FIELD_COLUMN), + (columns::DATA_TYPE, COLUMN_TYPE_COLUMN), + (COLUMN_COLLATION_COLUMN, COLUMN_COLLATION_COLUMN), + (columns::IS_NULLABLE, COLUMN_NULLABLE_COLUMN), + (COLUMN_KEY_COLUMN, COLUMN_KEY_COLUMN), + (columns::COLUMN_DEFAULT, COLUMN_DEFAULT_COLUMN), + (columns::COLUMN_COMMENT, COLUMN_COMMENT_COLUMN), + (COLUMN_PRIVILEGES_COLUMN, COLUMN_PRIVILEGES_COLUMN), + (COLUMN_EXTRA_COLUMN, COLUMN_EXTRA_COLUMN), + ] + } else { + vec![ + (columns::COLUMN_NAME, FIELD_COLUMN), + (columns::DATA_TYPE, COLUMN_TYPE_COLUMN), + (columns::IS_NULLABLE, COLUMN_NULLABLE_COLUMN), + (COLUMN_KEY_COLUMN, COLUMN_KEY_COLUMN), + (columns::COLUMN_DEFAULT, COLUMN_DEFAULT_COLUMN), + (COLUMN_EXTRA_COLUMN, COLUMN_EXTRA_COLUMN), + ] + }; + let filters = vec![ + col(columns::TABLE_NAME).eq(lit(&stmt.table)), + col(columns::TABLE_SCHEMA).eq(lit(schema_name.clone())), + col(columns::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())), + ]; + let like_field = Some(columns::COLUMN_NAME); + let sort = vec![col(columns::COLUMN_NAME).sort(true, true)]; + + query_from_information_schema_table( + query_engine, + catalog_manager, + query_ctx, + COLUMNS, + select, + projects, + filters, + like_field, + sort, + stmt.kind, + ) + .await +} + +/// Execute `SHOW INDEX` statement. +pub async fn show_index( + stmt: ShowIndex, + query_engine: &QueryEngineRef, + catalog_manager: &CatalogManagerRef, + query_ctx: QueryContextRef, +) -> Result { + let schema_name = if let Some(database) = stmt.database { + database + } else { + query_ctx.current_schema().to_owned() + }; + + let select = vec![ + // 1 as `Non_unique`: contain duplicates + lit(1).alias(INDEX_NONT_UNIQUE_COLUMN), + // How the column is sorted in the index: A (ascending). + lit("A").alias(COLUMN_COLLATION_COLUMN), + null().alias(INDEX_CARDINALITY_COLUMN), + null().alias(INDEX_SUB_PART_COLUMN), + null().alias(INDEX_PACKED_COLUMN), + // case `constraint_name` + // when 'TIME INDEX' then 'NO' + // else 'YES' + // end as `Null` + case(col(key_column_usage::CONSTRAINT_NAME)) + .when(lit(TIME_INDEX), lit(NO_STR)) + .otherwise(lit(YES_STR)) + .context(error::PlanSqlSnafu)? + .alias(COLUMN_NULLABLE_COLUMN), + // TODO(dennis): maybe 'BTREE'? + lit("greptime-inverted-index-v1").alias(INDEX_INDEX_TYPE_COLUMN), + lit("").alias(COLUMN_COMMENT_COLUMN), + lit("").alias(INDEX_COMMENT_COLUMN), + lit(YES_STR).alias(INDEX_VISIBLE_COLUMN), + null().alias(INDEX_EXPRESSION_COLUMN), + Expr::Wildcard, + ]; + + let projects = vec![ + (key_column_usage::TABLE_NAME, INDEX_TABLE_COLUMN), + (INDEX_NONT_UNIQUE_COLUMN, INDEX_NONT_UNIQUE_COLUMN), + (key_column_usage::CONSTRAINT_NAME, INDEX_KEY_NAME_COLUMN), + ( + key_column_usage::ORDINAL_POSITION, + INDEX_SEQ_IN_INDEX_COLUMN, + ), + (key_column_usage::COLUMN_NAME, INDEX_COLUMN_NAME_COLUMN), + (COLUMN_COLLATION_COLUMN, COLUMN_COLLATION_COLUMN), + (INDEX_CARDINALITY_COLUMN, INDEX_CARDINALITY_COLUMN), + (INDEX_SUB_PART_COLUMN, INDEX_SUB_PART_COLUMN), + (INDEX_PACKED_COLUMN, INDEX_PACKED_COLUMN), + (COLUMN_NULLABLE_COLUMN, COLUMN_NULLABLE_COLUMN), + (INDEX_INDEX_TYPE_COLUMN, INDEX_INDEX_TYPE_COLUMN), + (COLUMN_COMMENT_COLUMN, COLUMN_COMMENT_COLUMN), + (INDEX_COMMENT_COLUMN, INDEX_COMMENT_COLUMN), + (INDEX_VISIBLE_COLUMN, INDEX_VISIBLE_COLUMN), + (INDEX_EXPRESSION_COLUMN, INDEX_EXPRESSION_COLUMN), + ]; + + let filters = vec![ + col(key_column_usage::TABLE_NAME).eq(lit(&stmt.table)), + col(key_column_usage::TABLE_SCHEMA).eq(lit(schema_name.clone())), + col(key_column_usage::REAL_TABLE_CATALOG).eq(lit(query_ctx.current_catalog())), + ]; + let like_field = None; + let sort = vec![col(columns::COLUMN_NAME).sort(true, true)]; + + query_from_information_schema_table( + query_engine, + catalog_manager, + query_ctx, + KEY_COLUMN_USAGE, + select, + projects, + filters, + like_field, + sort, + stmt.kind, + ) + .await +} + pub async fn show_tables( stmt: ShowTables, query_engine: &QueryEngineRef, @@ -276,6 +483,7 @@ pub async fn show_tables( catalog_manager, query_ctx, TABLES, + vec![], projects, filters, like_field, @@ -381,9 +589,9 @@ fn describe_column_nullables(columns_schemas: &[ColumnSchema]) -> VectorRef { Arc::new(StringVector::from_iterator(columns_schemas.iter().map( |cs| { if cs.is_nullable() { - NULLABLE_YES + YES_STR } else { - NULLABLE_NO + NO_STR } }, ))) @@ -589,8 +797,8 @@ mod test { use crate::error; use crate::error::Result; use crate::sql::{ - describe_table, DESCRIBE_TABLE_OUTPUT_SCHEMA, NULLABLE_NO, NULLABLE_YES, - SEMANTIC_TYPE_FIELD, SEMANTIC_TYPE_TIME_INDEX, + describe_table, DESCRIBE_TABLE_OUTPUT_SCHEMA, NO_STR, SEMANTIC_TYPE_FIELD, + SEMANTIC_TYPE_TIME_INDEX, YES_STR, }; #[test] @@ -617,7 +825,7 @@ mod test { Arc::new(StringVector::from(vec!["t1", "t2"])) as _, Arc::new(StringVector::from(vec!["UInt32", "TimestampMillisecond"])) as _, Arc::new(StringVector::from(vec!["", "PRI"])) as _, - Arc::new(StringVector::from(vec![NULLABLE_YES, NULLABLE_NO])) as _, + Arc::new(StringVector::from(vec![YES_STR, NO_STR])) as _, Arc::new(StringVector::from(vec!["", "current_timestamp()"])) as _, Arc::new(StringVector::from(vec![ SEMANTIC_TYPE_FIELD, diff --git a/src/sql/src/error.rs b/src/sql/src/error.rs index 24643b4a6505..04352c033952 100644 --- a/src/sql/src/error.rs +++ b/src/sql/src/error.rs @@ -75,6 +75,18 @@ pub enum Error { #[snafu(display("Invalid SQL, error: {}", msg))] InvalidSql { msg: String }, + #[snafu(display( + "Unexpected token while parsing SQL statement: {}, expected: '{}', found: {}", + sql, + expected, + actual, + ))] + UnexpectedToken { + sql: String, + expected: String, + actual: String, + }, + #[snafu(display("Invalid column option, column name: {}, error: {}", name, msg))] InvalidColumnOption { name: String, msg: String }, @@ -175,6 +187,7 @@ impl ErrorExt for Error { | InvalidSql { .. } | ParseSqlValue { .. } | SqlTypeNotSupported { .. } + | UnexpectedToken { .. } | InvalidDefault { .. } => StatusCode::InvalidSyntax, InvalidColumnOption { .. } diff --git a/src/sql/src/parsers/show_parser.rs b/src/sql/src/parsers/show_parser.rs index a71397095eeb..9683aa4211c5 100644 --- a/src/sql/src/parsers/show_parser.rs +++ b/src/sql/src/parsers/show_parser.rs @@ -19,7 +19,7 @@ use sqlparser::tokenizer::Token; use crate::error::{self, InvalidDatabaseNameSnafu, InvalidTableNameSnafu, Result}; use crate::parser::ParserContext; use crate::statements::show::{ - ShowCreateTable, ShowDatabases, ShowKind, ShowTables, ShowVariables, + ShowColumns, ShowCreateTable, ShowDatabases, ShowIndex, ShowKind, ShowTables, ShowVariables, }; use crate::statements::statement::Statement; @@ -33,6 +33,16 @@ impl<'a> ParserContext<'a> { } else if self.matches_keyword(Keyword::TABLES) { let _ = self.parser.next_token(); self.parse_show_tables(false) + } else if self.matches_keyword(Keyword::COLUMNS) || self.matches_keyword(Keyword::FIELDS) { + // SHOW {COLUMNS | FIELDS} + let _ = self.parser.next_token(); + self.parse_show_columns(false) + } else if self.consume_token("INDEX") + || self.consume_token("INDEXES") + || self.consume_token("KEYS") + { + // SHOW {INDEX | INDEXES | KEYS} + self.parse_show_index() } else if self.consume_token("CREATE") { if self.consume_token("TABLE") { self.parse_show_create_table() @@ -42,6 +52,9 @@ impl<'a> ParserContext<'a> { } else if self.consume_token("FULL") { if self.consume_token("TABLES") { self.parse_show_tables(true) + } else if self.consume_token("COLUMNS") || self.consume_token("FIELDS") { + // SHOW {COLUMNS | FIELDS} + self.parse_show_columns(true) } else { self.unsupported(self.peek_token_as_string()) } @@ -80,6 +93,208 @@ impl<'a> ParserContext<'a> { Ok(Statement::ShowCreateTable(ShowCreateTable { table_name })) } + fn parse_show_columns(&mut self, full: bool) -> Result { + let table = match self.parser.peek_token().token { + // SHOW columns {in | FROM} TABLE + Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => { + let _ = self.parser.next_token(); + let table_name = + self.parser + .parse_object_name() + .with_context(|_| error::UnexpectedSnafu { + sql: self.sql, + expected: "a table name", + actual: self.peek_token_as_string(), + })?; + + ensure!( + table_name.0.len() == 1, + InvalidDatabaseNameSnafu { + name: table_name.to_string(), + } + ); + + table_name.to_string() + } + _ => { + return error::UnexpectedTokenSnafu { + sql: self.sql, + expected: "{FROM | IN} table", + actual: self.peek_token_as_string(), + } + .fail(); + } + }; + + let database = match self.parser.peek_token().token { + Token::EOF | Token::SemiColon => { + return Ok(Statement::ShowColumns(ShowColumns { + kind: ShowKind::All, + table, + database: None, + full, + })); + } + + // SHOW columns {In | FROM} TABLE {In | FROM} DATABASE + Token::Word(w) => match w.keyword { + Keyword::IN | Keyword::FROM => { + let _ = self.parser.next_token(); + let db_name = self.parser.parse_object_name().with_context(|_| { + error::UnexpectedSnafu { + sql: self.sql, + expected: "a database name", + actual: self.peek_token_as_string(), + } + })?; + + ensure!( + db_name.0.len() == 1, + InvalidDatabaseNameSnafu { + name: db_name.to_string(), + } + ); + + Some(db_name.to_string()) + } + + _ => None, + }, + _ => None, + }; + + let kind = match self.parser.peek_token().token { + Token::EOF | Token::SemiColon => ShowKind::All, + // SHOW COLUMNS [WHERE | LIKE] [EXPR] + Token::Word(w) => match w.keyword { + Keyword::LIKE => { + let _ = self.parser.next_token(); + ShowKind::Like(self.parser.parse_identifier().with_context(|_| { + error::UnexpectedSnafu { + sql: self.sql, + expected: "LIKE", + actual: self.peek_token_as_string(), + } + })?) + } + Keyword::WHERE => { + let _ = self.parser.next_token(); + ShowKind::Where(self.parser.parse_expr().with_context(|_| { + error::UnexpectedSnafu { + sql: self.sql, + expected: "some valid expression", + actual: self.peek_token_as_string(), + } + })?) + } + _ => return self.unsupported(self.peek_token_as_string()), + }, + _ => return self.unsupported(self.peek_token_as_string()), + }; + + Ok(Statement::ShowColumns(ShowColumns { + kind, + database, + table, + full, + })) + } + + fn parse_show_index(&mut self) -> Result { + let table = match self.parser.peek_token().token { + // SHOW INDEX {in | FROM} TABLE + Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => { + let _ = self.parser.next_token(); + let table_name = + self.parser + .parse_object_name() + .with_context(|_| error::UnexpectedSnafu { + sql: self.sql, + expected: "a table name", + actual: self.peek_token_as_string(), + })?; + + ensure!( + table_name.0.len() == 1, + InvalidDatabaseNameSnafu { + name: table_name.to_string(), + } + ); + + table_name.to_string() + } + _ => { + return error::UnexpectedTokenSnafu { + sql: self.sql, + expected: "{FROM | IN} table", + actual: self.peek_token_as_string(), + } + .fail(); + } + }; + + let database = match self.parser.peek_token().token { + Token::EOF | Token::SemiColon => { + return Ok(Statement::ShowIndex(ShowIndex { + kind: ShowKind::All, + table, + database: None, + })); + } + + // SHOW INDEX {In | FROM} TABLE {In | FROM} DATABASE + Token::Word(w) => match w.keyword { + Keyword::IN | Keyword::FROM => { + let _ = self.parser.next_token(); + let db_name = self.parser.parse_object_name().with_context(|_| { + error::UnexpectedSnafu { + sql: self.sql, + expected: "a database name", + actual: self.peek_token_as_string(), + } + })?; + + ensure!( + db_name.0.len() == 1, + InvalidDatabaseNameSnafu { + name: db_name.to_string(), + } + ); + + Some(db_name.to_string()) + } + + _ => None, + }, + _ => None, + }; + + let kind = match self.parser.peek_token().token { + Token::EOF | Token::SemiColon => ShowKind::All, + // SHOW INDEX [WHERE] [EXPR] + Token::Word(w) => match w.keyword { + Keyword::WHERE => { + let _ = self.parser.next_token(); + ShowKind::Where(self.parser.parse_expr().with_context(|_| { + error::UnexpectedSnafu { + sql: self.sql, + expected: "some valid expression", + actual: self.peek_token_as_string(), + } + })?) + } + _ => return self.unsupported(self.peek_token_as_string()), + }, + _ => return self.unsupported(self.peek_token_as_string()), + }; + + Ok(Statement::ShowIndex(ShowIndex { + kind, + database, + table, + })) + } + fn parse_show_tables(&mut self, full: bool) -> Result { let database = match self.parser.peek_token().token { Token::EOF | Token::SemiColon => { @@ -431,4 +646,120 @@ mod tests { }) ); } + + #[test] + pub fn test_show_columns() { + let sql = "SHOW COLUMNS"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let error = result.unwrap_err(); + assert_eq!("Unexpected token while parsing SQL statement: SHOW COLUMNS, expected: '{FROM | IN} table', found: EOF", error.to_string()); + + let sql = "SHOW COLUMNS from test"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowColumns(ShowColumns { + table, + database, + full, + .. + + }) if table == "test" && database.is_none() && !full)); + + let sql = "SHOW FULL COLUMNS from test from public"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowColumns(ShowColumns { + table, + database: Some(database), + full, + .. + }) if table == "test" && database == "public" && *full)); + + let sql = "SHOW COLUMNS from test like 'disk%'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowColumns(ShowColumns { + table, + kind: ShowKind::Like(ident), + .. + }) if table == "test" && ident.to_string() == "'disk%'")); + + let sql = "SHOW COLUMNS from test where Field = 'disk'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowColumns(ShowColumns { + table, + kind: ShowKind::Where(expr), + .. + }) if table == "test" && expr.to_string() == "Field = 'disk'")); + } + + #[test] + pub fn test_show_index() { + let sql = "SHOW INDEX"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let error = result.unwrap_err(); + assert_eq!("Unexpected token while parsing SQL statement: SHOW INDEX, expected: '{FROM | IN} table', found: EOF", error.to_string()); + + let sql = "SHOW INDEX from test"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowIndex(ShowIndex { + table, + database, + .. + + }) if table == "test" && database.is_none())); + + let sql = "SHOW INDEX from test from public"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowIndex(ShowIndex { + table, + database: Some(database), + .. + }) if table == "test" && database == "public")); + + // SHOW INDEX deosn't support like + let sql = "SHOW INDEX from test like 'disk%'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let error = result.unwrap_err(); + assert_eq!( + "SQL statement is not supported: SHOW INDEX from test like 'disk%', keyword: like", + error.to_string() + ); + + let sql = "SHOW INDEX from test where Field = 'disk'"; + let result = + ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default()); + let stmts = result.unwrap(); + assert_eq!(1, stmts.len()); + assert!(matches!(&stmts[0], + Statement::ShowIndex(ShowIndex { + table, + kind: ShowKind::Where(expr), + .. + }) if table == "test" && expr.to_string() == "Field = 'disk'")); + } } diff --git a/src/sql/src/statements/show.rs b/src/sql/src/statements/show.rs index fadb934b97f1..13cbb2f69ce0 100644 --- a/src/sql/src/statements/show.rs +++ b/src/sql/src/statements/show.rs @@ -42,6 +42,23 @@ pub struct ShowDatabases { pub kind: ShowKind, } +/// The SQL `SHOW COLUMNS` statement +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +pub struct ShowColumns { + pub kind: ShowKind, + pub table: String, + pub database: Option, + pub full: bool, +} + +/// The SQL `SHOW INDEX` statement +#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)] +pub struct ShowIndex { + pub kind: ShowKind, + pub table: String, + pub database: Option, +} + impl ShowDatabases { /// Creates a statement for `SHOW DATABASES` pub fn new(kind: ShowKind) -> Self { diff --git a/src/sql/src/statements/statement.rs b/src/sql/src/statements/statement.rs index b8af789616d1..5be66f32345c 100644 --- a/src/sql/src/statements/statement.rs +++ b/src/sql/src/statements/statement.rs @@ -29,7 +29,7 @@ use crate::statements::explain::Explain; use crate::statements::insert::Insert; use crate::statements::query::Query; use crate::statements::set_variables::SetVariables; -use crate::statements::show::{ShowCreateTable, ShowDatabases, ShowTables}; +use crate::statements::show::{ShowColumns, ShowCreateTable, ShowDatabases, ShowIndex, ShowTables}; use crate::statements::tql::Tql; use crate::statements::truncate::TruncateTable; @@ -59,6 +59,10 @@ pub enum Statement { ShowDatabases(ShowDatabases), // SHOW TABLES ShowTables(ShowTables), + // SHOW COLUMNS + ShowColumns(ShowColumns), + // SHOW INDEX + ShowIndex(ShowIndex), // SHOW CREATE TABLE ShowCreateTable(ShowCreateTable), // DESCRIBE TABLE diff --git a/tests/cases/standalone/common/show/show_columns.result b/tests/cases/standalone/common/show/show_columns.result new file mode 100644 index 000000000000..4aef103a83b6 --- /dev/null +++ b/tests/cases/standalone/common/show/show_columns.result @@ -0,0 +1,78 @@ +CREATE TABLE IF NOT EXISTS system_metrics ( + host STRING, + idc STRING, + cpu_util DOUBLE, + memory_util DOUBLE, + disk_util DOUBLE, + ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(host, idc), + TIME INDEX(ts) +); + +Affected Rows: 0 + +SHOW COLUMNS; + +Error: 2000(InvalidSyntax), Unexpected token while parsing SQL statement: SHOW COLUMNS;, expected: '{FROM | IN} table', found: ; + +SHOW COLUMNS FROM system_metrics; + ++-------------+----------------------+------+------------+---------------------+-------+ +| Field | Type | Null | Key | Default | Extra | ++-------------+----------------------+------+------------+---------------------+-------+ +| cpu_util | Float64 | Yes | | | | +| disk_util | Float64 | Yes | | | | +| host | String | Yes | PRI | | | +| idc | String | Yes | PRI | | | +| memory_util | Float64 | Yes | | | | +| ts | TimestampMillisecond | No | TIME INDEX | current_timestamp() | | ++-------------+----------------------+------+------------+---------------------+-------+ + +SHOW COLUMNS FROM system_metrics in public; + ++-------------+----------------------+------+------------+---------------------+-------+ +| Field | Type | Null | Key | Default | Extra | ++-------------+----------------------+------+------------+---------------------+-------+ +| cpu_util | Float64 | Yes | | | | +| disk_util | Float64 | Yes | | | | +| host | String | Yes | PRI | | | +| idc | String | Yes | PRI | | | +| memory_util | Float64 | Yes | | | | +| ts | TimestampMillisecond | No | TIME INDEX | current_timestamp() | | ++-------------+----------------------+------+------------+---------------------+-------+ + +SHOW FULL COLUMNS FROM system_metrics; + ++-------------+----------------------+-----------+------+------------+---------------------+---------+---------------------------------+-------+ +| Field | Type | Collation | Null | Key | Default | Comment | Privileges | Extra | ++-------------+----------------------+-----------+------+------------+---------------------+---------+---------------------------------+-------+ +| cpu_util | Float64 | | Yes | | | | select,insert,update,references | | +| disk_util | Float64 | | Yes | | | | select,insert,update,references | | +| host | String | utf8_bin | Yes | PRI | | | select,insert,update,references | | +| idc | String | utf8_bin | Yes | PRI | | | select,insert,update,references | | +| memory_util | Float64 | | Yes | | | | select,insert,update,references | | +| ts | TimestampMillisecond | | No | TIME INDEX | current_timestamp() | | select,insert,update,references | | ++-------------+----------------------+-----------+------+------------+---------------------+---------+---------------------------------+-------+ + +SHOW COLUMNS FROM system_metrics like '%util%'; + ++-------------+---------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++-------------+---------+------+-----+---------+-------+ +| cpu_util | Float64 | Yes | | | | +| disk_util | Float64 | Yes | | | | +| memory_util | Float64 | Yes | | | | ++-------------+---------+------+-----+---------+-------+ + +SHOW COLUMNS FROM system_metrics WHERE Field = 'cpu_util'; + ++----------+---------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++----------+---------+------+-----+---------+-------+ +| cpu_util | Float64 | Yes | | | | ++----------+---------+------+-----+---------+-------+ + +DROP TABLE system_metrics; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/show/show_columns.sql b/tests/cases/standalone/common/show/show_columns.sql new file mode 100644 index 000000000000..9de3fd7ea719 --- /dev/null +++ b/tests/cases/standalone/common/show/show_columns.sql @@ -0,0 +1,24 @@ +CREATE TABLE IF NOT EXISTS system_metrics ( + host STRING, + idc STRING, + cpu_util DOUBLE, + memory_util DOUBLE, + disk_util DOUBLE, + ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(host, idc), + TIME INDEX(ts) +); + +SHOW COLUMNS; + +SHOW COLUMNS FROM system_metrics; + +SHOW COLUMNS FROM system_metrics in public; + +SHOW FULL COLUMNS FROM system_metrics; + +SHOW COLUMNS FROM system_metrics like '%util%'; + +SHOW COLUMNS FROM system_metrics WHERE Field = 'cpu_util'; + +DROP TABLE system_metrics; diff --git a/tests/cases/standalone/common/show/show_index.result b/tests/cases/standalone/common/show/show_index.result new file mode 100644 index 000000000000..91047b249422 --- /dev/null +++ b/tests/cases/standalone/common/show/show_index.result @@ -0,0 +1,48 @@ +CREATE TABLE IF NOT EXISTS system_metrics ( + host STRING, + idc STRING, + cpu_util DOUBLE, + memory_util DOUBLE, + disk_util DOUBLE, + ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(host, idc), + TIME INDEX(ts) +); + +Affected Rows: 0 + +SHOW INDEX; + +Error: 2000(InvalidSyntax), Unexpected token while parsing SQL statement: SHOW INDEX;, expected: '{FROM | IN} table', found: ; + +SHOW INDEX FROM system_metrics; + ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ +| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ +| system_metrics | 1 | PRIMARY | 1 | host | A | | | | YES | greptime-inverted-index-v1 | | | YES | | +| system_metrics | 1 | PRIMARY | 2 | idc | A | | | | YES | greptime-inverted-index-v1 | | | YES | | +| system_metrics | 1 | TIME INDEX | 1 | ts | A | | | | NO | greptime-inverted-index-v1 | | | YES | | ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ + +SHOW INDEX FROM system_metrics in public; + +++ +++ + +SHOW INDEX FROM system_metrics like '%util%'; + +Error: 1001(Unsupported), SQL statement is not supported: SHOW INDEX FROM system_metrics like '%util%';, keyword: like + +SHOW INDEX FROM system_metrics WHERE Key_name = 'TIME INDEX'; + ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ +| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression | ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ +| system_metrics | 1 | TIME INDEX | 1 | ts | A | | | | NO | greptime-inverted-index-v1 | | | YES | | ++----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+ + +DROP TABLE system_metrics; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/show/show_index.sql b/tests/cases/standalone/common/show/show_index.sql new file mode 100644 index 000000000000..3405e0494e93 --- /dev/null +++ b/tests/cases/standalone/common/show/show_index.sql @@ -0,0 +1,22 @@ +CREATE TABLE IF NOT EXISTS system_metrics ( + host STRING, + idc STRING, + cpu_util DOUBLE, + memory_util DOUBLE, + disk_util DOUBLE, + ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(host, idc), + TIME INDEX(ts) +); + +SHOW INDEX; + +SHOW INDEX FROM system_metrics; + +SHOW INDEX FROM system_metrics in public; + +SHOW INDEX FROM system_metrics like '%util%'; + +SHOW INDEX FROM system_metrics WHERE Key_name = 'TIME INDEX'; + +DROP TABLE system_metrics; diff --git a/tests/cases/standalone/common/system/information_schema.result b/tests/cases/standalone/common/system/information_schema.result index 2a1d2d49f772..bdcdce16f334 100644 --- a/tests/cases/standalone/common/system/information_schema.result +++ b/tests/cases/standalone/common/system/information_schema.result @@ -171,6 +171,7 @@ select * from information_schema.columns order by table_schema, table_name, colu | greptime | information_schema | key_column_usage | constraint_schema | String | FIELD | | No | String | | | greptime | information_schema | key_column_usage | ordinal_position | UInt32 | FIELD | | No | UInt32 | | | greptime | information_schema | key_column_usage | position_in_unique_constraint | UInt32 | FIELD | | Yes | UInt32 | | +| greptime | information_schema | key_column_usage | real_table_catalog | String | FIELD | | No | String | | | greptime | information_schema | key_column_usage | referenced_column_name | String | FIELD | | Yes | String | | | greptime | information_schema | key_column_usage | referenced_table_name | String | FIELD | | Yes | String | | | greptime | information_schema | key_column_usage | referenced_table_schema | String | FIELD | | Yes | String | | @@ -456,35 +457,35 @@ Affected Rows: 0 -- test query filter for key_column_usage -- select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME = 'TIME INDEX'; -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| def | my_db | TIME INDEX | def | my_db | foo | ts | 1 | | | | | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| constraint_catalog | constraint_schema | constraint_name | table_catalog | real_table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| def | my_db | TIME INDEX | def | greptime | my_db | foo | ts | 1 | | | | | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME != 'TIME INDEX'; -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| def | public | PRIMARY | def | public | numbers | number | 1 | | | | | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| constraint_catalog | constraint_schema | constraint_name | table_catalog | real_table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| def | public | PRIMARY | def | greptime | public | numbers | number | 1 | | | | | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME LIKE '%INDEX'; -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| def | my_db | TIME INDEX | def | my_db | foo | ts | 1 | | | | | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| constraint_catalog | constraint_schema | constraint_name | table_catalog | real_table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| def | my_db | TIME INDEX | def | greptime | my_db | foo | ts | 1 | | | | | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME NOT LIKE '%INDEX'; -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| def | public | PRIMARY | def | public | numbers | number | 1 | | | | | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| constraint_catalog | constraint_schema | constraint_name | table_catalog | real_table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| def | public | PRIMARY | def | greptime | public | numbers | number | 1 | | | | | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME == 'TIME INDEX' AND CONSTRAINT_SCHEMA != 'my_db'; @@ -555,6 +556,7 @@ desc table key_column_usage; | constraint_schema | String | | NO | | FIELD | | constraint_name | String | | NO | | FIELD | | table_catalog | String | | NO | | FIELD | +| real_table_catalog | String | | NO | | FIELD | | table_schema | String | | NO | | FIELD | | table_name | String | | NO | | FIELD | | column_name | String | | NO | | FIELD | @@ -567,12 +569,12 @@ desc table key_column_usage; select * from key_column_usage; -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| constraint_catalog | constraint_schema | constraint_name | table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -| def | my_db | TIME INDEX | def | my_db | foo | ts | 1 | | | | | -| def | public | PRIMARY | def | public | numbers | number | 1 | | | | | -+--------------------+-------------------+-----------------+---------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| constraint_catalog | constraint_schema | constraint_name | table_catalog | real_table_catalog | table_schema | table_name | column_name | ordinal_position | position_in_unique_constraint | referenced_table_schema | referenced_table_name | referenced_column_name | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ +| def | my_db | TIME INDEX | def | greptime | my_db | foo | ts | 1 | | | | | +| def | public | PRIMARY | def | greptime | public | numbers | number | 1 | | | | | ++--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+ -- tables not implemented DESC TABLE COLUMN_PRIVILEGES;