mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-04 12:22:55 +00:00
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:
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()),
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 { .. }
|
||||
|
||||
@@ -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'"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
78
tests/cases/standalone/common/show/show_columns.result
Normal file
78
tests/cases/standalone/common/show/show_columns.result
Normal 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
|
||||
|
||||
24
tests/cases/standalone/common/show/show_columns.sql
Normal file
24
tests/cases/standalone/common/show/show_columns.sql
Normal 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;
|
||||
@@ -48,3 +48,7 @@ show tables;
|
||||
| triggers |
|
||||
+---------------------------------------+
|
||||
|
||||
use public;
|
||||
|
||||
Affected Rows: 0
|
||||
|
||||
|
||||
@@ -3,3 +3,5 @@ show databases;
|
||||
use information_schema;
|
||||
|
||||
show tables;
|
||||
|
||||
use public;
|
||||
|
||||
53
tests/cases/standalone/common/show/show_index.result
Normal file
53
tests/cases/standalone/common/show/show_index.result
Normal 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
|
||||
|
||||
22
tests/cases/standalone/common/show/show_index.sql
Normal file
22
tests/cases/standalone/common/show/show_index.sql
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user