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