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