Skip to main content

query/
sql.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod show_create_table;
16
17use std::collections::HashMap;
18use std::ops::ControlFlow;
19use std::sync::Arc;
20
21use catalog::CatalogManagerRef;
22use catalog::information_schema::{
23    CHARACTER_SETS, COLLATIONS, COLUMNS, FLOWS, REGION_PEERS, SCHEMATA, STATISTICS, TABLES, VIEWS,
24    columns, flows, process_list, region_peers, schemata, statistics, tables,
25};
26use common_catalog::consts::{
27    INFORMATION_SCHEMA_NAME, SEMANTIC_TYPE_FIELD, SEMANTIC_TYPE_PRIMARY_KEY,
28    SEMANTIC_TYPE_TIME_INDEX,
29};
30use common_catalog::format_full_table_name;
31use common_datasource::file_format::{FileFormat, Format, infer_schemas};
32use common_datasource::lister::{Lister, Source};
33use common_datasource::object_store::{LocalFileAccess, build_backend_with_path};
34use common_meta::SchemaOptions;
35use common_meta::ddl::create_flow::FlowType;
36use common_meta::key::flow::flow_info::FlowInfoValue;
37use common_query::Output;
38use common_query::prelude::greptime_timestamp;
39use common_recordbatch::RecordBatches;
40use common_recordbatch::adapter::RecordBatchStreamAdapter;
41use common_time::Timestamp;
42use common_time::timezone::get_timezone;
43use datafusion::prelude::SessionContext;
44use datafusion_expr::{Expr, SortExpr, col, lit};
45use datatypes::prelude::*;
46use datatypes::schema::{ColumnDefaultConstraint, ColumnSchema, Schema};
47use datatypes::vectors::StringVector;
48use itertools::Itertools;
49use object_store::ObjectStore;
50use once_cell::sync::Lazy;
51use regex::Regex;
52use session::context::{Channel, QueryContextRef};
53pub use show_create_table::create_table_stmt;
54use snafu::{OptionExt, ResultExt, ensure};
55use sql::ast::{Ident, visit_expressions_mut};
56use sql::parser::ParserContext;
57use sql::statements::OptionMap;
58use sql::statements::create::{CreateDatabase, CreateFlow, CreateView, Partitions, SqlOrTql};
59use sql::statements::show::{
60    ShowColumns, ShowDatabases, ShowFlows, ShowIndex, ShowKind, ShowProcessList, ShowRegion,
61    ShowTableStatus, ShowTables, ShowVariables, ShowViews,
62};
63use sql::statements::statement::Statement;
64use sqlparser::ast::ObjectName;
65use store_api::metric_engine_consts::{is_metric_engine, is_metric_engine_internal_column};
66use table::TableRef;
67use table::metadata::TableInfoRef;
68use table::requests::{FILE_TABLE_LOCATION_KEY, FILE_TABLE_PATTERN_KEY};
69
70use crate::QueryEngineRef;
71use crate::error::{self, Result, UnsupportedVariableSnafu};
72use crate::planner::DfLogicalPlanner;
73
74const SCHEMAS_COLUMN: &str = "Database";
75const OPTIONS_COLUMN: &str = "Options";
76const VIEWS_COLUMN: &str = "Views";
77const FLOWS_COLUMN: &str = "Flows";
78const FIELD_COLUMN: &str = "Field";
79const TABLE_TYPE_COLUMN: &str = "Table_type";
80
81const COLUMN_NAME_COLUMN: &str = "Column";
82const COLUMN_GREPTIME_TYPE_COLUMN: &str = "Greptime_type";
83const COLUMN_TYPE_COLUMN: &str = "Type";
84const COLUMN_KEY_COLUMN: &str = "Key";
85const COLUMN_EXTRA_COLUMN: &str = "Extra";
86const COLUMN_PRIVILEGES_COLUMN: &str = "Privileges";
87const COLUMN_COLLATION_COLUMN: &str = "Collation";
88const COLUMN_NULLABLE_COLUMN: &str = "Null";
89const COLUMN_DEFAULT_COLUMN: &str = "Default";
90const COLUMN_COMMENT_COLUMN: &str = "Comment";
91const COLUMN_SEMANTIC_TYPE_COLUMN: &str = "Semantic Type";
92
93const YES_STR: &str = "YES";
94const NO_STR: &str = "NO";
95const PRI_KEY: &str = "PRI";
96
97/// SHOW index columns
98const INDEX_TABLE_COLUMN: &str = "Table";
99const INDEX_NONT_UNIQUE_COLUMN: &str = "Non_unique";
100const INDEX_CARDINALITY_COLUMN: &str = "Cardinality";
101const INDEX_SUB_PART_COLUMN: &str = "Sub_part";
102const INDEX_PACKED_COLUMN: &str = "Packed";
103const INDEX_INDEX_TYPE_COLUMN: &str = "Index_type";
104const INDEX_COMMENT_COLUMN: &str = "Index_comment";
105const INDEX_VISIBLE_COLUMN: &str = "Visible";
106const INDEX_EXPRESSION_COLUMN: &str = "Expression";
107const INDEX_KEY_NAME_COLUMN: &str = "Key_name";
108const INDEX_SEQ_IN_INDEX_COLUMN: &str = "Seq_in_index";
109const INDEX_COLUMN_NAME_COLUMN: &str = "Column_name";
110
111static DESCRIBE_TABLE_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
112    Arc::new(Schema::new(vec![
113        ColumnSchema::new(
114            COLUMN_NAME_COLUMN,
115            ConcreteDataType::string_datatype(),
116            false,
117        ),
118        ColumnSchema::new(
119            COLUMN_TYPE_COLUMN,
120            ConcreteDataType::string_datatype(),
121            false,
122        ),
123        ColumnSchema::new(COLUMN_KEY_COLUMN, ConcreteDataType::string_datatype(), true),
124        ColumnSchema::new(
125            COLUMN_NULLABLE_COLUMN,
126            ConcreteDataType::string_datatype(),
127            false,
128        ),
129        ColumnSchema::new(
130            COLUMN_DEFAULT_COLUMN,
131            ConcreteDataType::string_datatype(),
132            false,
133        ),
134        ColumnSchema::new(
135            COLUMN_SEMANTIC_TYPE_COLUMN,
136            ConcreteDataType::string_datatype(),
137            false,
138        ),
139    ]))
140});
141
142static SHOW_CREATE_DATABASE_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
143    Arc::new(Schema::new(vec![
144        ColumnSchema::new("Database", ConcreteDataType::string_datatype(), false),
145        ColumnSchema::new(
146            "Create Database",
147            ConcreteDataType::string_datatype(),
148            false,
149        ),
150    ]))
151});
152
153static SHOW_CREATE_TABLE_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
154    Arc::new(Schema::new(vec![
155        ColumnSchema::new("Table", ConcreteDataType::string_datatype(), false),
156        ColumnSchema::new("Create Table", ConcreteDataType::string_datatype(), false),
157    ]))
158});
159
160static SHOW_CREATE_FLOW_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
161    Arc::new(Schema::new(vec![
162        ColumnSchema::new("Flow", ConcreteDataType::string_datatype(), false),
163        ColumnSchema::new("Create Flow", ConcreteDataType::string_datatype(), false),
164    ]))
165});
166
167static SHOW_CREATE_VIEW_OUTPUT_SCHEMA: Lazy<Arc<Schema>> = Lazy::new(|| {
168    Arc::new(Schema::new(vec![
169        ColumnSchema::new("View", ConcreteDataType::string_datatype(), false),
170        ColumnSchema::new("Create View", ConcreteDataType::string_datatype(), false),
171    ]))
172});
173
174pub async fn show_databases(
175    stmt: ShowDatabases,
176    query_engine: &QueryEngineRef,
177    catalog_manager: &CatalogManagerRef,
178    query_ctx: QueryContextRef,
179) -> Result<Output> {
180    let projects = if stmt.full {
181        vec![
182            (schemata::SCHEMA_NAME, SCHEMAS_COLUMN),
183            (schemata::SCHEMA_OPTS, OPTIONS_COLUMN),
184        ]
185    } else {
186        vec![(schemata::SCHEMA_NAME, SCHEMAS_COLUMN)]
187    };
188
189    let filters = vec![col(schemata::CATALOG_NAME).eq(lit(query_ctx.current_catalog()))];
190    let like_field = Some(schemata::SCHEMA_NAME);
191    let sort = vec![col(schemata::SCHEMA_NAME).sort(true, true)];
192
193    query_from_information_schema_table(
194        query_engine,
195        catalog_manager,
196        query_ctx,
197        SCHEMATA,
198        vec![],
199        projects,
200        filters,
201        like_field,
202        sort,
203        stmt.kind,
204    )
205    .await
206}
207
208/// Replaces column identifier references in a SQL expression.
209/// Used for backward compatibility where old column names should work with new ones.
210fn replace_column_in_expr(expr: &mut sqlparser::ast::Expr, from_column: &str, to_column: &str) {
211    let _ = visit_expressions_mut(expr, |e| {
212        match e {
213            sqlparser::ast::Expr::Identifier(ident)
214                if ident.value.eq_ignore_ascii_case(from_column) =>
215            {
216                ident.value = to_column.to_string();
217            }
218            sqlparser::ast::Expr::CompoundIdentifier(idents) => {
219                if let Some(last) = idents.last_mut()
220                    && last.value.eq_ignore_ascii_case(from_column)
221                {
222                    last.value = to_column.to_string();
223                }
224            }
225            _ => {}
226        }
227        ControlFlow::<()>::Continue(())
228    });
229}
230
231/// Cast a `show` statement execution into a query from tables in  `information_schema`.
232/// - `table_name`: the table name in `information_schema`,
233/// - `projects`: query projection, a list of `(column, renamed_column)`,
234/// - `filters`: filter expressions for query,
235/// - `like_field`: the field to filter by the predicate `ShowKind::Like`,
236/// - `sort`: sort the results by the specified sorting expressions,
237/// - `kind`: the show kind
238#[allow(clippy::too_many_arguments)]
239async fn query_from_information_schema_table(
240    query_engine: &QueryEngineRef,
241    catalog_manager: &CatalogManagerRef,
242    query_ctx: QueryContextRef,
243    table_name: &str,
244    select: Vec<Expr>,
245    projects: Vec<(&str, &str)>,
246    filters: Vec<Expr>,
247    like_field: Option<&str>,
248    sort: Vec<SortExpr>,
249    kind: ShowKind,
250) -> Result<Output> {
251    let table = catalog_manager
252        .table(
253            query_ctx.current_catalog(),
254            INFORMATION_SCHEMA_NAME,
255            table_name,
256            Some(&query_ctx),
257        )
258        .await
259        .context(error::CatalogSnafu)?
260        .with_context(|| error::TableNotFoundSnafu {
261            table: format_full_table_name(
262                query_ctx.current_catalog(),
263                INFORMATION_SCHEMA_NAME,
264                table_name,
265            ),
266        })?;
267
268    let dataframe = query_engine.read_table(table)?;
269
270    // Apply filters
271    let dataframe = filters.into_iter().try_fold(dataframe, |df, expr| {
272        df.filter(expr).context(error::PlanSqlSnafu)
273    })?;
274
275    // Apply `like` predicate if exists
276    let dataframe = if let (ShowKind::Like(ident), Some(field)) = (&kind, like_field) {
277        dataframe
278            .filter(col(field).like(lit(ident.value.clone())))
279            .context(error::PlanSqlSnafu)?
280    } else {
281        dataframe
282    };
283
284    // Apply sorting
285    let dataframe = if sort.is_empty() {
286        dataframe
287    } else {
288        dataframe.sort(sort).context(error::PlanSqlSnafu)?
289    };
290
291    // Apply select
292    let dataframe = if select.is_empty() {
293        if projects.is_empty() {
294            dataframe
295        } else {
296            let projection = projects
297                .iter()
298                .map(|x| col(x.0).alias(x.1))
299                .collect::<Vec<_>>();
300            dataframe.select(projection).context(error::PlanSqlSnafu)?
301        }
302    } else {
303        dataframe.select(select).context(error::PlanSqlSnafu)?
304    };
305
306    // Apply projection
307    let dataframe = projects
308        .into_iter()
309        .try_fold(dataframe, |df, (column, renamed_column)| {
310            df.with_column_renamed(column, renamed_column)
311                .context(error::PlanSqlSnafu)
312        })?;
313
314    let dataframe = match kind {
315        ShowKind::All | ShowKind::Like(_) => {
316            // Like kind is processed above
317            dataframe
318        }
319        ShowKind::Where(filter) => {
320            // Cast the results into VIEW for `where` clause,
321            // which is evaluated against the column names displayed by the SHOW statement.
322            let view = dataframe.into_view();
323            let dataframe = SessionContext::new_with_state(
324                query_engine
325                    .engine_context(query_ctx.clone())
326                    .state()
327                    .clone(),
328            )
329            .read_table(view)?;
330
331            let planner = query_engine.planner();
332            let planner = planner
333                .as_any()
334                .downcast_ref::<DfLogicalPlanner>()
335                .expect("Must be the datafusion planner");
336
337            let filter = planner
338                .sql_to_expr(filter, dataframe.schema(), false, query_ctx)
339                .await?;
340
341            // Apply the `where` clause filters
342            dataframe.filter(filter).context(error::PlanSqlSnafu)?
343        }
344    };
345
346    let stream = dataframe.execute_stream().await?;
347
348    Ok(Output::new_with_stream(Box::pin(
349        RecordBatchStreamAdapter::try_new(stream).context(error::CreateRecordBatchSnafu)?,
350    )))
351}
352
353/// Execute `SHOW COLUMNS` statement.
354pub async fn show_columns(
355    stmt: ShowColumns,
356    query_engine: &QueryEngineRef,
357    catalog_manager: &CatalogManagerRef,
358    query_ctx: QueryContextRef,
359) -> Result<Output> {
360    let schema_name = if let Some(database) = stmt.database {
361        database
362    } else {
363        query_ctx.current_schema()
364    };
365
366    let projects = if stmt.full {
367        vec![
368            (columns::COLUMN_NAME, FIELD_COLUMN),
369            (columns::DATA_TYPE, COLUMN_TYPE_COLUMN),
370            (columns::COLLATION_NAME, COLUMN_COLLATION_COLUMN),
371            (columns::IS_NULLABLE, COLUMN_NULLABLE_COLUMN),
372            (columns::COLUMN_KEY, COLUMN_KEY_COLUMN),
373            (columns::COLUMN_DEFAULT, COLUMN_DEFAULT_COLUMN),
374            (columns::COLUMN_COMMENT, COLUMN_COMMENT_COLUMN),
375            (columns::PRIVILEGES, COLUMN_PRIVILEGES_COLUMN),
376            (columns::EXTRA, COLUMN_EXTRA_COLUMN),
377            (columns::GREPTIME_DATA_TYPE, COLUMN_GREPTIME_TYPE_COLUMN),
378        ]
379    } else {
380        vec![
381            (columns::COLUMN_NAME, FIELD_COLUMN),
382            (columns::DATA_TYPE, COLUMN_TYPE_COLUMN),
383            (columns::IS_NULLABLE, COLUMN_NULLABLE_COLUMN),
384            (columns::COLUMN_KEY, COLUMN_KEY_COLUMN),
385            (columns::COLUMN_DEFAULT, COLUMN_DEFAULT_COLUMN),
386            (columns::EXTRA, COLUMN_EXTRA_COLUMN),
387            (columns::GREPTIME_DATA_TYPE, COLUMN_GREPTIME_TYPE_COLUMN),
388        ]
389    };
390
391    let filters = vec![
392        col(columns::TABLE_NAME).eq(lit(&stmt.table)),
393        col(columns::TABLE_SCHEMA).eq(lit(schema_name.clone())),
394        col(columns::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())),
395    ];
396    let like_field = Some(columns::COLUMN_NAME);
397    let sort = vec![col(columns::COLUMN_NAME).sort(true, true)];
398
399    query_from_information_schema_table(
400        query_engine,
401        catalog_manager,
402        query_ctx,
403        COLUMNS,
404        vec![],
405        projects,
406        filters,
407        like_field,
408        sort,
409        stmt.kind,
410    )
411    .await
412}
413
414/// Execute `SHOW INDEX` statement.
415pub async fn show_index(
416    stmt: ShowIndex,
417    query_engine: &QueryEngineRef,
418    catalog_manager: &CatalogManagerRef,
419    query_ctx: QueryContextRef,
420) -> Result<Output> {
421    let schema_name = if let Some(database) = stmt.database {
422        database
423    } else {
424        query_ctx.current_schema()
425    };
426
427    let select = vec![
428        col(statistics::TABLE_NAME).alias(INDEX_TABLE_COLUMN),
429        col(statistics::NON_UNIQUE).alias(INDEX_NONT_UNIQUE_COLUMN),
430        col(statistics::INDEX_NAME).alias(INDEX_KEY_NAME_COLUMN),
431        col(statistics::SEQ_IN_INDEX).alias(INDEX_SEQ_IN_INDEX_COLUMN),
432        col(statistics::COLUMN_NAME).alias(INDEX_COLUMN_NAME_COLUMN),
433        col(statistics::COLLATION).alias(COLUMN_COLLATION_COLUMN),
434        col(statistics::CARDINALITY).alias(INDEX_CARDINALITY_COLUMN),
435        col(statistics::SUB_PART).alias(INDEX_SUB_PART_COLUMN),
436        col(statistics::PACKED).alias(INDEX_PACKED_COLUMN),
437        col(statistics::NULLABLE).alias(COLUMN_NULLABLE_COLUMN),
438        col(statistics::INDEX_TYPE).alias(INDEX_INDEX_TYPE_COLUMN),
439        col(statistics::COMMENT).alias(COLUMN_COMMENT_COLUMN),
440        col(statistics::INDEX_COMMENT).alias(INDEX_COMMENT_COLUMN),
441        col(statistics::IS_VISIBLE).alias(INDEX_VISIBLE_COLUMN),
442        col(statistics::EXPRESSION).alias(INDEX_EXPRESSION_COLUMN),
443    ];
444
445    let projects = vec![
446        (statistics::TABLE_NAME, INDEX_TABLE_COLUMN),
447        (INDEX_NONT_UNIQUE_COLUMN, INDEX_NONT_UNIQUE_COLUMN),
448        (statistics::INDEX_NAME, INDEX_KEY_NAME_COLUMN),
449        (statistics::SEQ_IN_INDEX, INDEX_SEQ_IN_INDEX_COLUMN),
450        (statistics::COLUMN_NAME, INDEX_COLUMN_NAME_COLUMN),
451        (COLUMN_COLLATION_COLUMN, COLUMN_COLLATION_COLUMN),
452        (INDEX_CARDINALITY_COLUMN, INDEX_CARDINALITY_COLUMN),
453        (INDEX_SUB_PART_COLUMN, INDEX_SUB_PART_COLUMN),
454        (INDEX_PACKED_COLUMN, INDEX_PACKED_COLUMN),
455        (COLUMN_NULLABLE_COLUMN, COLUMN_NULLABLE_COLUMN),
456        (statistics::INDEX_TYPE, INDEX_INDEX_TYPE_COLUMN),
457        (COLUMN_COMMENT_COLUMN, COLUMN_COMMENT_COLUMN),
458        (INDEX_COMMENT_COLUMN, INDEX_COMMENT_COLUMN),
459        (INDEX_VISIBLE_COLUMN, INDEX_VISIBLE_COLUMN),
460        (INDEX_EXPRESSION_COLUMN, INDEX_EXPRESSION_COLUMN),
461    ];
462
463    let filters = vec![
464        col(statistics::TABLE_NAME).eq(lit(&stmt.table)),
465        col(statistics::TABLE_SCHEMA).eq(lit(schema_name.clone())),
466    ];
467    let like_field = None;
468    let sort = vec![
469        col(statistics::INDEX_NAME).sort(true, true),
470        col(statistics::SEQ_IN_INDEX).sort(true, true),
471    ];
472
473    query_from_information_schema_table(
474        query_engine,
475        catalog_manager,
476        query_ctx,
477        STATISTICS,
478        select,
479        projects,
480        filters,
481        like_field,
482        sort,
483        stmt.kind,
484    )
485    .await
486}
487
488/// Execute `SHOW REGION` statement.
489pub async fn show_region(
490    stmt: ShowRegion,
491    query_engine: &QueryEngineRef,
492    catalog_manager: &CatalogManagerRef,
493    query_ctx: QueryContextRef,
494) -> Result<Output> {
495    let schema_name = if let Some(database) = stmt.database {
496        database
497    } else {
498        query_ctx.current_schema()
499    };
500
501    let filters = vec![
502        col(region_peers::TABLE_NAME).eq(lit(&stmt.table)),
503        col(region_peers::TABLE_SCHEMA).eq(lit(schema_name.clone())),
504        col(region_peers::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())),
505    ];
506    let projects = vec![
507        (region_peers::TABLE_NAME, "Table"),
508        (region_peers::REGION_ID, "Region"),
509        (region_peers::PEER_ID, "Peer"),
510        (region_peers::IS_LEADER, "Leader"),
511    ];
512
513    let like_field = None;
514    let sort = vec![
515        col(columns::REGION_ID).sort(true, true),
516        col(columns::PEER_ID).sort(true, true),
517    ];
518
519    query_from_information_schema_table(
520        query_engine,
521        catalog_manager,
522        query_ctx,
523        REGION_PEERS,
524        vec![],
525        projects,
526        filters,
527        like_field,
528        sort,
529        stmt.kind,
530    )
531    .await
532}
533
534/// Execute [`ShowTables`] statement and return the [`Output`] if success.
535pub async fn show_tables(
536    stmt: ShowTables,
537    query_engine: &QueryEngineRef,
538    catalog_manager: &CatalogManagerRef,
539    query_ctx: QueryContextRef,
540) -> Result<Output> {
541    let schema_name = if let Some(database) = stmt.database {
542        database
543    } else {
544        query_ctx.current_schema()
545    };
546
547    // MySQL renames `table_name` to `Tables_in_{schema}` for protocol compatibility
548    let tables_column = format!("Tables_in_{}", schema_name);
549    let projects = if stmt.full {
550        vec![
551            (tables::TABLE_NAME, tables_column.as_str()),
552            (tables::TABLE_TYPE, TABLE_TYPE_COLUMN),
553        ]
554    } else {
555        vec![(tables::TABLE_NAME, tables_column.as_str())]
556    };
557    let filters = vec![
558        col(tables::TABLE_SCHEMA).eq(lit(schema_name.clone())),
559        col(tables::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())),
560    ];
561    let like_field = Some(tables::TABLE_NAME);
562    let sort = vec![col(tables::TABLE_NAME).sort(true, true)];
563
564    // Transform the WHERE clause for backward compatibility:
565    // Replace "Tables" with "Tables_in_{schema}" to support old queries
566    let kind = match stmt.kind {
567        ShowKind::Where(mut filter) => {
568            replace_column_in_expr(&mut filter, "Tables", &tables_column);
569            ShowKind::Where(filter)
570        }
571        other => other,
572    };
573
574    query_from_information_schema_table(
575        query_engine,
576        catalog_manager,
577        query_ctx,
578        TABLES,
579        vec![],
580        projects,
581        filters,
582        like_field,
583        sort,
584        kind,
585    )
586    .await
587}
588
589/// Execute [`ShowTableStatus`] statement and return the [`Output`] if success.
590pub async fn show_table_status(
591    stmt: ShowTableStatus,
592    query_engine: &QueryEngineRef,
593    catalog_manager: &CatalogManagerRef,
594    query_ctx: QueryContextRef,
595) -> Result<Output> {
596    let schema_name = if let Some(database) = stmt.database {
597        database
598    } else {
599        query_ctx.current_schema()
600    };
601
602    // Refer to https://dev.mysql.com/doc/refman/8.4/en/show-table-status.html
603    let projects = vec![
604        (tables::TABLE_NAME, "Name"),
605        (tables::ENGINE, "Engine"),
606        (tables::VERSION, "Version"),
607        (tables::ROW_FORMAT, "Row_format"),
608        (tables::TABLE_ROWS, "Rows"),
609        (tables::AVG_ROW_LENGTH, "Avg_row_length"),
610        (tables::DATA_LENGTH, "Data_length"),
611        (tables::MAX_DATA_LENGTH, "Max_data_length"),
612        (tables::INDEX_LENGTH, "Index_length"),
613        (tables::DATA_FREE, "Data_free"),
614        (tables::AUTO_INCREMENT, "Auto_increment"),
615        (tables::CREATE_TIME, "Create_time"),
616        (tables::UPDATE_TIME, "Update_time"),
617        (tables::CHECK_TIME, "Check_time"),
618        (tables::TABLE_COLLATION, "Collation"),
619        (tables::CHECKSUM, "Checksum"),
620        (tables::CREATE_OPTIONS, "Create_options"),
621        (tables::TABLE_COMMENT, "Comment"),
622    ];
623
624    let filters = vec![
625        col(tables::TABLE_SCHEMA).eq(lit(schema_name.clone())),
626        col(tables::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())),
627    ];
628    let like_field = Some(tables::TABLE_NAME);
629    let sort = vec![col(tables::TABLE_NAME).sort(true, true)];
630
631    query_from_information_schema_table(
632        query_engine,
633        catalog_manager,
634        query_ctx,
635        TABLES,
636        vec![],
637        projects,
638        filters,
639        like_field,
640        sort,
641        stmt.kind,
642    )
643    .await
644}
645
646/// Execute `SHOW COLLATION` statement and returns the `Output` if success.
647pub async fn show_collations(
648    kind: ShowKind,
649    query_engine: &QueryEngineRef,
650    catalog_manager: &CatalogManagerRef,
651    query_ctx: QueryContextRef,
652) -> Result<Output> {
653    // Refer to https://dev.mysql.com/doc/refman/8.0/en/show-collation.html
654    let projects = vec![
655        ("collation_name", "Collation"),
656        ("character_set_name", "Charset"),
657        ("id", "Id"),
658        ("is_default", "Default"),
659        ("is_compiled", "Compiled"),
660        ("sortlen", "Sortlen"),
661    ];
662
663    let filters = vec![];
664    let like_field = Some("collation_name");
665    let sort = vec![];
666
667    query_from_information_schema_table(
668        query_engine,
669        catalog_manager,
670        query_ctx,
671        COLLATIONS,
672        vec![],
673        projects,
674        filters,
675        like_field,
676        sort,
677        kind,
678    )
679    .await
680}
681
682/// Execute `SHOW CHARSET` statement and returns the `Output` if success.
683pub async fn show_charsets(
684    kind: ShowKind,
685    query_engine: &QueryEngineRef,
686    catalog_manager: &CatalogManagerRef,
687    query_ctx: QueryContextRef,
688) -> Result<Output> {
689    // Refer to https://dev.mysql.com/doc/refman/8.0/en/show-character-set.html
690    let projects = vec![
691        ("character_set_name", "Charset"),
692        ("description", "Description"),
693        ("default_collate_name", "Default collation"),
694        ("maxlen", "Maxlen"),
695    ];
696
697    let filters = vec![];
698    let like_field = Some("character_set_name");
699    let sort = vec![];
700
701    query_from_information_schema_table(
702        query_engine,
703        catalog_manager,
704        query_ctx,
705        CHARACTER_SETS,
706        vec![],
707        projects,
708        filters,
709        like_field,
710        sort,
711        kind,
712    )
713    .await
714}
715
716pub fn show_variable(stmt: ShowVariables, query_ctx: QueryContextRef) -> Result<Output> {
717    let variable = stmt.variable.to_string().to_uppercase();
718    let value = match variable.as_str() {
719        "SYSTEM_TIME_ZONE" | "SYSTEM_TIMEZONE" => get_timezone(None).to_string(),
720        "TIME_ZONE" | "TIMEZONE" => query_ctx.timezone().to_string(),
721        "READ_PREFERENCE" => query_ctx.read_preference().to_string(),
722        "DATESTYLE" => {
723            let (style, order) = *query_ctx.configuration_parameter().pg_datetime_style();
724            format!("{}, {}", style, order)
725        }
726        "INTERVALSTYLE" => {
727            let style = *query_ctx
728                .configuration_parameter()
729                .pg_intervalstyle_format();
730            style.to_string()
731        }
732        "MAX_EXECUTION_TIME"
733            if query_ctx.channel() == Channel::Mysql => {
734                query_ctx.query_timeout_as_millis().to_string()
735            }
736        "STATEMENT_TIMEOUT"
737            // Add time units to postgres query timeout display.
738            if query_ctx.channel() == Channel::Postgres => {
739                let mut timeout = query_ctx.query_timeout_as_millis().to_string();
740                timeout.push_str("ms");
741                timeout
742            }
743        _ => return UnsupportedVariableSnafu { name: variable }.fail(),
744    };
745    let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
746        variable,
747        ConcreteDataType::string_datatype(),
748        false,
749    )]));
750    let records = RecordBatches::try_from_columns(
751        schema,
752        vec![Arc::new(StringVector::from(vec![value])) as _],
753    )
754    .context(error::CreateRecordBatchSnafu)?;
755    Ok(Output::new_with_record_batches(records))
756}
757
758pub async fn show_status(_query_ctx: QueryContextRef) -> Result<Output> {
759    let schema = Arc::new(Schema::new(vec![
760        ColumnSchema::new("Variable_name", ConcreteDataType::string_datatype(), false),
761        ColumnSchema::new("Value", ConcreteDataType::string_datatype(), true),
762    ]));
763    let records = RecordBatches::try_from_columns(
764        schema,
765        vec![
766            Arc::new(StringVector::from(Vec::<&str>::new())) as _,
767            Arc::new(StringVector::from(Vec::<&str>::new())) as _,
768        ],
769    )
770    .context(error::CreateRecordBatchSnafu)?;
771    Ok(Output::new_with_record_batches(records))
772}
773
774pub async fn show_search_path(_query_ctx: QueryContextRef) -> Result<Output> {
775    let schema = Arc::new(Schema::new(vec![ColumnSchema::new(
776        "search_path",
777        ConcreteDataType::string_datatype(),
778        false,
779    )]));
780    let records = RecordBatches::try_from_columns(
781        schema,
782        vec![Arc::new(StringVector::from(vec![_query_ctx.current_schema()])) as _],
783    )
784    .context(error::CreateRecordBatchSnafu)?;
785    Ok(Output::new_with_record_batches(records))
786}
787
788pub fn show_create_database(database_name: &str, options: OptionMap) -> Result<Output> {
789    let stmt = CreateDatabase {
790        name: ObjectName::from(vec![Ident::new(database_name)]),
791        if_not_exists: true,
792        options,
793    };
794    let sql = format!("{stmt}");
795    let columns = vec![
796        Arc::new(StringVector::from(vec![database_name.to_string()])) as _,
797        Arc::new(StringVector::from(vec![sql])) as _,
798    ];
799    let records =
800        RecordBatches::try_from_columns(SHOW_CREATE_DATABASE_OUTPUT_SCHEMA.clone(), columns)
801            .context(error::CreateRecordBatchSnafu)?;
802    Ok(Output::new_with_record_batches(records))
803}
804
805pub fn show_create_table(
806    table_info: TableInfoRef,
807    schema_options: Option<SchemaOptions>,
808    partitions: Option<Partitions>,
809    query_ctx: QueryContextRef,
810) -> Result<Output> {
811    let table_name = table_info.name.clone();
812
813    let quote_style = query_ctx.quote_style();
814
815    let mut stmt = create_table_stmt(&table_info, schema_options, quote_style)?;
816    stmt.partitions = partitions.map(|mut p| {
817        p.set_quote(quote_style);
818        p
819    });
820    let sql = format!("{}", stmt);
821    let columns = vec![
822        Arc::new(StringVector::from(vec![table_name])) as _,
823        Arc::new(StringVector::from(vec![sql])) as _,
824    ];
825    let records = RecordBatches::try_from_columns(SHOW_CREATE_TABLE_OUTPUT_SCHEMA.clone(), columns)
826        .context(error::CreateRecordBatchSnafu)?;
827
828    Ok(Output::new_with_record_batches(records))
829}
830
831pub fn show_create_foreign_table_for_pg(
832    table: TableRef,
833    _query_ctx: QueryContextRef,
834) -> Result<Output> {
835    let table_info = table.table_info();
836
837    let table_meta = &table_info.meta;
838    let table_name = &table_info.name;
839    let schema = &table_info.meta.schema;
840    let is_metric_engine = is_metric_engine(&table_meta.engine);
841
842    let columns = schema
843        .column_schemas()
844        .iter()
845        .filter_map(|c| {
846            if is_metric_engine && is_metric_engine_internal_column(&c.name) {
847                None
848            } else {
849                Some(format!(
850                    "\"{}\" {}",
851                    c.name,
852                    c.data_type.postgres_datatype_name()
853                ))
854            }
855        })
856        .join(",\n  ");
857
858    let sql = format!(
859        r#"CREATE FOREIGN TABLE ft_{} (
860  {}
861)
862SERVER greptimedb
863OPTIONS (table_name '{}')"#,
864        table_name, columns, table_name
865    );
866
867    let columns = vec![
868        Arc::new(StringVector::from(vec![table_name.clone()])) as _,
869        Arc::new(StringVector::from(vec![sql])) as _,
870    ];
871    let records = RecordBatches::try_from_columns(SHOW_CREATE_TABLE_OUTPUT_SCHEMA.clone(), columns)
872        .context(error::CreateRecordBatchSnafu)?;
873
874    Ok(Output::new_with_record_batches(records))
875}
876
877pub fn show_create_view(
878    view_name: ObjectName,
879    definition: &str,
880    query_ctx: QueryContextRef,
881) -> Result<Output> {
882    let mut parser_ctx =
883        ParserContext::new(query_ctx.sql_dialect(), definition).context(error::SqlSnafu)?;
884
885    let Statement::CreateView(create_view) =
886        parser_ctx.parse_statement().context(error::SqlSnafu)?
887    else {
888        // MUST be `CreateView` statement.
889        unreachable!();
890    };
891
892    let stmt = CreateView {
893        name: view_name.clone(),
894        columns: create_view.columns,
895        query: create_view.query,
896        or_replace: create_view.or_replace,
897        if_not_exists: create_view.if_not_exists,
898    };
899
900    let sql = format!("{}", stmt);
901    let columns = vec![
902        Arc::new(StringVector::from(vec![view_name.to_string()])) as _,
903        Arc::new(StringVector::from(vec![sql])) as _,
904    ];
905    let records = RecordBatches::try_from_columns(SHOW_CREATE_VIEW_OUTPUT_SCHEMA.clone(), columns)
906        .context(error::CreateRecordBatchSnafu)?;
907
908    Ok(Output::new_with_record_batches(records))
909}
910
911/// Execute [`ShowViews`] statement and return the [`Output`] if success.
912pub async fn show_views(
913    stmt: ShowViews,
914    query_engine: &QueryEngineRef,
915    catalog_manager: &CatalogManagerRef,
916    query_ctx: QueryContextRef,
917) -> Result<Output> {
918    let schema_name = if let Some(database) = stmt.database {
919        database
920    } else {
921        query_ctx.current_schema()
922    };
923
924    let projects = vec![(tables::TABLE_NAME, VIEWS_COLUMN)];
925    let filters = vec![
926        col(tables::TABLE_SCHEMA).eq(lit(schema_name.clone())),
927        col(tables::TABLE_CATALOG).eq(lit(query_ctx.current_catalog())),
928    ];
929    let like_field = Some(tables::TABLE_NAME);
930    let sort = vec![col(tables::TABLE_NAME).sort(true, true)];
931
932    query_from_information_schema_table(
933        query_engine,
934        catalog_manager,
935        query_ctx,
936        VIEWS,
937        vec![],
938        projects,
939        filters,
940        like_field,
941        sort,
942        stmt.kind,
943    )
944    .await
945}
946
947/// Execute [`ShowFlows`] statement and return the [`Output`] if success.
948pub async fn show_flows(
949    stmt: ShowFlows,
950    query_engine: &QueryEngineRef,
951    catalog_manager: &CatalogManagerRef,
952    query_ctx: QueryContextRef,
953) -> Result<Output> {
954    let projects = vec![(flows::FLOW_NAME, FLOWS_COLUMN)];
955    let filters = vec![col(flows::TABLE_CATALOG).eq(lit(query_ctx.current_catalog()))];
956    let like_field = Some(flows::FLOW_NAME);
957    let sort = vec![col(flows::FLOW_NAME).sort(true, true)];
958
959    query_from_information_schema_table(
960        query_engine,
961        catalog_manager,
962        query_ctx,
963        FLOWS,
964        vec![],
965        projects,
966        filters,
967        like_field,
968        sort,
969        stmt.kind,
970    )
971    .await
972}
973
974#[cfg(feature = "enterprise")]
975pub async fn show_triggers(
976    stmt: sql::statements::show::trigger::ShowTriggers,
977    query_engine: &QueryEngineRef,
978    catalog_manager: &CatalogManagerRef,
979    query_ctx: QueryContextRef,
980) -> Result<Output> {
981    const TRIGGER_NAME: &str = "trigger_name";
982    const TRIGGERS_COLUMN: &str = "Triggers";
983
984    let projects = vec![(TRIGGER_NAME, TRIGGERS_COLUMN)];
985    let like_field = Some(TRIGGER_NAME);
986    let sort = vec![col(TRIGGER_NAME).sort(true, true)];
987
988    query_from_information_schema_table(
989        query_engine,
990        catalog_manager,
991        query_ctx,
992        catalog::information_schema::TRIGGERS,
993        vec![],
994        projects,
995        vec![],
996        like_field,
997        sort,
998        stmt.kind,
999    )
1000    .await
1001}
1002
1003pub fn show_create_flow(
1004    flow_name: ObjectName,
1005    flow_val: FlowInfoValue,
1006    query_ctx: QueryContextRef,
1007) -> Result<Output> {
1008    let mut parser_ctx =
1009        ParserContext::new(query_ctx.sql_dialect(), flow_val.raw_sql()).context(error::SqlSnafu)?;
1010
1011    let query = parser_ctx.parse_statement().context(error::SqlSnafu)?;
1012
1013    // since prom ql will parse `now()` to a fixed time, we need to not use it for generating raw query
1014    let raw_query = match &query {
1015        Statement::Tql(_) => flow_val.raw_sql().clone(),
1016        _ => query.to_string(),
1017    };
1018
1019    let query = Box::new(SqlOrTql::try_from_statement(query, &raw_query).context(error::SqlSnafu)?);
1020
1021    let comment = if flow_val.comment().is_empty() {
1022        None
1023    } else {
1024        Some(flow_val.comment().clone())
1025    };
1026
1027    let stmt = CreateFlow {
1028        flow_name,
1029        sink_table_name: ObjectName::from(vec![
1030            Ident::new(&flow_val.sink_table_name().schema_name),
1031            Ident::new(&flow_val.sink_table_name().table_name),
1032        ]),
1033        // notice we don't want `OR REPLACE` and `IF NOT EXISTS` in same sql since it's unclear what to do
1034        // so we set `or_replace` to false.
1035        or_replace: false,
1036        if_not_exists: true,
1037        expire_after: flow_val.expire_after(),
1038        eval_interval: flow_val.eval_interval(),
1039        comment,
1040        flow_options: OptionMap::from_filtered_string_map(
1041            flow_val.options(),
1042            &[FlowType::FLOW_TYPE_KEY],
1043        ),
1044        query,
1045    };
1046
1047    let sql = format!("{}", stmt);
1048    let columns = vec![
1049        Arc::new(StringVector::from(vec![flow_val.flow_name().clone()])) as _,
1050        Arc::new(StringVector::from(vec![sql])) as _,
1051    ];
1052    let records = RecordBatches::try_from_columns(SHOW_CREATE_FLOW_OUTPUT_SCHEMA.clone(), columns)
1053        .context(error::CreateRecordBatchSnafu)?;
1054
1055    Ok(Output::new_with_record_batches(records))
1056}
1057
1058pub fn describe_table(table: TableRef) -> Result<Output> {
1059    let table_info = table.table_info();
1060    let columns_schemas = table_info.meta.schema.column_schemas();
1061    let columns = vec![
1062        describe_column_names(columns_schemas),
1063        describe_column_types(columns_schemas),
1064        describe_column_keys(columns_schemas, &table_info.meta.primary_key_indices),
1065        describe_column_nullables(columns_schemas),
1066        describe_column_defaults(columns_schemas),
1067        describe_column_semantic_types(columns_schemas, &table_info.meta.primary_key_indices),
1068    ];
1069    let records = RecordBatches::try_from_columns(DESCRIBE_TABLE_OUTPUT_SCHEMA.clone(), columns)
1070        .context(error::CreateRecordBatchSnafu)?;
1071    Ok(Output::new_with_record_batches(records))
1072}
1073
1074fn describe_column_names(columns_schemas: &[ColumnSchema]) -> VectorRef {
1075    Arc::new(StringVector::from_iterator(
1076        columns_schemas.iter().map(|cs| cs.name.as_str()),
1077    ))
1078}
1079
1080fn describe_column_types(columns_schemas: &[ColumnSchema]) -> VectorRef {
1081    Arc::new(StringVector::from(
1082        columns_schemas
1083            .iter()
1084            .map(|cs| describe_column_type_name(&cs.data_type))
1085            .collect::<Vec<_>>(),
1086    ))
1087}
1088
1089fn describe_column_type_name(data_type: &ConcreteDataType) -> String {
1090    match data_type {
1091        ConcreteDataType::Json(json_type) if json_type.is_json2() => "Json2".to_string(),
1092        data_type => data_type.name(),
1093    }
1094}
1095
1096fn describe_column_keys(
1097    columns_schemas: &[ColumnSchema],
1098    primary_key_indices: &[usize],
1099) -> VectorRef {
1100    Arc::new(StringVector::from_iterator(
1101        columns_schemas.iter().enumerate().map(|(i, cs)| {
1102            if cs.is_time_index() || primary_key_indices.contains(&i) {
1103                PRI_KEY
1104            } else {
1105                ""
1106            }
1107        }),
1108    ))
1109}
1110
1111fn describe_column_nullables(columns_schemas: &[ColumnSchema]) -> VectorRef {
1112    Arc::new(StringVector::from_iterator(columns_schemas.iter().map(
1113        |cs| {
1114            if cs.is_nullable() { YES_STR } else { NO_STR }
1115        },
1116    )))
1117}
1118
1119fn describe_column_defaults(columns_schemas: &[ColumnSchema]) -> VectorRef {
1120    Arc::new(StringVector::from(
1121        columns_schemas
1122            .iter()
1123            .map(|cs| {
1124                cs.default_constraint()
1125                    .map_or(String::from(""), |dc| dc.to_string())
1126            })
1127            .collect::<Vec<String>>(),
1128    ))
1129}
1130
1131fn describe_column_semantic_types(
1132    columns_schemas: &[ColumnSchema],
1133    primary_key_indices: &[usize],
1134) -> VectorRef {
1135    Arc::new(StringVector::from_iterator(
1136        columns_schemas.iter().enumerate().map(|(i, cs)| {
1137            if primary_key_indices.contains(&i) {
1138                SEMANTIC_TYPE_PRIMARY_KEY
1139            } else if cs.is_time_index() {
1140                SEMANTIC_TYPE_TIME_INDEX
1141            } else {
1142                SEMANTIC_TYPE_FIELD
1143            }
1144        }),
1145    ))
1146}
1147
1148// lists files in the frontend to reduce unnecessary scan requests repeated in each datanode.
1149pub async fn prepare_file_table_files(
1150    options: &HashMap<String, String>,
1151    local_file_access: &LocalFileAccess,
1152) -> Result<(ObjectStore, Vec<String>)> {
1153    let url = options
1154        .get(FILE_TABLE_LOCATION_KEY)
1155        .context(error::MissingRequiredFieldSnafu {
1156            name: FILE_TABLE_LOCATION_KEY,
1157        })?;
1158
1159    let regex = options
1160        .get(FILE_TABLE_PATTERN_KEY)
1161        .map(|x| Regex::new(x))
1162        .transpose()
1163        .context(error::BuildRegexSnafu)?;
1164    let backend = build_backend_with_path(url, options, local_file_access)
1165        .await
1166        .context(error::BuildBackendSnafu)?;
1167    let source = if let Some(filename) = backend.object_path {
1168        Source::Filename(filename)
1169    } else {
1170        Source::Dir
1171    };
1172    let lister = Lister::new(backend.object_store.clone(), source, url.clone(), regex);
1173    // If we scan files in a directory every time the database restarts,
1174    // then it might lead to a potential undefined behavior:
1175    // If a user adds a file with an incompatible schema to that directory,
1176    // it will make the external table unavailable.
1177    let files = lister
1178        .list()
1179        .await
1180        .context(error::ListObjectsSnafu)?
1181        .into_iter()
1182        .filter_map(|entry| {
1183            if entry.path().ends_with('/') {
1184                None
1185            } else {
1186                Some(entry.path().to_string())
1187            }
1188        })
1189        .collect::<Vec<_>>();
1190    Ok((backend.object_store, files))
1191}
1192
1193pub async fn infer_file_table_schema(
1194    object_store: &ObjectStore,
1195    files: &[String],
1196    options: &HashMap<String, String>,
1197) -> Result<Schema> {
1198    let format = parse_file_table_format(options)?;
1199    let merged = infer_schemas(object_store, files, format.as_ref())
1200        .await
1201        .context(error::InferSchemaSnafu)?;
1202    Schema::try_from(merged).context(error::ConvertSchemaSnafu)
1203}
1204
1205// Converts the file column schemas to table column schemas.
1206// Returns the column schemas and the time index column name.
1207//
1208// More specifically, this function will do the following:
1209// 1. Add a default time index column if there is no time index column
1210//    in the file column schemas, or
1211// 2. If the file column schemas contain a column with name conflicts with
1212//    the default time index column, it will replace the column schema
1213//    with the default one.
1214pub fn file_column_schemas_to_table(
1215    file_column_schemas: &[ColumnSchema],
1216) -> (Vec<ColumnSchema>, String) {
1217    let mut column_schemas = file_column_schemas.to_owned();
1218    if let Some(time_index_column) = column_schemas.iter().find(|c| c.is_time_index()) {
1219        let time_index = time_index_column.name.clone();
1220        return (column_schemas, time_index);
1221    }
1222
1223    let timestamp_type = ConcreteDataType::timestamp_millisecond_datatype();
1224    let default_zero = Value::Timestamp(Timestamp::new_millisecond(0));
1225    let timestamp_column_schema = ColumnSchema::new(greptime_timestamp(), timestamp_type, false)
1226        .with_time_index(true)
1227        .with_default_constraint(Some(ColumnDefaultConstraint::Value(default_zero)))
1228        .unwrap();
1229
1230    if let Some(column_schema) = column_schemas
1231        .iter_mut()
1232        .find(|column_schema| column_schema.name == greptime_timestamp())
1233    {
1234        // Replace the column schema with the default one
1235        *column_schema = timestamp_column_schema;
1236    } else {
1237        column_schemas.push(timestamp_column_schema);
1238    }
1239
1240    (column_schemas, greptime_timestamp().to_string())
1241}
1242
1243/// This function checks if the column schemas from a file can be matched with
1244/// the column schemas of a table.
1245///
1246/// More specifically, for each column seen in the table schema,
1247/// - If the same column does exist in the file schema, it checks if the data
1248///   type of the file column can be casted into the form of the table column.
1249/// - If the same column does not exist in the file schema, it checks if the
1250///   table column is nullable or has a default constraint.
1251pub fn check_file_to_table_schema_compatibility(
1252    file_column_schemas: &[ColumnSchema],
1253    table_column_schemas: &[ColumnSchema],
1254) -> Result<()> {
1255    let file_schemas_map = file_column_schemas
1256        .iter()
1257        .map(|s| (s.name.clone(), s))
1258        .collect::<HashMap<_, _>>();
1259
1260    for table_column in table_column_schemas {
1261        if let Some(file_column) = file_schemas_map.get(&table_column.name) {
1262            // TODO(zhongzc): a temporary solution, we should use `can_cast_to` once it's ready.
1263            ensure!(
1264                file_column
1265                    .data_type
1266                    .can_arrow_type_cast_to(&table_column.data_type),
1267                error::ColumnSchemaIncompatibleSnafu {
1268                    column: table_column.name.clone(),
1269                    file_type: file_column.data_type.clone(),
1270                    table_type: table_column.data_type.clone(),
1271                }
1272            );
1273        } else {
1274            ensure!(
1275                table_column.is_nullable() || table_column.default_constraint().is_some(),
1276                error::ColumnSchemaNoDefaultSnafu {
1277                    column: table_column.name.clone(),
1278                }
1279            );
1280        }
1281    }
1282
1283    Ok(())
1284}
1285
1286fn parse_file_table_format(options: &HashMap<String, String>) -> Result<Box<dyn FileFormat>> {
1287    Ok(
1288        match Format::try_from(options).context(error::ParseFileFormatSnafu)? {
1289            Format::Csv(format) => Box::new(format),
1290            Format::Json(format) => Box::new(format),
1291            Format::Parquet(format) => Box::new(format),
1292            Format::Orc(format) => Box::new(format),
1293        },
1294    )
1295}
1296
1297pub async fn show_processlist(
1298    stmt: ShowProcessList,
1299    query_engine: &QueryEngineRef,
1300    catalog_manager: &CatalogManagerRef,
1301    query_ctx: QueryContextRef,
1302) -> Result<Output> {
1303    let projects = if stmt.full {
1304        vec![
1305            (process_list::ID, "Id"),
1306            (process_list::CATALOG, "Catalog"),
1307            (process_list::SCHEMAS, "Schema"),
1308            (process_list::CLIENT, "Client"),
1309            (process_list::FRONTEND, "Frontend"),
1310            (process_list::START_TIMESTAMP, "Start Time"),
1311            (process_list::ELAPSED_TIME, "Elapsed Time"),
1312            (process_list::QUERY, "Query"),
1313        ]
1314    } else {
1315        vec![
1316            (process_list::ID, "Id"),
1317            (process_list::CATALOG, "Catalog"),
1318            (process_list::QUERY, "Query"),
1319            (process_list::ELAPSED_TIME, "Elapsed Time"),
1320        ]
1321    };
1322
1323    let filters = vec![];
1324    let like_field = None;
1325    let sort = vec![col("id").sort(true, true)];
1326    query_from_information_schema_table(
1327        query_engine,
1328        catalog_manager,
1329        query_ctx.clone(),
1330        "process_list",
1331        vec![],
1332        projects.clone(),
1333        filters,
1334        like_field,
1335        sort,
1336        ShowKind::All,
1337    )
1338    .await
1339}
1340
1341#[cfg(test)]
1342mod test {
1343    use std::sync::Arc;
1344
1345    use common_query::{Output, OutputData};
1346    use common_recordbatch::{RecordBatch, RecordBatches};
1347    use common_time::Timezone;
1348    use common_time::timestamp::TimeUnit;
1349    use datatypes::prelude::ConcreteDataType;
1350    use datatypes::schema::{ColumnDefaultConstraint, ColumnSchema, Schema, SchemaRef};
1351    use datatypes::types::json_type::JsonNativeType;
1352    use datatypes::vectors::{StringVector, TimestampMillisecondVector, UInt32Vector, VectorRef};
1353    use session::context::QueryContextBuilder;
1354    use snafu::ResultExt;
1355    use sql::ast::{Ident, ObjectName};
1356    use sql::statements::show::ShowVariables;
1357    use table::TableRef;
1358    use table::test_util::MemTable;
1359
1360    use super::{describe_column_type_name, show_variable};
1361    use crate::error;
1362    use crate::error::Result;
1363    use crate::sql::{
1364        DESCRIBE_TABLE_OUTPUT_SCHEMA, NO_STR, SEMANTIC_TYPE_FIELD, SEMANTIC_TYPE_TIME_INDEX,
1365        YES_STR, describe_table,
1366    };
1367
1368    #[test]
1369    fn test_describe_table_multiple_columns() -> Result<()> {
1370        let table_name = "test_table";
1371        let schema = vec![
1372            ColumnSchema::new("t1", ConcreteDataType::uint32_datatype(), true),
1373            ColumnSchema::new(
1374                "t2",
1375                ConcreteDataType::timestamp_datatype(TimeUnit::Millisecond),
1376                false,
1377            )
1378            .with_default_constraint(Some(ColumnDefaultConstraint::Function(String::from(
1379                "current_timestamp()",
1380            ))))
1381            .unwrap()
1382            .with_time_index(true),
1383        ];
1384        let data = vec![
1385            Arc::new(UInt32Vector::from_slice([0])) as _,
1386            Arc::new(TimestampMillisecondVector::from_slice([0])) as _,
1387        ];
1388        let expected_columns = vec![
1389            Arc::new(StringVector::from(vec!["t1", "t2"])) as _,
1390            Arc::new(StringVector::from(vec!["UInt32", "TimestampMillisecond"])) as _,
1391            Arc::new(StringVector::from(vec!["", "PRI"])) as _,
1392            Arc::new(StringVector::from(vec![YES_STR, NO_STR])) as _,
1393            Arc::new(StringVector::from(vec!["", "current_timestamp()"])) as _,
1394            Arc::new(StringVector::from(vec![
1395                SEMANTIC_TYPE_FIELD,
1396                SEMANTIC_TYPE_TIME_INDEX,
1397            ])) as _,
1398        ];
1399
1400        describe_table_test_by_schema(table_name, schema, data, expected_columns)
1401    }
1402
1403    #[test]
1404    fn test_describe_column_type_name_json2() {
1405        assert_eq!(
1406            describe_column_type_name(&ConcreteDataType::json2(JsonNativeType::Null)),
1407            "Json2"
1408        );
1409        assert_eq!(
1410            describe_column_type_name(&ConcreteDataType::uint32_datatype()),
1411            "UInt32"
1412        );
1413    }
1414
1415    fn describe_table_test_by_schema(
1416        table_name: &str,
1417        schema: Vec<ColumnSchema>,
1418        data: Vec<VectorRef>,
1419        expected_columns: Vec<VectorRef>,
1420    ) -> Result<()> {
1421        let table_schema = SchemaRef::new(Schema::new(schema));
1422        let table = prepare_describe_table(table_name, table_schema, data);
1423
1424        let expected =
1425            RecordBatches::try_from_columns(DESCRIBE_TABLE_OUTPUT_SCHEMA.clone(), expected_columns)
1426                .context(error::CreateRecordBatchSnafu)?;
1427
1428        if let OutputData::RecordBatches(res) = describe_table(table)?.data {
1429            assert_eq!(res.take(), expected.take());
1430        } else {
1431            panic!("describe table must return record batch");
1432        }
1433
1434        Ok(())
1435    }
1436
1437    fn prepare_describe_table(
1438        table_name: &str,
1439        table_schema: SchemaRef,
1440        data: Vec<VectorRef>,
1441    ) -> TableRef {
1442        let record_batch = RecordBatch::new(table_schema, data).unwrap();
1443        MemTable::table(table_name, record_batch)
1444    }
1445
1446    #[test]
1447    fn test_show_variable() {
1448        assert_eq!(
1449            exec_show_variable("SYSTEM_TIME_ZONE", "Asia/Shanghai").unwrap(),
1450            "UTC"
1451        );
1452        assert_eq!(
1453            exec_show_variable("SYSTEM_TIMEZONE", "Asia/Shanghai").unwrap(),
1454            "UTC"
1455        );
1456        assert_eq!(
1457            exec_show_variable("TIME_ZONE", "Asia/Shanghai").unwrap(),
1458            "Asia/Shanghai"
1459        );
1460        assert_eq!(
1461            exec_show_variable("TIMEZONE", "Asia/Shanghai").unwrap(),
1462            "Asia/Shanghai"
1463        );
1464        assert!(exec_show_variable("TIME ZONE", "Asia/Shanghai").is_err());
1465        assert!(exec_show_variable("SYSTEM TIME ZONE", "Asia/Shanghai").is_err());
1466    }
1467
1468    fn exec_show_variable(variable: &str, tz: &str) -> Result<String> {
1469        let stmt = ShowVariables {
1470            variable: ObjectName::from(vec![Ident::new(variable)]),
1471        };
1472        let ctx = Arc::new(
1473            QueryContextBuilder::default()
1474                .timezone(Timezone::from_tz_string(tz).unwrap())
1475                .build(),
1476        );
1477        match show_variable(stmt, ctx) {
1478            Ok(Output {
1479                data: OutputData::RecordBatches(record),
1480                ..
1481            }) => {
1482                let record = record.take().first().cloned().unwrap();
1483                Ok(record.iter_column_as_string(0).next().unwrap().unwrap())
1484            }
1485            Ok(_) => unreachable!(),
1486            Err(e) => Err(e),
1487        }
1488    }
1489}