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