1#[cfg(feature = "enterprise")]
16pub mod trigger;
17
18use std::collections::{HashMap, HashSet};
19
20use api::helper::ColumnDataTypeWrapper;
21use api::v1::alter_database_expr::Kind as AlterDatabaseKind;
22use api::v1::alter_table_expr::Kind as AlterTableKind;
23use api::v1::column_def::{options_from_column_schema, try_as_column_schema};
24use api::v1::{
25 AddColumn, AddColumns, AlterDatabaseExpr, AlterTableExpr, Analyzer, ColumnDataType,
26 ColumnDataTypeExtension, CreateFlowExpr, CreateTableExpr, CreateViewExpr, DropColumn,
27 DropColumns, DropDefaults, ExpireAfter, FulltextBackend as PbFulltextBackend, ModifyColumnType,
28 ModifyColumnTypes, RenameTable, SemanticType, SetDatabaseOptions, SetDefaults, SetFulltext,
29 SetIndex, SetIndexes, SetInverted, SetSkipping, SetTableOptions,
30 SkippingIndexType as PbSkippingIndexType, TableName, UnsetDatabaseOptions, UnsetFulltext,
31 UnsetIndex, UnsetIndexes, UnsetInverted, UnsetSkipping, UnsetTableOptions, set_index,
32 unset_index,
33};
34use common_datasource::object_store::LocalFileAccess;
35use common_error::ext::BoxedError;
36use common_grpc_expr::util::ColumnExpr;
37use common_time::Timezone;
38use datafusion::sql::planner::object_name_to_table_reference;
39use datatypes::prelude::ConcreteDataType;
40use datatypes::schema::{
41 COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_BACKEND,
42 COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE,
43 COLUMN_FULLTEXT_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE,
44 COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE, COMMENT_KEY,
45 ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextBackend, Schema,
46 SkippingIndexType,
47};
48use file_engine::FileOptions;
49use query::sql::{
50 check_file_to_table_schema_compatibility, file_column_schemas_to_table,
51 infer_file_table_schema, prepare_file_table_files,
52};
53use session::context::QueryContextRef;
54use session::table_name::table_idents_to_full_name;
55use snafu::{OptionExt, ResultExt, ensure};
56use sql::ast::{
57 ColumnDef, ColumnOption, ColumnOptionDef, Expr, Ident, ObjectName, ObjectNamePartExt,
58};
59use sql::dialect::GreptimeDbDialect;
60use sql::parser::ParserContext;
61use sql::statements::alter::{
62 AlterDatabase, AlterDatabaseOperation, AlterTable, AlterTableOperation,
63};
64use sql::statements::create::{
65 Column as SqlColumn, ColumnExtensions, CreateExternalTable, CreateFlow, CreateTable,
66 CreateView, TableConstraint,
67};
68use sql::statements::{
69 OptionMap, column_to_schema, concrete_data_type_to_sql_data_type,
70 sql_column_def_to_grpc_column_def, sql_data_type_to_concrete_data_type, value_to_sql_value,
71};
72use sql::util::extract_tables_from_query;
73use store_api::mito_engine_options::{COMPACTION_OVERRIDE, COMPACTION_TYPE};
74use table::requests::{FILE_TABLE_META_KEY, TableOptions};
75use table::table_reference::TableReference;
76#[cfg(feature = "enterprise")]
77pub use trigger::to_create_trigger_task_expr;
78
79use crate::error::{
80 BuildCreateExprOnInsertionSnafu, ColumnDataTypeSnafu, ConvertColumnDefaultConstraintSnafu,
81 ConvertIdentifierSnafu, EncodeJsonSnafu, ExternalSnafu, FindNewColumnsOnInsertionSnafu,
82 IllegalPrimaryKeysDefSnafu, InferFileTableSchemaSnafu, InvalidColumnDefSnafu,
83 InvalidFlowNameSnafu, InvalidSqlSnafu, NotSupportedSnafu, ParseSqlSnafu, ParseSqlValueSnafu,
84 PrepareFileTableSnafu, Result, SchemaIncompatibleSnafu, UnrecognizedTableOptionSnafu,
85};
86
87pub fn create_table_expr_by_column_schemas(
88 table_name: &TableReference<'_>,
89 column_schemas: &[api::v1::ColumnSchema],
90 engine: &str,
91 desc: Option<&str>,
92) -> Result<CreateTableExpr> {
93 let column_exprs = ColumnExpr::from_column_schemas(column_schemas);
94 let expr = common_grpc_expr::util::build_create_table_expr(
95 None,
96 table_name,
97 column_exprs,
98 engine,
99 desc.unwrap_or("Created on insertion"),
100 )
101 .context(BuildCreateExprOnInsertionSnafu)?;
102
103 validate_create_expr(&expr)?;
104 Ok(expr)
105}
106
107pub fn extract_add_columns_expr(
108 schema: &Schema,
109 column_exprs: Vec<ColumnExpr>,
110) -> Result<Option<AddColumns>> {
111 let add_columns = common_grpc_expr::util::extract_new_columns(schema, column_exprs)
112 .context(FindNewColumnsOnInsertionSnafu)?;
113 if let Some(add_columns) = &add_columns {
114 validate_add_columns_expr(add_columns)?;
115 }
116 Ok(add_columns)
117}
118
119pub(crate) async fn create_external_expr(
143 create: CreateExternalTable,
144 query_ctx: &QueryContextRef,
145 local_file_access: &LocalFileAccess,
146) -> Result<CreateTableExpr> {
147 let (catalog_name, schema_name, table_name) =
148 table_idents_to_full_name(&create.name, query_ctx)
149 .map_err(BoxedError::new)
150 .context(ExternalSnafu)?;
151
152 let mut table_options = create.options.into_map();
153
154 let (object_store, files) = prepare_file_table_files(&table_options, local_file_access)
155 .await
156 .context(PrepareFileTableSnafu)?;
157
158 let file_column_schemas = infer_file_table_schema(&object_store, &files, &table_options)
159 .await
160 .context(InferFileTableSchemaSnafu)?
161 .column_schemas()
162 .to_vec();
163
164 let (time_index, primary_keys, table_column_schemas) = if !create.columns.is_empty() {
165 let time_index = find_time_index(&create.constraints)?;
167 let primary_keys = find_primary_keys(&create.columns, &create.constraints)?;
168 let column_schemas =
169 columns_to_column_schemas(&create.columns, &time_index, Some(&query_ctx.timezone()))?;
170 (time_index, primary_keys, column_schemas)
171 } else {
172 let (column_schemas, time_index) = file_column_schemas_to_table(&file_column_schemas);
174 let primary_keys = vec![];
175 (time_index, primary_keys, column_schemas)
176 };
177
178 check_file_to_table_schema_compatibility(&file_column_schemas, &table_column_schemas)
179 .context(SchemaIncompatibleSnafu)?;
180
181 let meta = FileOptions {
182 files,
183 file_column_schemas,
184 };
185 table_options.insert(
186 FILE_TABLE_META_KEY.to_string(),
187 serde_json::to_string(&meta).context(EncodeJsonSnafu)?,
188 );
189
190 let column_defs = column_schemas_to_defs(table_column_schemas, &primary_keys)?;
191 let expr = CreateTableExpr {
192 catalog_name,
193 schema_name,
194 table_name,
195 desc: String::default(),
196 column_defs,
197 time_index,
198 primary_keys,
199 create_if_not_exists: create.if_not_exists,
200 table_options,
201 table_id: None,
202 engine: create.engine.clone(),
203 };
204
205 Ok(expr)
206}
207
208pub fn create_to_expr(
210 create: &CreateTable,
211 query_ctx: &QueryContextRef,
212) -> Result<CreateTableExpr> {
213 let (catalog_name, schema_name, table_name) =
214 table_idents_to_full_name(&create.name, query_ctx)
215 .map_err(BoxedError::new)
216 .context(ExternalSnafu)?;
217
218 let time_index = find_time_index(&create.constraints)?;
219 let mut table_options = HashMap::from(
220 &TableOptions::try_from_iter(create.options.to_str_map())
221 .context(UnrecognizedTableOptionSnafu)?,
222 );
223
224 if table_options.contains_key(COMPACTION_TYPE) {
225 table_options.insert(COMPACTION_OVERRIDE.to_string(), "true".to_string());
226 }
227
228 let primary_keys = find_primary_keys(&create.columns, &create.constraints)?;
229
230 let expr = CreateTableExpr {
231 catalog_name,
232 schema_name,
233 table_name,
234 desc: String::default(),
235 column_defs: columns_to_expr(
236 &create.columns,
237 &time_index,
238 &primary_keys,
239 Some(&query_ctx.timezone()),
240 )?,
241 time_index,
242 primary_keys,
243 create_if_not_exists: create.if_not_exists,
244 table_options,
245 table_id: None,
246 engine: create.engine.clone(),
247 };
248
249 validate_create_expr(&expr)?;
250 Ok(expr)
251}
252
253pub fn expr_to_create(expr: &CreateTableExpr, quote_style: Option<char>) -> Result<CreateTable> {
261 let quote_style = quote_style.unwrap_or('`');
262
263 let table_name = ObjectName(vec![sql::ast::ObjectNamePart::Identifier(
265 sql::ast::Ident::with_quote(quote_style, &expr.table_name),
266 )]);
267
268 let mut columns = Vec::with_capacity(expr.column_defs.len());
270 for column_def in &expr.column_defs {
271 let column_schema = try_as_column_schema(column_def).context(InvalidColumnDefSnafu {
272 column: &column_def.name,
273 })?;
274
275 let mut options = Vec::new();
276
277 if column_def.is_nullable {
279 options.push(ColumnOptionDef {
280 name: None,
281 option: ColumnOption::Null,
282 });
283 } else {
284 options.push(ColumnOptionDef {
285 name: None,
286 option: ColumnOption::NotNull,
287 });
288 }
289
290 if let Some(default_constraint) = column_schema.default_constraint() {
292 let expr = match default_constraint {
293 ColumnDefaultConstraint::Value(v) => {
294 Expr::Value(value_to_sql_value(v).context(ParseSqlValueSnafu)?.into())
295 }
296 ColumnDefaultConstraint::Function(func_expr) => {
297 ParserContext::parse_function(func_expr, &GreptimeDbDialect {})
298 .context(ParseSqlSnafu)?
299 }
300 };
301 options.push(ColumnOptionDef {
302 name: None,
303 option: ColumnOption::Default(expr),
304 });
305 }
306
307 if !column_def.comment.is_empty() {
309 options.push(ColumnOptionDef {
310 name: None,
311 option: ColumnOption::Comment(column_def.comment.clone()),
312 });
313 }
314
315 let mut extensions = ColumnExtensions::default();
320
321 if let Ok(Some(opt)) = column_schema.fulltext_options()
323 && opt.enable
324 {
325 let mut map = HashMap::from([
326 (
327 COLUMN_FULLTEXT_OPT_KEY_ANALYZER.to_string(),
328 opt.analyzer.to_string(),
329 ),
330 (
331 COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE.to_string(),
332 opt.case_sensitive.to_string(),
333 ),
334 (
335 COLUMN_FULLTEXT_OPT_KEY_BACKEND.to_string(),
336 opt.backend.to_string(),
337 ),
338 ]);
339 if opt.backend == FulltextBackend::Bloom {
340 map.insert(
341 COLUMN_FULLTEXT_OPT_KEY_GRANULARITY.to_string(),
342 opt.granularity.to_string(),
343 );
344 map.insert(
345 COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE.to_string(),
346 opt.false_positive_rate().to_string(),
347 );
348 }
349 extensions.fulltext_index_options = Some(map.into());
350 }
351
352 if let Ok(Some(opt)) = column_schema.skipping_index_options() {
354 let map = HashMap::from([
355 (
356 COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY.to_string(),
357 opt.granularity.to_string(),
358 ),
359 (
360 COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE.to_string(),
361 opt.false_positive_rate().to_string(),
362 ),
363 (
364 COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE.to_string(),
365 opt.index_type.to_string(),
366 ),
367 ]);
368 extensions.skipping_index_options = Some(map.into());
369 }
370
371 if column_schema.is_inverted_indexed() {
373 extensions.inverted_index_options = Some(HashMap::new().into());
374 }
375
376 let sql_column = SqlColumn {
377 column_def: ColumnDef {
378 name: Ident::with_quote(quote_style, &column_def.name),
379 data_type: concrete_data_type_to_sql_data_type(&column_schema.data_type)
380 .context(ParseSqlSnafu)?,
381 options,
382 },
383 extensions,
384 };
385
386 columns.push(sql_column);
387 }
388
389 let mut constraints = Vec::new();
391
392 constraints.push(TableConstraint::TimeIndex {
394 column: Ident::with_quote(quote_style, &expr.time_index),
395 });
396
397 if !expr.primary_keys.is_empty() {
399 let primary_key_columns: Vec<Ident> = expr
400 .primary_keys
401 .iter()
402 .map(|pk| Ident::with_quote(quote_style, pk))
403 .collect();
404
405 constraints.push(TableConstraint::PrimaryKey {
406 columns: primary_key_columns,
407 });
408 }
409
410 let mut options = OptionMap::default();
412 for (key, value) in &expr.table_options {
413 options.insert(key.clone(), value.clone());
414 }
415
416 Ok(CreateTable {
417 if_not_exists: expr.create_if_not_exists,
418 table_id: expr.table_id.as_ref().map(|tid| tid.id).unwrap_or(0),
419 name: table_name,
420 columns,
421 engine: expr.engine.clone(),
422 constraints,
423 options,
424 partitions: None,
425 })
426}
427
428pub fn validate_create_expr(create: &CreateTableExpr) -> Result<()> {
430 let mut column_to_indices = HashMap::with_capacity(create.column_defs.len());
432 for (idx, column) in create.column_defs.iter().enumerate() {
433 if let Some(indices) = column_to_indices.get(&column.name) {
434 return InvalidSqlSnafu {
435 err_msg: format!(
436 "column name `{}` is duplicated at index {} and {}",
437 column.name, indices, idx
438 ),
439 }
440 .fail();
441 }
442 column_to_indices.insert(&column.name, idx);
443 }
444
445 let time_index_idx =
447 column_to_indices
448 .get(&create.time_index)
449 .with_context(|| InvalidSqlSnafu {
450 err_msg: format!(
451 "column name `{}` is not found in column list",
452 create.time_index
453 ),
454 })?;
455
456 let time_index_column = &create.column_defs[*time_index_idx];
458 let data_type = ConcreteDataType::from(
459 ColumnDataTypeWrapper::try_new(
460 time_index_column.data_type,
461 time_index_column.datatype_extension.clone(),
462 )
463 .context(ColumnDataTypeSnafu)?,
464 );
465 ensure!(
466 data_type.is_timestamp(),
467 InvalidSqlSnafu {
468 err_msg: format!(
469 "column `{}` is not a timestamp type, it can't be used as time index",
470 create.time_index
471 ),
472 }
473 );
474
475 for pk in &create.primary_keys {
477 let _ = column_to_indices
478 .get(&pk)
479 .with_context(|| InvalidSqlSnafu {
480 err_msg: format!("column name `{}` is not found in column list", pk),
481 })?;
482 }
483
484 let mut pk_set = HashSet::new();
486 for pk in &create.primary_keys {
487 if !pk_set.insert(pk) {
488 return InvalidSqlSnafu {
489 err_msg: format!("column name `{}` is duplicated in primary keys", pk),
490 }
491 .fail();
492 }
493 }
494
495 if pk_set.contains(&create.time_index) {
497 return InvalidSqlSnafu {
498 err_msg: format!(
499 "column name `{}` is both primary key and time index",
500 create.time_index
501 ),
502 }
503 .fail();
504 }
505
506 for column in &create.column_defs {
507 if is_interval_type(&column.data_type()) {
509 return InvalidSqlSnafu {
510 err_msg: format!(
511 "column name `{}` is interval type, which is not supported",
512 column.name
513 ),
514 }
515 .fail();
516 }
517 if is_date_time_type(&column.data_type()) {
519 return InvalidSqlSnafu {
520 err_msg: format!(
521 "column name `{}` is datetime type, which is not supported, please use `timestamp` type instead",
522 column.name
523 ),
524 }
525 .fail();
526 }
527 }
528 Ok(())
529}
530
531fn validate_add_columns_expr(add_columns: &AddColumns) -> Result<()> {
532 for add_column in &add_columns.add_columns {
533 let Some(column_def) = &add_column.column_def else {
534 continue;
535 };
536 if is_date_time_type(&column_def.data_type()) {
537 return InvalidSqlSnafu {
538 err_msg: format!("column name `{}` is datetime type, which is not supported, please use `timestamp` type instead", column_def.name),
539 }
540 .fail();
541 }
542 if is_interval_type(&column_def.data_type()) {
543 return InvalidSqlSnafu {
544 err_msg: format!(
545 "column name `{}` is interval type, which is not supported",
546 column_def.name
547 ),
548 }
549 .fail();
550 }
551 }
552 Ok(())
553}
554
555fn is_date_time_type(data_type: &ColumnDataType) -> bool {
556 matches!(data_type, ColumnDataType::Datetime)
557}
558
559fn is_interval_type(data_type: &ColumnDataType) -> bool {
560 matches!(
561 data_type,
562 ColumnDataType::IntervalYearMonth
563 | ColumnDataType::IntervalDayTime
564 | ColumnDataType::IntervalMonthDayNano
565 )
566}
567
568fn find_primary_keys(
569 columns: &[SqlColumn],
570 constraints: &[TableConstraint],
571) -> Result<Vec<String>> {
572 let columns_pk = columns
573 .iter()
574 .filter_map(|x| {
575 if x.options()
576 .iter()
577 .any(|o| matches!(o.option, ColumnOption::PrimaryKey(_)))
578 {
579 Some(x.name().value.clone())
580 } else {
581 None
582 }
583 })
584 .collect::<Vec<String>>();
585
586 ensure!(
587 columns_pk.len() <= 1,
588 IllegalPrimaryKeysDefSnafu {
589 msg: "not allowed to inline multiple primary keys in columns options"
590 }
591 );
592
593 let constraints_pk = constraints
594 .iter()
595 .filter_map(|constraint| match constraint {
596 TableConstraint::PrimaryKey { columns, .. } => {
597 Some(columns.iter().map(|ident| ident.value.clone()))
598 }
599 _ => None,
600 })
601 .flatten()
602 .collect::<Vec<String>>();
603
604 ensure!(
605 columns_pk.is_empty() || constraints_pk.is_empty(),
606 IllegalPrimaryKeysDefSnafu {
607 msg: "found definitions of primary keys in multiple places"
608 }
609 );
610
611 let mut primary_keys = Vec::with_capacity(columns_pk.len() + constraints_pk.len());
612 primary_keys.extend(columns_pk);
613 primary_keys.extend(constraints_pk);
614 Ok(primary_keys)
615}
616
617pub fn find_time_index(constraints: &[TableConstraint]) -> Result<String> {
618 let time_index = constraints
619 .iter()
620 .filter_map(|constraint| match constraint {
621 TableConstraint::TimeIndex { column, .. } => Some(&column.value),
622 _ => None,
623 })
624 .collect::<Vec<&String>>();
625 ensure!(
626 time_index.len() == 1,
627 InvalidSqlSnafu {
628 err_msg: "must have one and only one TimeIndex columns",
629 }
630 );
631 Ok(time_index[0].clone())
632}
633
634fn columns_to_expr(
635 column_defs: &[SqlColumn],
636 time_index: &str,
637 primary_keys: &[String],
638 timezone: Option<&Timezone>,
639) -> Result<Vec<api::v1::ColumnDef>> {
640 let column_schemas = columns_to_column_schemas(column_defs, time_index, timezone)?;
641 column_schemas_to_defs(column_schemas, primary_keys)
642}
643
644fn columns_to_column_schemas(
645 columns: &[SqlColumn],
646 time_index: &str,
647 timezone: Option<&Timezone>,
648) -> Result<Vec<ColumnSchema>> {
649 columns
650 .iter()
651 .map(|c| column_to_schema(c, time_index, timezone).context(ParseSqlSnafu))
652 .collect::<Result<Vec<ColumnSchema>>>()
653}
654
655pub fn column_schemas_to_defs(
657 column_schemas: Vec<ColumnSchema>,
658 primary_keys: &[String],
659) -> Result<Vec<api::v1::ColumnDef>> {
660 let column_datatypes: Vec<(ColumnDataType, Option<ColumnDataTypeExtension>)> = column_schemas
661 .iter()
662 .map(|c| {
663 ColumnDataTypeWrapper::try_from(c.data_type.clone())
664 .map(|w| w.to_parts())
665 .context(ColumnDataTypeSnafu)
666 })
667 .collect::<Result<Vec<_>>>()?;
668
669 column_schemas
670 .iter()
671 .zip(column_datatypes)
672 .map(|(schema, datatype)| {
673 let semantic_type = if schema.is_time_index() {
674 SemanticType::Timestamp
675 } else if primary_keys.contains(&schema.name) {
676 SemanticType::Tag
677 } else {
678 SemanticType::Field
679 } as i32;
680 let comment = schema
681 .metadata()
682 .get(COMMENT_KEY)
683 .cloned()
684 .unwrap_or_default();
685
686 Ok(api::v1::ColumnDef {
687 name: schema.name.clone(),
688 data_type: datatype.0 as i32,
689 is_nullable: schema.is_nullable(),
690 default_constraint: match schema.default_constraint() {
691 None => vec![],
692 Some(v) => {
693 v.clone()
694 .try_into()
695 .context(ConvertColumnDefaultConstraintSnafu {
696 column_name: &schema.name,
697 })?
698 }
699 },
700 semantic_type,
701 comment,
702 datatype_extension: datatype.1,
703 options: options_from_column_schema(schema),
704 })
705 })
706 .collect()
707}
708
709#[derive(Debug, Clone, PartialEq, Eq)]
710pub struct RepartitionRequest {
711 pub catalog_name: String,
712 pub schema_name: String,
713 pub table_name: String,
714 pub source: RepartitionSource,
715 pub into_exprs: Vec<Expr>,
716 pub options: OptionMap,
717}
718
719#[derive(Debug, Clone, PartialEq, Eq)]
720pub enum RepartitionSource {
721 Partitions {
722 from_exprs: Vec<Expr>,
723 target_partition_columns: Option<Vec<String>>,
724 },
725 Unpartitioned {
726 partition_columns: Vec<String>,
727 },
728}
729
730pub(crate) fn to_repartition_request(
731 alter_table: AlterTable,
732 query_ctx: &QueryContextRef,
733) -> Result<RepartitionRequest> {
734 let AlterTable {
735 table_name,
736 alter_operation,
737 options,
738 } = alter_table;
739
740 let (catalog_name, schema_name, table_name) = table_idents_to_full_name(&table_name, query_ctx)
741 .map_err(BoxedError::new)
742 .context(ExternalSnafu)?;
743
744 let (source, into_exprs) = match alter_operation {
745 AlterTableOperation::Repartition { operation } => (
746 RepartitionSource::Partitions {
747 from_exprs: operation.from_exprs,
748 target_partition_columns: operation.partition_columns.map(|columns| {
749 columns
750 .into_iter()
751 .map(|ident| ident.value)
752 .collect::<Vec<_>>()
753 }),
754 },
755 operation.into_exprs,
756 ),
757 AlterTableOperation::Partition { partitions } => (
758 RepartitionSource::Unpartitioned {
759 partition_columns: partitions
760 .column_list
761 .into_iter()
762 .map(|ident| ident.value)
763 .collect(),
764 },
765 partitions.exprs,
766 ),
767 _ => {
768 return InvalidSqlSnafu {
769 err_msg: "expected REPARTITION or PARTITION operation",
770 }
771 .fail();
772 }
773 };
774
775 Ok(RepartitionRequest {
776 catalog_name,
777 schema_name,
778 table_name,
779 source,
780 into_exprs,
781 options,
782 })
783}
784
785pub(crate) fn to_alter_table_expr(
787 alter_table: AlterTable,
788 query_ctx: &QueryContextRef,
789) -> Result<AlterTableExpr> {
790 let (catalog_name, schema_name, table_name) =
791 table_idents_to_full_name(alter_table.table_name(), query_ctx)
792 .map_err(BoxedError::new)
793 .context(ExternalSnafu)?;
794
795 let kind = match alter_table.alter_operation {
796 AlterTableOperation::AddConstraint(_) => {
797 return NotSupportedSnafu {
798 feat: "ADD CONSTRAINT",
799 }
800 .fail();
801 }
802 AlterTableOperation::AddColumns { add_columns } => AlterTableKind::AddColumns(AddColumns {
803 add_columns: add_columns
804 .into_iter()
805 .map(|add_column| {
806 let column_def = sql_column_def_to_grpc_column_def(
807 &add_column.column_def,
808 Some(&query_ctx.timezone()),
809 )
810 .map_err(BoxedError::new)
811 .context(ExternalSnafu)?;
812 if is_interval_type(&column_def.data_type()) {
813 return NotSupportedSnafu {
814 feat: "Add column with interval type",
815 }
816 .fail();
817 }
818 Ok(AddColumn {
819 column_def: Some(column_def),
820 location: add_column.location.as_ref().map(From::from),
821 add_if_not_exists: add_column.add_if_not_exists,
822 })
823 })
824 .collect::<Result<Vec<AddColumn>>>()?,
825 }),
826 AlterTableOperation::ModifyColumnType {
827 column_name,
828 target_type,
829 ..
830 } => {
831 let target_type =
832 sql_data_type_to_concrete_data_type(&target_type).context(ParseSqlSnafu)?;
833 if target_type.is_json2() {
834 return NotSupportedSnafu {
835 feat: "ALTER TABLE MODIFY COLUMN to JSON2 type",
836 }
837 .fail();
838 }
839 let (target_type, target_type_extension) = ColumnDataTypeWrapper::try_from(target_type)
840 .map(|w| w.to_parts())
841 .context(ColumnDataTypeSnafu)?;
842 if is_interval_type(&target_type) {
843 return NotSupportedSnafu {
844 feat: "Modify column type to interval type",
845 }
846 .fail();
847 }
848 AlterTableKind::ModifyColumnTypes(ModifyColumnTypes {
849 modify_column_types: vec![ModifyColumnType {
850 column_name: column_name.value,
851 target_type: target_type as i32,
852 target_type_extension,
853 }],
854 })
855 }
856 AlterTableOperation::DropColumn { name } => AlterTableKind::DropColumns(DropColumns {
857 drop_columns: vec![DropColumn {
858 name: name.value.clone(),
859 }],
860 }),
861 AlterTableOperation::RenameTable { new_table_name } => {
862 AlterTableKind::RenameTable(RenameTable {
863 new_table_name: new_table_name.clone(),
864 })
865 }
866 AlterTableOperation::SetTableOptions { options } => {
867 AlterTableKind::SetTableOptions(SetTableOptions {
868 table_options: options.into_iter().map(Into::into).collect(),
869 })
870 }
871 AlterTableOperation::UnsetTableOptions { keys } => {
872 AlterTableKind::UnsetTableOptions(UnsetTableOptions { keys })
873 }
874 AlterTableOperation::Repartition { .. } => {
875 return NotSupportedSnafu {
876 feat: "ALTER TABLE ... REPARTITION",
877 }
878 .fail();
879 }
880 AlterTableOperation::Partition { .. } => {
881 return NotSupportedSnafu {
882 feat: "ALTER TABLE ... PARTITION ON COLUMNS",
883 }
884 .fail();
885 }
886 AlterTableOperation::SetIndex { options } => {
887 let option = match options {
888 sql::statements::alter::SetIndexOperation::Fulltext {
889 column_name,
890 options,
891 } => SetIndex {
892 options: Some(set_index::Options::Fulltext(SetFulltext {
893 column_name: column_name.value,
894 enable: options.enable,
895 analyzer: match options.analyzer {
896 FulltextAnalyzer::English => Analyzer::English.into(),
897 FulltextAnalyzer::Chinese => Analyzer::Chinese.into(),
898 },
899 case_sensitive: options.case_sensitive,
900 backend: match options.backend {
901 FulltextBackend::Bloom => PbFulltextBackend::Bloom.into(),
902 FulltextBackend::Tantivy => PbFulltextBackend::Tantivy.into(),
903 },
904 granularity: options.granularity as u64,
905 false_positive_rate: options.false_positive_rate(),
906 })),
907 },
908 sql::statements::alter::SetIndexOperation::Inverted { column_name } => SetIndex {
909 options: Some(set_index::Options::Inverted(SetInverted {
910 column_name: column_name.value,
911 })),
912 },
913 sql::statements::alter::SetIndexOperation::Skipping {
914 column_name,
915 options,
916 } => SetIndex {
917 options: Some(set_index::Options::Skipping(SetSkipping {
918 column_name: column_name.value,
919 enable: true,
920 granularity: options.granularity as u64,
921 false_positive_rate: options.false_positive_rate(),
922 skipping_index_type: match options.index_type {
923 SkippingIndexType::BloomFilter => {
924 PbSkippingIndexType::BloomFilter.into()
925 }
926 },
927 })),
928 },
929 };
930 AlterTableKind::SetIndexes(SetIndexes {
931 set_indexes: vec![option],
932 })
933 }
934 AlterTableOperation::UnsetIndex { options } => {
935 let option = match options {
936 sql::statements::alter::UnsetIndexOperation::Fulltext { column_name } => {
937 UnsetIndex {
938 options: Some(unset_index::Options::Fulltext(UnsetFulltext {
939 column_name: column_name.value,
940 })),
941 }
942 }
943 sql::statements::alter::UnsetIndexOperation::Inverted { column_name } => {
944 UnsetIndex {
945 options: Some(unset_index::Options::Inverted(UnsetInverted {
946 column_name: column_name.value,
947 })),
948 }
949 }
950 sql::statements::alter::UnsetIndexOperation::Skipping { column_name } => {
951 UnsetIndex {
952 options: Some(unset_index::Options::Skipping(UnsetSkipping {
953 column_name: column_name.value,
954 })),
955 }
956 }
957 };
958
959 AlterTableKind::UnsetIndexes(UnsetIndexes {
960 unset_indexes: vec![option],
961 })
962 }
963 AlterTableOperation::DropDefaults { columns } => {
964 AlterTableKind::DropDefaults(DropDefaults {
965 drop_defaults: columns
966 .into_iter()
967 .map(|col| {
968 let column_name = col.0.to_string();
969 Ok(api::v1::DropDefault { column_name })
970 })
971 .collect::<Result<Vec<_>>>()?,
972 })
973 }
974 AlterTableOperation::SetDefaults { defaults } => AlterTableKind::SetDefaults(SetDefaults {
975 set_defaults: defaults
976 .into_iter()
977 .map(|col| {
978 let column_name = col.column_name.to_string();
979 let default_constraint = serde_json::to_string(&col.default_constraint)
980 .context(EncodeJsonSnafu)?
981 .into_bytes();
982 Ok(api::v1::SetDefault {
983 column_name,
984 default_constraint,
985 })
986 })
987 .collect::<Result<Vec<_>>>()?,
988 }),
989 };
990
991 Ok(AlterTableExpr {
992 catalog_name,
993 schema_name,
994 table_name,
995 kind: Some(kind),
996 })
997}
998
999pub fn to_alter_database_expr(
1001 alter_database: AlterDatabase,
1002 query_ctx: &QueryContextRef,
1003) -> Result<AlterDatabaseExpr> {
1004 let catalog = query_ctx.current_catalog();
1005 let schema = alter_database.database_name;
1006
1007 let kind = match alter_database.alter_operation {
1008 AlterDatabaseOperation::SetDatabaseOption { options } => {
1009 let options = options.into_iter().map(Into::into).collect();
1010 AlterDatabaseKind::SetDatabaseOptions(SetDatabaseOptions {
1011 set_database_options: options,
1012 })
1013 }
1014 AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
1015 AlterDatabaseKind::UnsetDatabaseOptions(UnsetDatabaseOptions { keys })
1016 }
1017 };
1018
1019 Ok(AlterDatabaseExpr {
1020 catalog_name: catalog.to_string(),
1021 schema_name: schema.to_string(),
1022 kind: Some(kind),
1023 })
1024}
1025
1026pub fn to_create_view_expr(
1028 stmt: CreateView,
1029 logical_plan: Vec<u8>,
1030 table_names: Vec<TableName>,
1031 columns: Vec<String>,
1032 plan_columns: Vec<String>,
1033 definition: String,
1034 query_ctx: QueryContextRef,
1035) -> Result<CreateViewExpr> {
1036 let (catalog_name, schema_name, view_name) = table_idents_to_full_name(&stmt.name, &query_ctx)
1037 .map_err(BoxedError::new)
1038 .context(ExternalSnafu)?;
1039
1040 let expr = CreateViewExpr {
1041 catalog_name,
1042 schema_name,
1043 view_name,
1044 logical_plan,
1045 create_if_not_exists: stmt.if_not_exists,
1046 or_replace: stmt.or_replace,
1047 table_names,
1048 columns,
1049 plan_columns,
1050 definition,
1051 };
1052
1053 Ok(expr)
1054}
1055
1056pub fn to_create_flow_task_expr(
1057 create_flow: CreateFlow,
1058 query_ctx: &QueryContextRef,
1059) -> Result<CreateFlowExpr> {
1060 let sink_table_ref = object_name_to_table_reference(create_flow.sink_table_name.clone(), true)
1062 .with_context(|_| ConvertIdentifierSnafu {
1063 ident: create_flow.sink_table_name.to_string(),
1064 })?;
1065 let catalog = sink_table_ref
1066 .catalog()
1067 .unwrap_or(query_ctx.current_catalog())
1068 .to_string();
1069 let schema = sink_table_ref
1070 .schema()
1071 .map(|s| s.to_owned())
1072 .unwrap_or(query_ctx.current_schema());
1073
1074 let sink_table_name = TableName {
1075 catalog_name: catalog,
1076 schema_name: schema,
1077 table_name: sink_table_ref.table().to_string(),
1078 };
1079
1080 let source_table_names = extract_tables_from_query(&create_flow.query)
1081 .map(|name| {
1082 let reference =
1083 object_name_to_table_reference(name.clone(), true).with_context(|_| {
1084 ConvertIdentifierSnafu {
1085 ident: name.to_string(),
1086 }
1087 })?;
1088 let catalog = reference
1089 .catalog()
1090 .unwrap_or(query_ctx.current_catalog())
1091 .to_string();
1092 let schema = reference
1093 .schema()
1094 .map(|s| s.to_string())
1095 .unwrap_or(query_ctx.current_schema());
1096
1097 let table_name = TableName {
1098 catalog_name: catalog,
1099 schema_name: schema,
1100 table_name: reference.table().to_string(),
1101 };
1102 Ok(table_name)
1103 })
1104 .collect::<Result<Vec<_>>>()?;
1105
1106 let eval_interval = create_flow.eval_interval;
1107
1108 let flow_options = stringify_flow_options(create_flow.flow_options)?;
1109 Ok(CreateFlowExpr {
1110 catalog_name: query_ctx.current_catalog().to_string(),
1111 flow_name: sanitize_flow_name(create_flow.flow_name)?,
1112 source_table_names,
1113 sink_table_name: Some(sink_table_name),
1114 or_replace: create_flow.or_replace,
1115 create_if_not_exists: create_flow.if_not_exists,
1116 expire_after: create_flow.expire_after.map(|value| ExpireAfter { value }),
1117 eval_interval: eval_interval.map(|seconds| api::v1::EvalInterval { seconds }),
1118 comment: create_flow.comment.unwrap_or_default(),
1119 sql: create_flow.query.to_string(),
1120 flow_options,
1121 })
1122}
1123
1124fn stringify_flow_options(flow_options: OptionMap) -> Result<HashMap<String, String>> {
1125 let options_len = flow_options.len();
1126 let flow_options = flow_options.into_map();
1127 ensure!(
1128 flow_options.len() == options_len,
1129 InvalidSqlSnafu {
1130 err_msg: "flow options only support scalar string-compatible values".to_string(),
1131 }
1132 );
1133 Ok(flow_options)
1134}
1135
1136fn sanitize_flow_name(mut flow_name: ObjectName) -> Result<String> {
1138 ensure!(
1139 flow_name.0.len() == 1,
1140 InvalidFlowNameSnafu {
1141 name: flow_name.to_string(),
1142 }
1143 );
1144 Ok(flow_name.0.swap_remove(0).to_string_unquoted())
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150 use std::collections::HashMap;
1151
1152 use api::v1::{SetDatabaseOptions, UnsetDatabaseOptions};
1153 use datatypes::value::Value;
1154 use session::context::{QueryContext, QueryContextBuilder};
1155 use sql::dialect::GreptimeDbDialect;
1156 use sql::parser::{ParseOptions, ParserContext};
1157 use sql::statements::statement::Statement;
1158 use store_api::storage::ColumnDefaultConstraint;
1159
1160 use super::*;
1161
1162 #[test]
1163 fn test_create_flow_tql_expr() {
1164 let sql = r#"
1165CREATE FLOW calc_reqs SINK TO cnt_reqs AS
1166TQL EVAL (0, 15, '5s') count_values("status_code", http_requests);"#;
1167 let stmt =
1168 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1169
1170 assert!(
1171 stmt.is_err(),
1172 "Expected error for invalid TQL EVAL parameters: {:#?}",
1173 stmt
1174 );
1175
1176 let sql = r#"
1177CREATE FLOW calc_reqs SINK TO cnt_reqs AS
1178TQL EVAL (now() - '15s'::interval, now(), '5s') count_values("status_code", http_requests);"#;
1179 let stmt =
1180 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1181 .unwrap()
1182 .pop()
1183 .unwrap();
1184
1185 let Statement::CreateFlow(create_flow) = stmt else {
1186 unreachable!()
1187 };
1188 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1189
1190 let to_dot_sep =
1191 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1192 assert_eq!("calc_reqs", expr.flow_name);
1193 assert_eq!("greptime", expr.catalog_name);
1194 assert_eq!(
1195 "greptime.public.cnt_reqs",
1196 expr.sink_table_name.map(to_dot_sep).unwrap()
1197 );
1198 assert_eq!(1, expr.source_table_names.len());
1199 assert_eq!(
1200 "greptime.public.http_requests",
1201 to_dot_sep(expr.source_table_names[0].clone())
1202 );
1203 assert_eq!(
1204 r#"TQL EVAL (now() - '15s'::interval, now(), '5s') count_values("status_code", http_requests)"#,
1205 expr.sql
1206 );
1207
1208 let sql = r#"
1209CREATE FLOW calc_reqs SINK TO cnt_reqs AS
1210TQL EVAL (now() - '15s'::interval, now(), '5s') count_values("status_code", http_requests{__schema__="greptime_private"});"#;
1211 let stmt =
1212 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1213 .unwrap()
1214 .pop()
1215 .unwrap();
1216 let Statement::CreateFlow(create_flow) = stmt else {
1217 unreachable!()
1218 };
1219 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1220 assert_eq!(1, expr.source_table_names.len());
1221 assert_eq!(
1222 "greptime.greptime_private.http_requests",
1223 to_dot_sep(expr.source_table_names[0].clone())
1224 );
1225
1226 let sql = r#"
1227CREATE FLOW calc_reqs SINK TO cnt_reqs AS
1228TQL EVAL (now() - '15s'::interval, now(), '5s') count_values("status_code", http_requests{__database__="greptime_private"});"#;
1229 let stmt =
1230 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1231 .unwrap()
1232 .pop()
1233 .unwrap();
1234 let Statement::CreateFlow(create_flow) = stmt else {
1235 unreachable!()
1236 };
1237 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1238 assert_eq!(1, expr.source_table_names.len());
1239 assert_eq!(
1240 "greptime.greptime_private.http_requests",
1241 to_dot_sep(expr.source_table_names[0].clone())
1242 );
1243 }
1244
1245 #[test]
1246 fn test_create_flow_tql_cte_source_tables() {
1247 let sql = r#"
1248CREATE FLOW calc_cte
1249SINK TO metric_cte_sink
1250EVAL INTERVAL '1m'
1251AS
1252WITH tql(ts, the_value) AS (
1253 TQL EVAL (now() - '1m'::interval, now(), '5s') metric_cte
1254)
1255SELECT * FROM tql;
1256"#;
1257
1258 let stmt =
1259 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1260 .unwrap()
1261 .pop()
1262 .unwrap();
1263
1264 let Statement::CreateFlow(create_flow) = stmt else {
1265 unreachable!()
1266 };
1267 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1268
1269 let to_dot_sep =
1270 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1271 assert_eq!(1, expr.source_table_names.len());
1272 assert_eq!(
1273 "greptime.public.metric_cte",
1274 to_dot_sep(expr.source_table_names[0].clone())
1275 );
1276 }
1277
1278 #[test]
1279 fn test_create_flow_tql_cte_source_tables_quoted_cte_name() {
1280 let sql = r#"
1281CREATE FLOW calc_cte
1282SINK TO metric_cte_sink
1283EVAL INTERVAL '1m'
1284AS
1285WITH "TQL"(ts, the_value) AS (
1286 TQL EVAL (now() - '1m'::interval, now(), '5s') metric_cte
1287)
1288SELECT * FROM "TQL";
1289"#;
1290
1291 let stmt =
1292 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1293 .unwrap()
1294 .pop()
1295 .unwrap();
1296
1297 let Statement::CreateFlow(create_flow) = stmt else {
1298 unreachable!()
1299 };
1300 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1301
1302 let to_dot_sep =
1303 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1304 assert_eq!(1, expr.source_table_names.len());
1305 assert_eq!(
1306 "greptime.public.metric_cte",
1307 to_dot_sep(expr.source_table_names[0].clone())
1308 );
1309 }
1310
1311 #[test]
1312 fn test_create_flow_tql_cte_source_tables_same_name() {
1313 let sql = r#"
1314CREATE FLOW calc_cte
1315SINK TO metric_cte_sink
1316EVAL INTERVAL '1m'
1317AS
1318WITH tql(ts, the_value) AS (
1319 TQL EVAL (now() - '1m'::interval, now(), '5s') tql
1320)
1321SELECT * FROM tql;
1322"#;
1323
1324 let stmt =
1325 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1326 .unwrap()
1327 .pop()
1328 .unwrap();
1329
1330 let Statement::CreateFlow(create_flow) = stmt else {
1331 unreachable!()
1332 };
1333 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1334
1335 let to_dot_sep =
1336 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1337 assert_eq!(1, expr.source_table_names.len());
1338 assert_eq!(
1339 "greptime.public.tql",
1340 to_dot_sep(expr.source_table_names[0].clone())
1341 );
1342 }
1343
1344 #[test]
1345 fn test_create_flow_expr() {
1346 let sql = r"
1347CREATE FLOW test_distinct_basic SINK TO out_distinct_basic AS
1348SELECT
1349 DISTINCT number as dis
1350FROM
1351 distinct_basic;";
1352 let stmt =
1353 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1354 .unwrap()
1355 .pop()
1356 .unwrap();
1357
1358 let Statement::CreateFlow(create_flow) = stmt else {
1359 unreachable!()
1360 };
1361 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1362
1363 let to_dot_sep =
1364 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1365 assert_eq!("test_distinct_basic", expr.flow_name);
1366 assert_eq!("greptime", expr.catalog_name);
1367 assert_eq!(
1368 "greptime.public.out_distinct_basic",
1369 expr.sink_table_name.map(to_dot_sep).unwrap()
1370 );
1371 assert_eq!(1, expr.source_table_names.len());
1372 assert_eq!(
1373 "greptime.public.distinct_basic",
1374 to_dot_sep(expr.source_table_names[0].clone())
1375 );
1376 assert_eq!(
1377 r"SELECT
1378 DISTINCT number as dis
1379FROM
1380 distinct_basic",
1381 expr.sql
1382 );
1383
1384 let sql = r"
1385CREATE FLOW `task_2`
1386SINK TO schema_1.table_1
1387AS
1388SELECT max(c1), min(c2) FROM schema_2.table_2;";
1389 let stmt =
1390 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1391 .unwrap()
1392 .pop()
1393 .unwrap();
1394
1395 let Statement::CreateFlow(create_flow) = stmt else {
1396 unreachable!()
1397 };
1398 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1399
1400 let to_dot_sep =
1401 |c: TableName| format!("{}.{}.{}", c.catalog_name, c.schema_name, c.table_name);
1402 assert_eq!("task_2", expr.flow_name);
1403 assert_eq!("greptime", expr.catalog_name);
1404 assert_eq!(
1405 "greptime.schema_1.table_1",
1406 expr.sink_table_name.map(to_dot_sep).unwrap()
1407 );
1408 assert_eq!(1, expr.source_table_names.len());
1409 assert_eq!(
1410 "greptime.schema_2.table_2",
1411 to_dot_sep(expr.source_table_names[0].clone())
1412 );
1413 assert_eq!("SELECT max(c1), min(c2) FROM schema_2.table_2", expr.sql);
1414 assert!(expr.flow_options.is_empty());
1415
1416 let sql = r"
1417CREATE FLOW task_3
1418SINK TO schema_1.table_1
1419WITH (defer_on_missing_source = 'true', foo = 'bar')
1420AS
1421SELECT max(c1), min(c2) FROM schema_2.table_2;";
1422 let stmt =
1423 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1424 .unwrap()
1425 .pop()
1426 .unwrap();
1427
1428 let Statement::CreateFlow(create_flow) = stmt else {
1429 unreachable!()
1430 };
1431 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1432 assert_eq!(
1433 expr.flow_options,
1434 HashMap::from([
1435 ("defer_on_missing_source".to_string(), "true".to_string()),
1436 ("foo".to_string(), "bar".to_string()),
1437 ])
1438 );
1439
1440 let sql = r"
1441CREATE FLOW task_4
1442SINK TO schema_1.table_1
1443WITH (defer_on_missing_source = true)
1444AS
1445SELECT max(c1), min(c2) FROM schema_2.table_2;";
1446 let stmt =
1447 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1448 .unwrap()
1449 .pop()
1450 .unwrap();
1451
1452 let Statement::CreateFlow(create_flow) = stmt else {
1453 unreachable!()
1454 };
1455 let expr = to_create_flow_task_expr(create_flow, &QueryContext::arc()).unwrap();
1456 assert_eq!(
1457 expr.flow_options,
1458 HashMap::from([("defer_on_missing_source".to_string(), "true".to_string(),)])
1459 );
1460
1461 let sql = r"
1462CREATE FLOW task_5
1463SINK TO schema_1.table_1
1464WITH (defer_on_missing_source = [true])
1465AS
1466SELECT max(c1), min(c2) FROM schema_2.table_2;";
1467 let stmt =
1468 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1469 .unwrap()
1470 .pop()
1471 .unwrap();
1472
1473 let Statement::CreateFlow(create_flow) = stmt else {
1474 unreachable!()
1475 };
1476 let res = to_create_flow_task_expr(create_flow, &QueryContext::arc());
1477 assert!(res.is_err());
1478 assert!(
1479 res.unwrap_err()
1480 .to_string()
1481 .contains("flow options only support scalar string-compatible values")
1482 );
1483
1484 let sql = r"
1485CREATE FLOW abc.`task_2`
1486SINK TO schema_1.table_1
1487AS
1488SELECT max(c1), min(c2) FROM schema_2.table_2;";
1489 let stmt =
1490 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1491 .unwrap()
1492 .pop()
1493 .unwrap();
1494
1495 let Statement::CreateFlow(create_flow) = stmt else {
1496 unreachable!()
1497 };
1498 let res = to_create_flow_task_expr(create_flow, &QueryContext::arc());
1499
1500 assert!(res.is_err());
1501 assert!(
1502 res.unwrap_err()
1503 .to_string()
1504 .contains("Invalid flow name: abc.`task_2`")
1505 );
1506 }
1507
1508 #[test]
1509 fn test_create_to_expr() {
1510 let sql = "CREATE TABLE monitor (host STRING,ts TIMESTAMP,TIME INDEX (ts),PRIMARY KEY(host)) ENGINE=mito WITH(ttl='3days', write_buffer_size='1024KB');";
1511 let stmt =
1512 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1513 .unwrap()
1514 .pop()
1515 .unwrap();
1516
1517 let Statement::CreateTable(create_table) = stmt else {
1518 unreachable!()
1519 };
1520 let expr = create_to_expr(&create_table, &QueryContext::arc()).unwrap();
1521 assert_eq!("3days", expr.table_options.get("ttl").unwrap());
1522 assert_eq!(
1523 "1.0MiB",
1524 expr.table_options.get("write_buffer_size").unwrap()
1525 );
1526
1527 let sql = "CREATE TABLE monitor (ts TIMESTAMP TIME INDEX) WITH(skip_wal='false');";
1528 let stmt =
1529 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1530 .unwrap()
1531 .pop()
1532 .unwrap();
1533 let Statement::CreateTable(create_table) = stmt else {
1534 unreachable!()
1535 };
1536 let expr = create_to_expr(&create_table, &QueryContext::arc()).unwrap();
1537 assert_eq!(
1538 Some("false"),
1539 expr.table_options
1540 .get(store_api::mito_engine_options::SKIP_WAL_KEY)
1541 .map(String::as_str)
1542 );
1543 }
1544
1545 #[test]
1546 fn test_invalid_create_to_expr() {
1547 let cases = [
1548 "CREATE TABLE monitor (host STRING primary key, ts TIMESTAMP TIME INDEX, some_column text, some_column string);",
1550 "CREATE TABLE monitor (host STRING, ts TIMESTAMP TIME INDEX, some_column STRING, PRIMARY KEY (some_column, host, some_column));",
1552 "CREATE TABLE monitor (host STRING, ts TIMESTAMP TIME INDEX, PRIMARY KEY (host, ts));",
1554 ];
1555
1556 for sql in cases {
1557 let stmt = ParserContext::create_with_dialect(
1558 sql,
1559 &GreptimeDbDialect {},
1560 ParseOptions::default(),
1561 )
1562 .unwrap()
1563 .pop()
1564 .unwrap();
1565 let Statement::CreateTable(create_table) = stmt else {
1566 unreachable!()
1567 };
1568 create_to_expr(&create_table, &QueryContext::arc()).unwrap_err();
1569 }
1570 }
1571
1572 #[test]
1573 fn test_create_to_expr_with_default_timestamp_value() {
1574 let sql = "CREATE TABLE monitor (v double,ts TIMESTAMP default '2024-01-30T00:01:01',TIME INDEX (ts)) engine=mito;";
1575 let stmt =
1576 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1577 .unwrap()
1578 .pop()
1579 .unwrap();
1580
1581 let Statement::CreateTable(create_table) = stmt else {
1582 unreachable!()
1583 };
1584
1585 let expr = create_to_expr(&create_table, &QueryContext::arc()).unwrap();
1587 let ts_column = &expr.column_defs[1];
1588 let constraint = assert_ts_column(ts_column);
1589 assert!(
1590 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
1591 if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
1592 );
1593
1594 let ctx = QueryContextBuilder::default()
1596 .timezone(Timezone::from_tz_string("+08:00").unwrap())
1597 .build()
1598 .into();
1599 let expr = create_to_expr(&create_table, &ctx).unwrap();
1600 let ts_column = &expr.column_defs[1];
1601 let constraint = assert_ts_column(ts_column);
1602 assert!(
1603 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
1604 if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
1605 );
1606 }
1607
1608 fn assert_ts_column(ts_column: &api::v1::ColumnDef) -> ColumnDefaultConstraint {
1609 assert_eq!("ts", ts_column.name);
1610 assert_eq!(
1611 ColumnDataType::TimestampMillisecond as i32,
1612 ts_column.data_type
1613 );
1614 assert!(!ts_column.default_constraint.is_empty());
1615
1616 ColumnDefaultConstraint::try_from(&ts_column.default_constraint[..]).unwrap()
1617 }
1618
1619 #[test]
1620 fn test_to_alter_expr() {
1621 let sql = "ALTER DATABASE greptime SET key1='value1', key2='value2';";
1622 let stmt =
1623 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1624 .unwrap()
1625 .pop()
1626 .unwrap();
1627
1628 let Statement::AlterDatabase(alter_database) = stmt else {
1629 unreachable!()
1630 };
1631
1632 let expr = to_alter_database_expr(alter_database, &QueryContext::arc()).unwrap();
1633 let kind = expr.kind.unwrap();
1634
1635 let AlterDatabaseKind::SetDatabaseOptions(SetDatabaseOptions {
1636 set_database_options,
1637 }) = kind
1638 else {
1639 unreachable!()
1640 };
1641
1642 assert_eq!(2, set_database_options.len());
1643 assert_eq!("key1", set_database_options[0].key);
1644 assert_eq!("value1", set_database_options[0].value);
1645 assert_eq!("key2", set_database_options[1].key);
1646 assert_eq!("value2", set_database_options[1].value);
1647
1648 let sql = "ALTER DATABASE greptime UNSET key1, key2;";
1649 let stmt =
1650 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1651 .unwrap()
1652 .pop()
1653 .unwrap();
1654
1655 let Statement::AlterDatabase(alter_database) = stmt else {
1656 unreachable!()
1657 };
1658
1659 let expr = to_alter_database_expr(alter_database, &QueryContext::arc()).unwrap();
1660 let kind = expr.kind.unwrap();
1661
1662 let AlterDatabaseKind::UnsetDatabaseOptions(UnsetDatabaseOptions { keys }) = kind else {
1663 unreachable!()
1664 };
1665
1666 assert_eq!(2, keys.len());
1667 assert!(keys.contains(&"key1".to_string()));
1668 assert!(keys.contains(&"key2".to_string()));
1669
1670 let sql = "ALTER TABLE monitor add column ts TIMESTAMP default '2024-01-30T00:01:01';";
1671 let stmt =
1672 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1673 .unwrap()
1674 .pop()
1675 .unwrap();
1676
1677 let Statement::AlterTable(alter_table) = stmt else {
1678 unreachable!()
1679 };
1680
1681 let expr = to_alter_table_expr(alter_table.clone(), &QueryContext::arc()).unwrap();
1683 let kind = expr.kind.unwrap();
1684
1685 let AlterTableKind::AddColumns(AddColumns { add_columns, .. }) = kind else {
1686 unreachable!()
1687 };
1688
1689 assert_eq!(1, add_columns.len());
1690 let ts_column = add_columns[0].column_def.clone().unwrap();
1691 let constraint = assert_ts_column(&ts_column);
1692 assert!(
1693 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
1694 if ts.to_iso8601_string() == "2024-01-30 00:01:01+0000")
1695 );
1696
1697 let ctx = QueryContextBuilder::default()
1700 .timezone(Timezone::from_tz_string("+08:00").unwrap())
1701 .build()
1702 .into();
1703 let expr = to_alter_table_expr(alter_table, &ctx).unwrap();
1704 let kind = expr.kind.unwrap();
1705
1706 let AlterTableKind::AddColumns(AddColumns { add_columns, .. }) = kind else {
1707 unreachable!()
1708 };
1709
1710 assert_eq!(1, add_columns.len());
1711 let ts_column = add_columns[0].column_def.clone().unwrap();
1712 let constraint = assert_ts_column(&ts_column);
1713 assert!(
1714 matches!(constraint, ColumnDefaultConstraint::Value(Value::Timestamp(ts))
1715 if ts.to_iso8601_string() == "2024-01-29 16:01:01+0000")
1716 );
1717 }
1718
1719 #[test]
1720 fn test_to_alter_modify_column_type_expr() {
1721 let sql = "ALTER TABLE monitor MODIFY COLUMN mem_usage STRING;";
1722 let stmt =
1723 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1724 .unwrap()
1725 .pop()
1726 .unwrap();
1727
1728 let Statement::AlterTable(alter_table) = stmt else {
1729 unreachable!()
1730 };
1731
1732 let expr = to_alter_table_expr(alter_table.clone(), &QueryContext::arc()).unwrap();
1734 let kind = expr.kind.unwrap();
1735
1736 let AlterTableKind::ModifyColumnTypes(ModifyColumnTypes {
1737 modify_column_types,
1738 }) = kind
1739 else {
1740 unreachable!()
1741 };
1742
1743 assert_eq!(1, modify_column_types.len());
1744 let modify_column_type = &modify_column_types[0];
1745
1746 assert_eq!("mem_usage", modify_column_type.column_name);
1747 assert_eq!(
1748 ColumnDataType::String as i32,
1749 modify_column_type.target_type
1750 );
1751 assert!(modify_column_type.target_type_extension.is_none());
1752 }
1753
1754 #[test]
1755 fn test_alter_json2_is_not_supported() {
1756 for sql in [
1757 "ALTER TABLE monitor MODIFY COLUMN payload JSON2;",
1758 "ALTER TABLE monitor MODIFY COLUMN payload JSON2 (service STRING);",
1759 ] {
1760 let stmt = ParserContext::create_with_dialect(
1761 sql,
1762 &GreptimeDbDialect {},
1763 ParseOptions::default(),
1764 )
1765 .unwrap()
1766 .pop()
1767 .unwrap();
1768
1769 let Statement::AlterTable(alter_table) = stmt else {
1770 unreachable!()
1771 };
1772 let err = to_alter_table_expr(alter_table, &QueryContext::arc()).unwrap_err();
1773
1774 assert!(matches!(err, crate::error::Error::NotSupported { .. }));
1775 assert_eq!(
1776 "Not supported: ALTER TABLE MODIFY COLUMN to JSON2 type",
1777 err.to_string()
1778 );
1779 }
1780 }
1781
1782 #[test]
1783 fn test_to_repartition_request() {
1784 let sql = r#"
1785ALTER TABLE metrics REPARTITION (
1786 device_id < 100
1787) INTO (
1788 device_id < 100 AND area < 'South',
1789 device_id < 100 AND area >= 'South'
1790);"#;
1791 let stmt =
1792 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1793 .unwrap()
1794 .pop()
1795 .unwrap();
1796
1797 let Statement::AlterTable(alter_table) = stmt else {
1798 unreachable!()
1799 };
1800
1801 let request = to_repartition_request(alter_table, &QueryContext::arc()).unwrap();
1802 assert_eq!("greptime", request.catalog_name);
1803 assert_eq!("public", request.schema_name);
1804 assert_eq!("metrics", request.table_name);
1805 let RepartitionSource::Partitions {
1806 from_exprs,
1807 target_partition_columns,
1808 } = request.source
1809 else {
1810 unreachable!()
1811 };
1812 assert!(target_partition_columns.is_none());
1813 assert_eq!(
1814 from_exprs
1815 .into_iter()
1816 .map(|x| x.to_string())
1817 .collect::<Vec<_>>(),
1818 vec!["device_id < 100".to_string()]
1819 );
1820 assert_eq!(
1821 request
1822 .into_exprs
1823 .into_iter()
1824 .map(|x| x.to_string())
1825 .collect::<Vec<_>>(),
1826 vec![
1827 "device_id < 100 AND area < 'South'".to_string(),
1828 "device_id < 100 AND area >= 'South'".to_string()
1829 ]
1830 );
1831 }
1832
1833 #[test]
1834 fn test_to_repartition_request_with_target_partition_columns() {
1835 let sql = r#"
1836ALTER TABLE metrics REPARTITION (
1837 device_id < 100
1838) ON COLUMNS (device_id, area) INTO (
1839 device_id < 100 AND area < 'South',
1840 device_id < 100 AND area >= 'South'
1841);"#;
1842 let stmt =
1843 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1844 .unwrap()
1845 .pop()
1846 .unwrap();
1847
1848 let Statement::AlterTable(alter_table) = stmt else {
1849 unreachable!()
1850 };
1851
1852 let request = to_repartition_request(alter_table, &QueryContext::arc()).unwrap();
1853 let RepartitionSource::Partitions {
1854 target_partition_columns,
1855 ..
1856 } = request.source
1857 else {
1858 unreachable!()
1859 };
1860
1861 assert_eq!(
1862 target_partition_columns,
1863 Some(vec!["device_id".to_string(), "area".to_string()])
1864 );
1865 }
1866
1867 #[test]
1868 fn test_to_repartition_request_with_unpartitioned_source() {
1869 let sql = r#"
1870ALTER TABLE metrics PARTITION ON COLUMNS (device_id, area) (
1871 device_id < 100 AND area < 'South',
1872 device_id < 100 AND area >= 'South'
1873);"#;
1874 let stmt =
1875 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1876 .unwrap()
1877 .pop()
1878 .unwrap();
1879
1880 let Statement::AlterTable(alter_table) = stmt else {
1881 unreachable!()
1882 };
1883
1884 let request = to_repartition_request(alter_table, &QueryContext::arc()).unwrap();
1885 assert_eq!("greptime", request.catalog_name);
1886 assert_eq!("public", request.schema_name);
1887 assert_eq!("metrics", request.table_name);
1888 let RepartitionSource::Unpartitioned { partition_columns } = request.source else {
1889 unreachable!()
1890 };
1891 assert_eq!(partition_columns, vec!["device_id", "area"]);
1892 assert_eq!(
1893 request
1894 .into_exprs
1895 .into_iter()
1896 .map(|x| x.to_string())
1897 .collect::<Vec<_>>(),
1898 vec![
1899 "device_id < 100 AND area < 'South'".to_string(),
1900 "device_id < 100 AND area >= 'South'".to_string()
1901 ]
1902 );
1903 }
1904
1905 fn new_test_table_names() -> Vec<TableName> {
1906 vec![
1907 TableName {
1908 catalog_name: "greptime".to_string(),
1909 schema_name: "public".to_string(),
1910 table_name: "a_table".to_string(),
1911 },
1912 TableName {
1913 catalog_name: "greptime".to_string(),
1914 schema_name: "public".to_string(),
1915 table_name: "b_table".to_string(),
1916 },
1917 ]
1918 }
1919
1920 #[test]
1921 fn test_to_create_view_expr() {
1922 let sql = "CREATE VIEW test AS SELECT * FROM NUMBERS";
1923 let stmt =
1924 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1925 .unwrap()
1926 .pop()
1927 .unwrap();
1928
1929 let Statement::CreateView(stmt) = stmt else {
1930 unreachable!()
1931 };
1932
1933 let logical_plan = vec![1, 2, 3];
1934 let table_names = new_test_table_names();
1935 let columns = vec!["a".to_string()];
1936 let plan_columns = vec!["number".to_string()];
1937
1938 let expr = to_create_view_expr(
1939 stmt,
1940 logical_plan.clone(),
1941 table_names.clone(),
1942 columns.clone(),
1943 plan_columns.clone(),
1944 sql.to_string(),
1945 QueryContext::arc(),
1946 )
1947 .unwrap();
1948
1949 assert_eq!("greptime", expr.catalog_name);
1950 assert_eq!("public", expr.schema_name);
1951 assert_eq!("test", expr.view_name);
1952 assert!(!expr.create_if_not_exists);
1953 assert!(!expr.or_replace);
1954 assert_eq!(logical_plan, expr.logical_plan);
1955 assert_eq!(table_names, expr.table_names);
1956 assert_eq!(sql, expr.definition);
1957 assert_eq!(columns, expr.columns);
1958 assert_eq!(plan_columns, expr.plan_columns);
1959 }
1960
1961 #[test]
1962 fn test_to_create_view_expr_complex() {
1963 let sql = "CREATE OR REPLACE VIEW IF NOT EXISTS test.test_view AS SELECT * FROM NUMBERS";
1964 let stmt =
1965 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1966 .unwrap()
1967 .pop()
1968 .unwrap();
1969
1970 let Statement::CreateView(stmt) = stmt else {
1971 unreachable!()
1972 };
1973
1974 let logical_plan = vec![1, 2, 3];
1975 let table_names = new_test_table_names();
1976 let columns = vec!["a".to_string()];
1977 let plan_columns = vec!["number".to_string()];
1978
1979 let expr = to_create_view_expr(
1980 stmt,
1981 logical_plan.clone(),
1982 table_names.clone(),
1983 columns.clone(),
1984 plan_columns.clone(),
1985 sql.to_string(),
1986 QueryContext::arc(),
1987 )
1988 .unwrap();
1989
1990 assert_eq!("greptime", expr.catalog_name);
1991 assert_eq!("test", expr.schema_name);
1992 assert_eq!("test_view", expr.view_name);
1993 assert!(expr.create_if_not_exists);
1994 assert!(expr.or_replace);
1995 assert_eq!(logical_plan, expr.logical_plan);
1996 assert_eq!(table_names, expr.table_names);
1997 assert_eq!(sql, expr.definition);
1998 assert_eq!(columns, expr.columns);
1999 assert_eq!(plan_columns, expr.plan_columns);
2000 }
2001
2002 #[test]
2003 fn test_expr_to_create() {
2004 let sql = r#"CREATE TABLE IF NOT EXISTS `tt` (
2005 `timestamp` TIMESTAMP(9) NOT NULL,
2006 `ip_address` STRING NULL SKIPPING INDEX WITH(false_positive_rate = '0.01', granularity = '10240', type = 'BLOOM'),
2007 `username` STRING NULL,
2008 `http_method` STRING NULL INVERTED INDEX,
2009 `request_line` STRING NULL FULLTEXT INDEX WITH(analyzer = 'English', backend = 'bloom', case_sensitive = 'false', false_positive_rate = '0.01', granularity = '10240'),
2010 `protocol` STRING NULL,
2011 `status_code` INT NULL INVERTED INDEX,
2012 `response_size` BIGINT NULL,
2013 `message` STRING NULL,
2014 TIME INDEX (`timestamp`),
2015 PRIMARY KEY (`username`, `status_code`)
2016)
2017ENGINE=mito
2018WITH(
2019 append_mode = 'true'
2020)"#;
2021 let stmt =
2022 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
2023 .unwrap()
2024 .pop()
2025 .unwrap();
2026
2027 let Statement::CreateTable(original_create) = stmt else {
2028 unreachable!()
2029 };
2030
2031 let expr = create_to_expr(&original_create, &QueryContext::arc()).unwrap();
2033
2034 let create_table = expr_to_create(&expr, Some('`')).unwrap();
2035 let new_sql = format!("{:#}", create_table);
2036 assert_eq!(sql, new_sql);
2037 }
2038}