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