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