Skip to main content

operator/
statement.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 admin;
16mod comment;
17mod copy_database;
18mod copy_query_to;
19mod copy_table_from;
20mod copy_table_to;
21mod cursor;
22mod database_copy;
23pub mod ddl;
24mod describe;
25mod dml;
26pub mod export_database;
27pub mod export_logical_tables;
28pub mod import_packed;
29mod kill;
30pub mod semantic_graph;
31mod set;
32mod show;
33mod tql;
34
35use std::collections::HashMap;
36use std::sync::Arc;
37
38use api::v1::RowInsertRequests;
39use catalog::CatalogManagerRef;
40use catalog::kvbackend::KvBackendCatalogManager;
41use catalog::process_manager::ProcessManagerRef;
42use client::RecordBatches;
43use client::error::{ExternalSnafu as ClientExternalSnafu, Result as ClientResult};
44use client::inserter::{InsertOptions, Inserter};
45use common_datasource::object_store::LocalFileAccess;
46use common_error::ext::BoxedError;
47use common_meta::cache_invalidator::CacheInvalidatorRef;
48use common_meta::key::flow::{FlowMetadataManager, FlowMetadataManagerRef};
49use common_meta::key::schema_name::SchemaNameKey;
50use common_meta::key::view_info::{ViewInfoManager, ViewInfoManagerRef};
51use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
52use common_meta::kv_backend::KvBackendRef;
53use common_meta::procedure_executor::ProcedureExecutorRef;
54#[cfg(feature = "enterprise")]
55use common_meta::rpc::ddl::CreatorGrantIntent;
56use common_query::Output;
57use common_telemetry::{debug, tracing, warn};
58use common_time::Timestamp;
59use common_time::range::TimestampRange;
60use datafusion_expr::LogicalPlan;
61use datatypes::prelude::ConcreteDataType;
62use datatypes::schema::ColumnSchema;
63use humantime::format_duration;
64use itertools::Itertools;
65use partition::manager::PartitionRuleManagerRef;
66use query::QueryEngineRef;
67use query::parser::QueryStatement;
68use session::context::{Channel, QueryContextBuilder, QueryContextRef};
69use session::table_name::table_idents_to_full_name;
70use set::{set_query_timeout, set_read_preference, set_skip_wal};
71use snafu::{OptionExt, ResultExt, ensure};
72use sql::ast::ObjectNamePartExt;
73use sql::statements::OptionMap;
74use sql::statements::copy::{
75    CopyDatabase, CopyDatabaseArgument, CopyQueryToArgument, CopyTable, CopyTableArgument,
76};
77use sql::statements::set_variables::SetVariables;
78use sql::statements::show::ShowCreateTableVariant;
79use sql::statements::statement::Statement;
80use sql::util::format_raw_object_name;
81use sqlparser::ast::ObjectName;
82use store_api::mito_engine_options::{APPEND_MODE_KEY, TTL_KEY};
83use table::TableRef;
84use table::requests::{CopyDatabaseRequest, CopyDirection, CopyQueryToRequest, CopyTableRequest};
85use table::table_name::TableName;
86use table::table_reference::TableReference;
87
88pub use self::admin::{
89    AdminEventRecorderHandle, AdminFunctionLayer, AdminFunctionLayerRef,
90    AdminFunctionRecordingLayer, AdminFunctionRequest, AdminFunctionResponse, AdminFunctionService,
91    AdminFunctionServiceRef, admin_output_schema,
92};
93use self::set::{
94    set_bytea_output, set_datestyle, set_intervalstyle, set_timezone, validate_client_encoding,
95};
96use crate::error::{
97    self, CatalogSnafu, ExecLogicalPlanSnafu, ExternalSnafu, InvalidSqlSnafu, NotSupportedSnafu,
98    PlanStatementSnafu, Result, SchemaNotFoundSnafu, SqlCommonSnafu, TableMetadataManagerSnafu,
99    TableNotFoundSnafu, UnexpectedSnafu, UpgradeCatalogManagerRefSnafu,
100};
101use crate::insert::InserterRef;
102use crate::statement::copy_database::{COPY_DATABASE_TIME_END_KEY, COPY_DATABASE_TIME_START_KEY};
103use crate::statement::set::set_query_option;
104
105/// A configurator that customizes or enhances a [`StatementExecutor`].
106#[async_trait::async_trait]
107pub trait StatementExecutorConfigurator: Send + Sync {
108    async fn configure(
109        &self,
110        executor: StatementExecutor,
111        ctx: ExecutorConfigureContext,
112    ) -> std::result::Result<StatementExecutor, BoxedError>;
113}
114
115pub type StatementExecutorConfiguratorRef = Arc<dyn StatementExecutorConfigurator>;
116
117#[cfg(feature = "enterprise")]
118#[async_trait::async_trait]
119pub trait CreateDatabaseHandler: Send + Sync {
120    fn creator(
121        &self,
122        query_ctx: &QueryContextRef,
123    ) -> std::result::Result<Option<CreatorGrantIntent>, BoxedError>;
124
125    async fn refresh_current_user(
126        &self,
127        query_ctx: &QueryContextRef,
128    ) -> std::result::Result<(), BoxedError>;
129}
130
131#[cfg(feature = "enterprise")]
132pub type CreateDatabaseHandlerRef = Arc<dyn CreateDatabaseHandler>;
133
134pub struct ExecutorConfigureContext {
135    pub kv_backend: KvBackendRef,
136}
137
138#[derive(Clone)]
139pub struct StatementExecutor {
140    catalog_manager: CatalogManagerRef,
141    query_engine: QueryEngineRef,
142    procedure_executor: ProcedureExecutorRef,
143    table_metadata_manager: TableMetadataManagerRef,
144    flow_metadata_manager: FlowMetadataManagerRef,
145    view_info_manager: ViewInfoManagerRef,
146    partition_manager: PartitionRuleManagerRef,
147    cache_invalidator: CacheInvalidatorRef,
148    inserter: InserterRef,
149    process_manager: Option<ProcessManagerRef>,
150    origin_frontend_addr: String,
151    admin_function_service: AdminFunctionServiceRef,
152    pub(crate) local_file_access: LocalFileAccess,
153    #[cfg(feature = "enterprise")]
154    create_database_handler: Option<CreateDatabaseHandlerRef>,
155    #[cfg(feature = "enterprise")]
156    trigger_querier: Option<TriggerQuerierRef>,
157}
158
159pub type StatementExecutorRef = Arc<StatementExecutor>;
160
161/// Trait for querying trigger info, such as `SHOW CREATE TRIGGER` etc.
162#[cfg(feature = "enterprise")]
163#[async_trait::async_trait]
164pub trait TriggerQuerier: Send + Sync {
165    // Query the `SHOW CREATE TRIGGER` statement for the given trigger.
166    async fn show_create_trigger(
167        &self,
168        catalog: &str,
169        trigger: &str,
170        query_ctx: &QueryContextRef,
171    ) -> std::result::Result<Output, BoxedError>;
172
173    fn as_any(&self) -> &dyn std::any::Any;
174}
175
176#[cfg(feature = "enterprise")]
177pub type TriggerQuerierRef = Arc<dyn TriggerQuerier>;
178
179impl StatementExecutor {
180    #[allow(clippy::too_many_arguments)]
181    pub fn new(
182        catalog_manager: CatalogManagerRef,
183        query_engine: QueryEngineRef,
184        procedure_executor: ProcedureExecutorRef,
185        kv_backend: KvBackendRef,
186        cache_invalidator: CacheInvalidatorRef,
187        inserter: InserterRef,
188        partition_manager: PartitionRuleManagerRef,
189        process_manager: Option<ProcessManagerRef>,
190        origin_frontend_addr: String,
191        local_file_access: LocalFileAccess,
192    ) -> Self {
193        let admin_function_service = admin::new_admin_function_service(query_engine.clone());
194        Self {
195            catalog_manager,
196            query_engine,
197            procedure_executor,
198            table_metadata_manager: Arc::new(TableMetadataManager::new(kv_backend.clone())),
199            flow_metadata_manager: Arc::new(FlowMetadataManager::new(kv_backend.clone())),
200            view_info_manager: Arc::new(ViewInfoManager::new(kv_backend.clone())),
201            partition_manager,
202            cache_invalidator,
203            inserter,
204            process_manager,
205            origin_frontend_addr,
206            admin_function_service,
207            local_file_access,
208            #[cfg(feature = "enterprise")]
209            create_database_handler: None,
210            #[cfg(feature = "enterprise")]
211            trigger_querier: None,
212        }
213    }
214
215    /// Adds a layer around the ADMIN function execution service.
216    ///
217    /// The last added layer is the outermost layer.
218    pub fn with_admin_function_layer(mut self, layer: AdminFunctionLayerRef) -> Self {
219        self.admin_function_service = layer.layer(self.admin_function_service);
220        self
221    }
222
223    #[cfg(feature = "enterprise")]
224    pub fn with_trigger_querier(mut self, querier: TriggerQuerierRef) -> Self {
225        self.trigger_querier = Some(querier);
226        self
227    }
228
229    #[cfg(feature = "enterprise")]
230    pub fn with_create_database_handler(mut self, handler: CreateDatabaseHandlerRef) -> Self {
231        self.create_database_handler = Some(handler);
232        self
233    }
234
235    #[cfg(feature = "testing")]
236    pub async fn execute_stmt(
237        &self,
238        stmt: QueryStatement,
239        query_ctx: QueryContextRef,
240    ) -> Result<Output> {
241        match stmt {
242            QueryStatement::Sql(stmt) => self.execute_sql(stmt, query_ctx).await,
243            QueryStatement::Promql(_, _) => self.plan_exec(stmt, query_ctx).await,
244        }
245    }
246
247    #[tracing::instrument(skip_all)]
248    pub async fn execute_sql(&self, stmt: Statement, query_ctx: QueryContextRef) -> Result<Output> {
249        match stmt {
250            Statement::Query(_) | Statement::Explain(_) | Statement::Delete(_) => {
251                self.plan_exec(QueryStatement::Sql(stmt), query_ctx).await
252            }
253
254            Statement::DeclareCursor(declare_cursor) => {
255                self.declare_cursor(declare_cursor, query_ctx).await
256            }
257            Statement::FetchCursor(fetch_cursor) => {
258                self.fetch_cursor(fetch_cursor, query_ctx).await
259            }
260            Statement::CloseCursor(close_cursor) => {
261                self.close_cursor(close_cursor, query_ctx).await
262            }
263
264            Statement::Insert(insert) => self.insert(insert, query_ctx).await,
265
266            Statement::Tql(tql) => self.execute_tql(tql, query_ctx).await,
267
268            Statement::DescribeTable(stmt) => self.describe_table(stmt, query_ctx).await,
269
270            Statement::ShowDatabases(stmt) => self.show_databases(stmt, query_ctx).await,
271
272            Statement::ShowTables(stmt) => self.show_tables(stmt, query_ctx).await,
273
274            Statement::ShowTableStatus(stmt) => self.show_table_status(stmt, query_ctx).await,
275
276            Statement::ShowCollation(kind) => self.show_collation(kind, query_ctx).await,
277
278            Statement::ShowCharset(kind) => self.show_charset(kind, query_ctx).await,
279
280            Statement::ShowViews(stmt) => self.show_views(stmt, query_ctx).await,
281
282            Statement::ShowFlows(stmt) => self.show_flows(stmt, query_ctx).await,
283            Statement::ShowFlowStatus(stmt) => self.show_flow_status(stmt, query_ctx).await,
284
285            #[cfg(feature = "enterprise")]
286            Statement::ShowTriggers(stmt) => self.show_triggers(stmt, query_ctx).await,
287
288            Statement::Copy(sql::statements::copy::Copy::CopyQueryTo(stmt)) => {
289                let query_output = self
290                    .plan_exec(QueryStatement::Sql(*stmt.query), query_ctx)
291                    .await?;
292                let req = to_copy_query_request(stmt.arg)?;
293
294                self.copy_query_to(req, query_output)
295                    .await
296                    .map(Output::new_with_affected_rows)
297            }
298
299            Statement::Copy(sql::statements::copy::Copy::CopyTable(stmt)) => {
300                let req = to_copy_table_request(stmt, query_ctx.clone())?;
301                match req.direction {
302                    CopyDirection::Export => self
303                        .copy_table_to(req, query_ctx)
304                        .await
305                        .map(Output::new_with_affected_rows),
306                    CopyDirection::Import => self.copy_table_from(req, query_ctx).await,
307                }
308            }
309
310            Statement::Copy(sql::statements::copy::Copy::CopyDatabase(copy_database)) => {
311                match copy_database {
312                    CopyDatabase::To(arg) => {
313                        self.copy_database_to(
314                            to_copy_database_request(arg, &query_ctx)?,
315                            query_ctx.clone(),
316                        )
317                        .await
318                    }
319                    CopyDatabase::From(arg) => {
320                        self.copy_database_from(
321                            to_copy_database_request(arg, &query_ctx)?,
322                            query_ctx,
323                        )
324                        .await
325                    }
326                }
327            }
328
329            Statement::CreateTable(stmt) => {
330                let _ = self.create_table(stmt, query_ctx).await?;
331                Ok(Output::new_with_affected_rows(0))
332            }
333            Statement::CreateTableLike(stmt) => {
334                let _ = self.create_table_like(stmt, query_ctx).await?;
335                Ok(Output::new_with_affected_rows(0))
336            }
337            Statement::CreateExternalTable(stmt) => {
338                let _ = self.create_external_table(stmt, query_ctx).await?;
339                Ok(Output::new_with_affected_rows(0))
340            }
341            Statement::CreateFlow(stmt) => self.create_flow(stmt, query_ctx).await,
342            #[cfg(feature = "enterprise")]
343            Statement::CreateTrigger(stmt) => self.create_trigger(stmt, query_ctx).await,
344            Statement::DropFlow(stmt) => {
345                self.drop_flow(
346                    query_ctx.current_catalog().to_string(),
347                    format_raw_object_name(stmt.flow_name()),
348                    stmt.drop_if_exists(),
349                    query_ctx,
350                )
351                .await
352            }
353            #[cfg(feature = "enterprise")]
354            Statement::DropTrigger(stmt) => {
355                self.drop_trigger(
356                    query_ctx.current_catalog().to_string(),
357                    format_raw_object_name(stmt.trigger_name()),
358                    stmt.drop_if_exists(),
359                    query_ctx,
360                )
361                .await
362            }
363            Statement::CreateView(stmt) => {
364                let _ = self.create_view(stmt, query_ctx).await?;
365                Ok(Output::new_with_affected_rows(0))
366            }
367            Statement::DropView(stmt) => {
368                let (catalog_name, schema_name, view_name) =
369                    table_idents_to_full_name(&stmt.view_name, &query_ctx)
370                        .map_err(BoxedError::new)
371                        .context(ExternalSnafu)?;
372
373                self.drop_view(
374                    catalog_name,
375                    schema_name,
376                    view_name,
377                    stmt.drop_if_exists,
378                    query_ctx,
379                )
380                .await
381            }
382            Statement::AlterTable(alter_table) => self.alter_table(alter_table, query_ctx).await,
383
384            Statement::AlterDatabase(alter_database) => {
385                self.alter_database(alter_database, query_ctx).await
386            }
387
388            #[cfg(feature = "enterprise")]
389            Statement::AlterTrigger(alter_trigger) => {
390                self.alter_trigger(alter_trigger, query_ctx).await
391            }
392
393            Statement::DropTable(stmt) => {
394                let mut table_names = Vec::with_capacity(stmt.table_names().len());
395                for table_name_stmt in stmt.table_names() {
396                    let (catalog, schema, table) =
397                        table_idents_to_full_name(table_name_stmt, &query_ctx)
398                            .map_err(BoxedError::new)
399                            .context(ExternalSnafu)?;
400                    table_names.push(TableName::new(catalog, schema, table));
401                }
402                self.drop_tables(&table_names[..], stmt.drop_if_exists(), query_ctx.clone())
403                    .await
404            }
405            #[cfg(feature = "enterprise")]
406            Statement::UndropTable(stmt) => {
407                let (catalog, schema, table) =
408                    table_idents_to_full_name(stmt.table_name(), &query_ctx)
409                        .map_err(BoxedError::new)
410                        .context(ExternalSnafu)?;
411                self.undrop_table(TableName::new(catalog, schema, table), query_ctx)
412                    .await
413            }
414            Statement::DropDatabase(stmt) => {
415                self.drop_database(
416                    query_ctx.current_catalog().to_string(),
417                    format_raw_object_name(stmt.name()),
418                    stmt.drop_if_exists(),
419                    query_ctx,
420                )
421                .await
422            }
423            Statement::TruncateTable(stmt) => {
424                let ddl_options = ddl::parse_ddl_options(stmt.options())?;
425                let (catalog, schema, table) =
426                    table_idents_to_full_name(stmt.table_name(), &query_ctx)
427                        .map_err(BoxedError::new)
428                        .context(ExternalSnafu)?;
429                let table_name = TableName::new(catalog, schema, table);
430                let time_ranges = self
431                    .convert_truncate_time_ranges(&table_name, stmt.time_ranges(), &query_ctx)
432                    .await?;
433                self.truncate_table_with_options(table_name, time_ranges, ddl_options, query_ctx)
434                    .await
435            }
436            Statement::CreateDatabase(stmt) => {
437                self.create_database(
438                    &format_raw_object_name(&stmt.name),
439                    stmt.if_not_exists,
440                    stmt.options.into_map(),
441                    query_ctx,
442                )
443                .await
444            }
445            Statement::ShowCreateDatabase(show) => {
446                let (catalog, database) =
447                    idents_to_full_database_name(&show.database_name, &query_ctx)
448                        .map_err(BoxedError::new)
449                        .context(ExternalSnafu)?;
450                let table_metadata_manager = self
451                    .catalog_manager
452                    .as_any()
453                    .downcast_ref::<KvBackendCatalogManager>()
454                    .map(|manager| manager.table_metadata_manager_ref().clone())
455                    .context(UpgradeCatalogManagerRefSnafu)?;
456                let opts: HashMap<String, String> = table_metadata_manager
457                    .schema_manager()
458                    .get(SchemaNameKey::new(&catalog, &database))
459                    .await
460                    .context(TableMetadataManagerSnafu)?
461                    .context(SchemaNotFoundSnafu {
462                        schema_info: &database,
463                    })?
464                    .into_inner()
465                    .into();
466
467                self.show_create_database(&database, opts.into()).await
468            }
469            Statement::ShowCreateTable(show) => {
470                let (catalog, schema, table) =
471                    table_idents_to_full_name(&show.table_name, &query_ctx)
472                        .map_err(BoxedError::new)
473                        .context(ExternalSnafu)?;
474
475                let table_ref = self
476                    .catalog_manager
477                    .table(&catalog, &schema, &table, Some(&query_ctx))
478                    .await
479                    .context(CatalogSnafu)?
480                    .context(TableNotFoundSnafu { table_name: &table })?;
481                let table_name = TableName::new(catalog, schema, table);
482
483                match show.variant {
484                    ShowCreateTableVariant::Original => {
485                        self.show_create_table(table_name, table_ref, query_ctx)
486                            .await
487                    }
488                    ShowCreateTableVariant::PostgresForeignTable => {
489                        self.show_create_table_for_pg(table_name, table_ref, query_ctx)
490                            .await
491                    }
492                }
493            }
494            Statement::ShowCreateFlow(show) => self.show_create_flow(show, query_ctx).await,
495            Statement::ShowCreateView(show) => self.show_create_view(show, query_ctx).await,
496            #[cfg(feature = "enterprise")]
497            Statement::ShowCreateTrigger(show) => self.show_create_trigger(show, query_ctx).await,
498            Statement::SetVariables(set_var) => self.set_variables(set_var, query_ctx),
499            Statement::ShowVariables(show_variable) => self.show_variable(show_variable, query_ctx),
500            Statement::Comment(stmt) => self.comment(stmt, query_ctx).await,
501            Statement::ShowColumns(show_columns) => {
502                self.show_columns(show_columns, query_ctx).await
503            }
504            Statement::ShowIndex(show_index) => self.show_index(show_index, query_ctx).await,
505            Statement::ShowRegion(show_region) => self.show_region(show_region, query_ctx).await,
506            Statement::ShowStatus(_) => self.show_status(query_ctx).await,
507            Statement::ShowSearchPath(_) => self.show_search_path(query_ctx).await,
508            Statement::Use(db) => self.use_database(db, query_ctx).await,
509            Statement::Admin(admin) => self.execute_admin_command(admin, query_ctx).await,
510            Statement::Kill(kill) => self.execute_kill(query_ctx, kill).await,
511            Statement::ShowProcesslist(show) => self.show_processlist(show, query_ctx).await,
512        }
513    }
514
515    pub async fn use_database(&self, db: String, query_ctx: QueryContextRef) -> Result<Output> {
516        let catalog = query_ctx.current_catalog();
517        ensure!(
518            self.catalog_manager
519                .schema_exists(catalog, db.as_ref(), Some(&query_ctx))
520                .await
521                .context(CatalogSnafu)?,
522            SchemaNotFoundSnafu { schema_info: &db }
523        );
524
525        query_ctx.set_current_schema(&db);
526
527        Ok(Output::new_with_record_batches(RecordBatches::empty()))
528    }
529
530    fn set_variables(&self, set_var: SetVariables, query_ctx: QueryContextRef) -> Result<Output> {
531        let variable = sql::ast::object_name_to_string_unquoted(&set_var.variable);
532        let var_name = variable.to_uppercase();
533
534        debug!(
535            "Trying to set {}={} for session: {} ",
536            var_name,
537            set_var.value.iter().map(|e| e.to_string()).join(", "),
538            query_ctx.conn_info()
539        );
540
541        if set_query_option(&variable, set_var.value.clone(), query_ctx.clone())? {
542            return Ok(Output::new_with_affected_rows(0));
543        }
544
545        match var_name.as_str() {
546            "READ_PREFERENCE" => set_read_preference(set_var.value, query_ctx)?,
547            "SKIP_WAL" => set_skip_wal(set_var.value, query_ctx)?,
548
549            "@@TIME_ZONE" | "@@SESSION.TIME_ZONE" | "TIMEZONE" | "TIME_ZONE" => {
550                set_timezone(set_var.value, query_ctx)?
551            }
552
553            "BYTEA_OUTPUT" => set_bytea_output(set_var.value, query_ctx)?,
554
555            // Same as "bytea_output", we just ignore it here.
556            // Not harmful since it only relates to how date is viewed in client app's output.
557            // The tracked issue is https://github.com/GreptimeTeam/greptimedb/issues/3442.
558            "DATESTYLE" => set_datestyle(set_var.value, query_ctx)?,
559            "INTERVALSTYLE" => set_intervalstyle(set_var.value, query_ctx)?,
560
561            "CLIENT_ENCODING" => validate_client_encoding(set_var)?,
562            "@@SESSION.MAX_EXECUTION_TIME" | "MAX_EXECUTION_TIME" => match query_ctx.channel() {
563                Channel::Mysql => set_query_timeout(set_var.value, query_ctx)?,
564                Channel::Postgres => {
565                    warn!(
566                        "Unsupported set variable {} for channel {:?}",
567                        var_name,
568                        query_ctx.channel()
569                    );
570                    query_ctx.set_warning(format!("Unsupported set variable {}", var_name))
571                }
572                _ => {
573                    return NotSupportedSnafu {
574                        feat: format!("Unsupported set variable {}", var_name),
575                    }
576                    .fail();
577                }
578            },
579            "STATEMENT_TIMEOUT" => match query_ctx.channel() {
580                Channel::Postgres => set_query_timeout(set_var.value, query_ctx)?,
581                Channel::Mysql => {
582                    warn!(
583                        "Unsupported set variable {} for channel {:?}",
584                        var_name,
585                        query_ctx.channel()
586                    );
587                    query_ctx.set_warning(format!("Unsupported set variable {}", var_name));
588                }
589                _ => {
590                    return NotSupportedSnafu {
591                        feat: format!("Unsupported set variable {}", var_name),
592                    }
593                    .fail();
594                }
595            },
596            "SEARCH_PATH" => {
597                if query_ctx.channel() == Channel::Postgres {
598                    let search_path = set_var.search_path().context(NotSupportedSnafu {
599                        feat: "Unsupported search path in set variable statement",
600                    })?;
601                    query_ctx.set_current_schema(search_path);
602                } else {
603                    return NotSupportedSnafu {
604                        feat: format!("Unsupported set variable {}", var_name),
605                    }
606                    .fail();
607                }
608            }
609            _ => {
610                if query_ctx.channel() == Channel::Postgres || query_ctx.channel() == Channel::Mysql
611                {
612                    // For unknown SET statements, we give a warning with success.
613                    // This prevents the SET call from becoming a blocker of MySQL/Postgres clients'
614                    // connection establishment.
615                    warn!(
616                        "Unsupported set variable {} for channel {:?}",
617                        var_name,
618                        query_ctx.channel()
619                    );
620                    query_ctx.set_warning(format!("Unsupported set variable {}", var_name));
621                } else {
622                    return NotSupportedSnafu {
623                        feat: format!("Unsupported set variable {}", var_name),
624                    }
625                    .fail();
626                }
627            }
628        }
629        Ok(Output::new_with_affected_rows(0))
630    }
631
632    #[tracing::instrument(skip_all)]
633    pub async fn plan(
634        &self,
635        stmt: &QueryStatement,
636        query_ctx: QueryContextRef,
637    ) -> Result<LogicalPlan> {
638        self.query_engine
639            .planner()
640            .plan(stmt, query_ctx)
641            .await
642            .context(PlanStatementSnafu)
643    }
644
645    /// Execute [`LogicalPlan`] directly.
646    #[tracing::instrument(skip_all)]
647    pub async fn exec_plan(&self, plan: LogicalPlan, query_ctx: QueryContextRef) -> Result<Output> {
648        self.query_engine
649            .execute(plan, query_ctx)
650            .await
651            .context(ExecLogicalPlanSnafu)
652    }
653
654    pub fn optimize_logical_plan(&self, plan: LogicalPlan) -> Result<LogicalPlan> {
655        self.query_engine
656            .planner()
657            .optimize(plan)
658            .context(PlanStatementSnafu)
659    }
660
661    #[tracing::instrument(skip_all)]
662    async fn plan_exec(&self, stmt: QueryStatement, query_ctx: QueryContextRef) -> Result<Output> {
663        let plan = self.plan(&stmt, query_ctx.clone()).await?;
664        self.exec_plan(plan, query_ctx).await
665    }
666
667    async fn get_table(&self, table_ref: &TableReference<'_>) -> Result<TableRef> {
668        let TableReference {
669            catalog,
670            schema,
671            table,
672        } = table_ref;
673        self.catalog_manager
674            .table(catalog, schema, table, None)
675            .await
676            .context(CatalogSnafu)?
677            .with_context(|| TableNotFoundSnafu {
678                table_name: table_ref.to_string(),
679            })
680    }
681
682    pub fn procedure_executor(&self) -> &ProcedureExecutorRef {
683        &self.procedure_executor
684    }
685
686    pub fn cache_invalidator(&self) -> &CacheInvalidatorRef {
687        &self.cache_invalidator
688    }
689
690    /// Convert truncate time ranges for the given table from sql values to timestamps
691    ///
692    pub async fn convert_truncate_time_ranges(
693        &self,
694        table_name: &TableName,
695        sql_values_time_range: &[(sqlparser::ast::Value, sqlparser::ast::Value)],
696        query_ctx: &QueryContextRef,
697    ) -> Result<Vec<(Timestamp, Timestamp)>> {
698        if sql_values_time_range.is_empty() {
699            return Ok(vec![]);
700        }
701        let table = self.get_table(&table_name.table_ref()).await?;
702        let info = table.table_info();
703        let time_index_dt = info
704            .meta
705            .schema
706            .timestamp_column()
707            .context(UnexpectedSnafu {
708                violated: "Table must have a timestamp column",
709            })?;
710
711        let time_unit = time_index_dt
712            .data_type
713            .as_timestamp()
714            .with_context(|| UnexpectedSnafu {
715                violated: format!(
716                    "Table {}'s time index column must be a timestamp type, found: {:?}",
717                    table_name, time_index_dt
718                ),
719            })?
720            .unit();
721
722        let start_column = ColumnSchema::new(
723            "range_start",
724            ConcreteDataType::timestamp_datatype(time_unit),
725            false,
726        );
727        let end_column = ColumnSchema::new(
728            "range_end",
729            ConcreteDataType::timestamp_datatype(time_unit),
730            false,
731        );
732        let mut time_ranges = Vec::with_capacity(sql_values_time_range.len());
733        for (start, end) in sql_values_time_range {
734            let start = common_sql::convert::sql_value_to_value(
735                &start_column,
736                start,
737                Some(&query_ctx.timezone()),
738                None,
739                false,
740            )
741            .context(SqlCommonSnafu)
742            .and_then(|v| {
743                if let datatypes::value::Value::Timestamp(t) = v {
744                    Ok(t)
745                } else {
746                    error::InvalidSqlSnafu {
747                        err_msg: format!("Expected a timestamp value, found {v:?}"),
748                    }
749                    .fail()
750                }
751            })?;
752
753            let end = common_sql::convert::sql_value_to_value(
754                &end_column,
755                end,
756                Some(&query_ctx.timezone()),
757                None,
758                false,
759            )
760            .context(SqlCommonSnafu)
761            .and_then(|v| {
762                if let datatypes::value::Value::Timestamp(t) = v {
763                    Ok(t)
764                } else {
765                    error::InvalidSqlSnafu {
766                        err_msg: format!("Expected a timestamp value, found {v:?}"),
767                    }
768                    .fail()
769                }
770            })?;
771            time_ranges.push((start, end));
772        }
773        Ok(time_ranges)
774    }
775
776    /// Returns the inserter for the statement executor.
777    pub(crate) fn inserter(&self) -> &InserterRef {
778        &self.inserter
779    }
780}
781
782fn to_copy_query_request(stmt: CopyQueryToArgument) -> Result<CopyQueryToRequest> {
783    let CopyQueryToArgument {
784        with,
785        connection,
786        location,
787    } = stmt;
788
789    Ok(CopyQueryToRequest {
790        location,
791        with: with.into_map(),
792        connection: connection.into_map(),
793    })
794}
795
796// Verifies time related format is valid
797fn verify_time_related_format(with: &OptionMap) -> Result<()> {
798    let time_format = with.get(common_datasource::file_format::TIME_FORMAT);
799    let date_format = with.get(common_datasource::file_format::DATE_FORMAT);
800    let timestamp_format = with.get(common_datasource::file_format::TIMESTAMP_FORMAT);
801    let file_format = with.get(common_datasource::file_format::FORMAT_TYPE);
802
803    if !matches!(file_format, Some(f) if f.eq_ignore_ascii_case("csv")) {
804        ensure!(
805            time_format.is_none() && date_format.is_none() && timestamp_format.is_none(),
806            error::TimestampFormatNotSupportedSnafu {
807                format: "<unknown>".to_string(),
808                file_format: file_format.unwrap_or_default(),
809            }
810        );
811    }
812
813    for (key, format_opt) in [
814        (common_datasource::file_format::TIME_FORMAT, time_format),
815        (common_datasource::file_format::DATE_FORMAT, date_format),
816        (
817            common_datasource::file_format::TIMESTAMP_FORMAT,
818            timestamp_format,
819        ),
820    ] {
821        if let Some(format) = format_opt {
822            chrono::format::strftime::StrftimeItems::new(format)
823                .parse()
824                .map_err(|_| error::InvalidCopyParameterSnafu { key, value: format }.build())?;
825        }
826    }
827
828    Ok(())
829}
830
831fn to_copy_table_request(stmt: CopyTable, query_ctx: QueryContextRef) -> Result<CopyTableRequest> {
832    let direction = match stmt {
833        CopyTable::To(_) => CopyDirection::Export,
834        CopyTable::From(_) => CopyDirection::Import,
835    };
836
837    let CopyTableArgument {
838        location,
839        connection,
840        with,
841        table_name,
842        limit,
843        ..
844    } = match stmt {
845        CopyTable::To(arg) => arg,
846        CopyTable::From(arg) => arg,
847    };
848    let (catalog_name, schema_name, table_name) =
849        table_idents_to_full_name(&table_name, &query_ctx)
850            .map_err(BoxedError::new)
851            .context(ExternalSnafu)?;
852
853    let timestamp_range = timestamp_range_from_option_map(&with, &query_ctx)?;
854
855    verify_time_related_format(&with)?;
856
857    let pattern = with
858        .get(common_datasource::file_format::FILE_PATTERN)
859        .map(|x| x.to_string());
860
861    Ok(CopyTableRequest {
862        catalog_name,
863        schema_name,
864        table_name,
865        location,
866        with: with.into_map(),
867        connection: connection.into_map(),
868        pattern,
869        direction,
870        timestamp_range,
871        limit,
872    })
873}
874
875/// Converts [CopyDatabaseArgument] to [CopyDatabaseRequest].
876/// This function extracts the necessary info including catalog/database name, time range, etc.
877pub fn to_copy_database_request(
878    arg: CopyDatabaseArgument,
879    query_ctx: &QueryContextRef,
880) -> Result<CopyDatabaseRequest> {
881    let (catalog_name, database_name) = idents_to_full_database_name(&arg.database_name, query_ctx)
882        .map_err(BoxedError::new)
883        .context(ExternalSnafu)?;
884    let time_range = timestamp_range_from_option_map(&arg.with, query_ctx)?;
885
886    Ok(CopyDatabaseRequest {
887        catalog_name,
888        schema_name: database_name,
889        location: arg.location,
890        with: arg.with.into_map(),
891        connection: arg.connection.into_map(),
892        time_range,
893    })
894}
895
896/// Extracts timestamp range from OptionMap with keys `start_time` and `end_time`.
897/// The timestamp ranges should be a valid timestamp string as defined in [Timestamp::from_str].
898/// The timezone used for conversion will respect that inside `query_ctx`.
899fn timestamp_range_from_option_map(
900    options: &OptionMap,
901    query_ctx: &QueryContextRef,
902) -> Result<Option<TimestampRange>> {
903    let start_timestamp = extract_timestamp(options, COPY_DATABASE_TIME_START_KEY, query_ctx)?;
904    let end_timestamp = extract_timestamp(options, COPY_DATABASE_TIME_END_KEY, query_ctx)?;
905    let time_range = match (start_timestamp, end_timestamp) {
906        (Some(start), Some(end)) => Some(TimestampRange::new(start, end).with_context(|| {
907            error::InvalidTimestampRangeSnafu {
908                start: start.to_iso8601_string(),
909                end: end.to_iso8601_string(),
910            }
911        })?),
912        (Some(start), None) => Some(TimestampRange::from_start(start)),
913        (None, Some(end)) => Some(TimestampRange::until_end(end, false)), // exclusive end
914        (None, None) => None,
915    };
916    Ok(time_range)
917}
918
919/// Extracts timestamp from a [HashMap<String, String>] with given key.
920fn extract_timestamp(
921    map: &OptionMap,
922    key: &str,
923    query_ctx: &QueryContextRef,
924) -> Result<Option<Timestamp>> {
925    map.get(key)
926        .map(|v| {
927            Timestamp::from_str(v, Some(&query_ctx.timezone()))
928                .map_err(|_| error::InvalidCopyParameterSnafu { key, value: v }.build())
929        })
930        .transpose()
931}
932
933fn idents_to_full_database_name(
934    obj_name: &ObjectName,
935    query_ctx: &QueryContextRef,
936) -> Result<(String, String)> {
937    match &obj_name.0[..] {
938        [database] => Ok((
939            query_ctx.current_catalog().to_owned(),
940            database.to_string_unquoted(),
941        )),
942        [catalog, database] => Ok((catalog.to_string_unquoted(), database.to_string_unquoted())),
943        _ => InvalidSqlSnafu {
944            err_msg: format!(
945                "expect database name to be <catalog>.<database>, <database>, found: {obj_name}",
946            ),
947        }
948        .fail(),
949    }
950}
951
952/// The [`Inserter`] implementation for the statement executor.
953pub struct InserterImpl {
954    statement_executor: StatementExecutorRef,
955    options: Option<InsertOptions>,
956}
957
958impl InserterImpl {
959    pub fn new(statement_executor: StatementExecutorRef, options: Option<InsertOptions>) -> Self {
960        Self {
961            statement_executor,
962            options,
963        }
964    }
965}
966
967#[async_trait::async_trait]
968impl Inserter for InserterImpl {
969    async fn insert_rows(
970        &self,
971        context: &client::inserter::Context<'_>,
972        requests: RowInsertRequests,
973    ) -> ClientResult<()> {
974        let mut ctx_builder = QueryContextBuilder::default()
975            .current_catalog(context.catalog.to_string())
976            .current_schema(context.schema.to_string());
977        if let Some(options) = self.options.as_ref() {
978            ctx_builder = ctx_builder
979                .set_extension(
980                    TTL_KEY.to_string(),
981                    format_duration(options.ttl).to_string(),
982                )
983                .set_extension(APPEND_MODE_KEY.to_string(), options.append_mode.to_string());
984        }
985        let query_ctx = ctx_builder.build().into();
986
987        self.statement_executor
988            .inserter()
989            .handle_row_inserts(
990                requests,
991                query_ctx,
992                self.statement_executor.as_ref(),
993                false,
994                false,
995            )
996            .await
997            .map_err(BoxedError::new)
998            .context(ClientExternalSnafu)
999            .map(|_| ())
1000    }
1001
1002    fn set_options(&mut self, options: &InsertOptions) {
1003        self.options = Some(*options);
1004    }
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009    use std::assert_matches;
1010    use std::collections::HashMap;
1011
1012    use common_time::range::TimestampRange;
1013    use common_time::{Timestamp, Timezone};
1014    use session::context::QueryContextBuilder;
1015    use sql::statements::OptionMap;
1016
1017    use crate::error;
1018    use crate::statement::copy_database::{
1019        COPY_DATABASE_TIME_END_KEY, COPY_DATABASE_TIME_START_KEY,
1020    };
1021    use crate::statement::{timestamp_range_from_option_map, verify_time_related_format};
1022
1023    fn check_timestamp_range((start, end): (&str, &str)) -> error::Result<Option<TimestampRange>> {
1024        let query_ctx = QueryContextBuilder::default()
1025            .timezone(Timezone::from_tz_string("Asia/Shanghai").unwrap())
1026            .build()
1027            .into();
1028        let map = OptionMap::from(
1029            [
1030                (COPY_DATABASE_TIME_START_KEY.to_string(), start.to_string()),
1031                (COPY_DATABASE_TIME_END_KEY.to_string(), end.to_string()),
1032            ]
1033            .into_iter()
1034            .collect::<HashMap<_, _>>(),
1035        );
1036        timestamp_range_from_option_map(&map, &query_ctx)
1037    }
1038
1039    #[test]
1040    fn test_timestamp_range_from_option_map() {
1041        assert_eq!(
1042            Some(
1043                TimestampRange::new(
1044                    Timestamp::new_second(1649635200),
1045                    Timestamp::new_second(1649664000),
1046                )
1047                .unwrap(),
1048            ),
1049            check_timestamp_range(("2022-04-11 08:00:00", "2022-04-11 16:00:00"),).unwrap()
1050        );
1051
1052        assert_matches!(
1053            check_timestamp_range(("2022-04-11 08:00:00", "2022-04-11 07:00:00")).unwrap_err(),
1054            error::Error::InvalidTimestampRange { .. }
1055        );
1056    }
1057
1058    #[test]
1059    fn test_verify_timestamp_format() {
1060        let map = OptionMap::from(
1061            [
1062                (
1063                    common_datasource::file_format::TIMESTAMP_FORMAT.to_string(),
1064                    "%Y-%m-%d %H:%M:%S".to_string(),
1065                ),
1066                (
1067                    common_datasource::file_format::FORMAT_TYPE.to_string(),
1068                    "csv".to_string(),
1069                ),
1070            ]
1071            .into_iter()
1072            .collect::<HashMap<_, _>>(),
1073        );
1074        assert!(verify_time_related_format(&map).is_ok());
1075
1076        let map = OptionMap::from(
1077            [
1078                (
1079                    common_datasource::file_format::TIMESTAMP_FORMAT.to_string(),
1080                    "%Y-%m-%d %H:%M:%S".to_string(),
1081                ),
1082                (
1083                    common_datasource::file_format::FORMAT_TYPE.to_string(),
1084                    "json".to_string(),
1085                ),
1086            ]
1087            .into_iter()
1088            .collect::<HashMap<_, _>>(),
1089        );
1090
1091        assert_matches!(
1092            verify_time_related_format(&map).unwrap_err(),
1093            error::Error::TimestampFormatNotSupported { .. }
1094        );
1095        let map = OptionMap::from(
1096            [
1097                (
1098                    common_datasource::file_format::TIMESTAMP_FORMAT.to_string(),
1099                    "%111112".to_string(),
1100                ),
1101                (
1102                    common_datasource::file_format::FORMAT_TYPE.to_string(),
1103                    "csv".to_string(),
1104                ),
1105            ]
1106            .into_iter()
1107            .collect::<HashMap<_, _>>(),
1108        );
1109
1110        assert_matches!(
1111            verify_time_related_format(&map).unwrap_err(),
1112            error::Error::InvalidCopyParameter { .. }
1113        );
1114    }
1115}