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