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