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