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