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