feat: impl show index and show columns (#3577)

* feat: impl show index and show columns

* fix: show index from database

* fix: canonicalize table name

* refactor: show parsers
This commit is contained in:
dennis zhuang
2024-03-29 11:34:52 -07:00
committed by GitHub
parent 18d676802a
commit 4a5bb698a9
18 changed files with 846 additions and 71 deletions

View File

@@ -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;

View File

@@ -48,16 +48,16 @@ pub(super) struct InformationSchemaColumns {
catalog_manager: Weak<dyn CatalogManager>,
}
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 {

View File

@@ -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()),

View File

@@ -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,
@@ -495,11 +506,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)?;

View File

@@ -239,6 +239,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,
}
}

View File

@@ -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<Output> {
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<Output> {
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,

View File

@@ -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<Arc<Schema>> = Lazy::new(|| {
Arc::new(Schema::new(vec![
@@ -107,6 +132,10 @@ static SHOW_CREATE_TABLE_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = 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<Expr>,
projects: Vec<(&str, &str)>,
filters: Vec<Expr>,
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,176 @@ 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<Output> {
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<Output> {
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 +484,7 @@ pub async fn show_tables(
catalog_manager,
query_ctx,
TABLES,
vec![],
projects,
filters,
like_field,
@@ -381,9 +590,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 +798,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 +826,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,

View File

@@ -84,6 +84,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 },
@@ -185,6 +197,7 @@ impl ErrorExt for Error {
| InvalidSql { .. }
| ParseSqlValue { .. }
| SqlTypeNotSupported { .. }
| UnexpectedToken { .. }
| InvalidDefault { .. } => StatusCode::InvalidSyntax,
InvalidColumnOption { .. }

View File

@@ -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,186 @@ impl<'a> ParserContext<'a> {
Ok(Statement::ShowCreateTable(ShowCreateTable { table_name }))
}
fn parse_show_table_name(&mut self) -> Result<String> {
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(),
}
);
// Safety: already checked above
Ok(Self::canonicalize_object_name(table_name).0[0]
.value
.clone())
}
fn parse_db_name(&mut self) -> Result<Option<String>> {
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(),
}
);
// Safety: already checked above
Ok(Some(
Self::canonicalize_object_name(db_name).0[0].value.clone(),
))
}
fn parse_show_columns(&mut self, full: bool) -> Result<Statement> {
let table = match self.parser.peek_token().token {
// SHOW columns {in | FROM} TABLE
Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => {
self.parse_show_table_name()?
}
_ => {
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 => self.parse_db_name()?,
_ => 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<Statement> {
let table = match self.parser.peek_token().token {
// SHOW INDEX {in | FROM} TABLE
Token::Word(w) if matches!(w.keyword, Keyword::IN | Keyword::FROM) => {
self.parse_show_table_name()?
}
_ => {
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 => self.parse_db_name()?,
_ => 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<Statement> {
let database = match self.parser.peek_token().token {
Token::EOF | Token::SemiColon => {
@@ -92,25 +285,7 @@ impl<'a> ParserContext<'a> {
// SHOW TABLES [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())
}
Keyword::IN | Keyword::FROM => self.parse_db_name()?,
_ => None,
},
@@ -431,4 +606,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'"));
}
}

View File

@@ -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<String>,
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<String>,
}
impl ShowDatabases {
/// Creates a statement for `SHOW DATABASES`
pub fn new(kind: ShowKind) -> Self {

View File

@@ -30,7 +30,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;
@@ -62,6 +62,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

View File

@@ -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

View File

@@ -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;

View File

@@ -48,3 +48,7 @@ show tables;
| triggers |
+---------------------------------------+
use public;
Affected Rows: 0

View File

@@ -3,3 +3,5 @@ show databases;
use information_schema;
show tables;
use public;

View File

@@ -0,0 +1,53 @@
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;
+----------------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+----------------------------+---------+---------------+---------+------------+
| 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 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

View File

@@ -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;

View File

@@ -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 | |
@@ -461,11 +462,11 @@ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME = 'TIME INDEX';
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';
@@ -474,11 +475,11 @@ select * from KEY_COLUMN_USAGE where CONSTRAINT_NAME LIKE '%INDEX';
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';
@@ -548,6 +549,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 |
@@ -560,11 +562,11 @@ 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 | 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 | | | | |
+--------------------+-------------------+-----------------+---------------+--------------------+--------------+------------+-------------+------------------+-------------------------------+-------------------------+-----------------------+------------------------+
-- tables not implemented
DESC TABLE COLUMN_PRIVILEGES;