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