Skip to main content

operator/
statement.rs

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