1use std::collections::HashMap;
16use std::fmt::{Display, Formatter};
17
18use common_catalog::consts::FILE_ENGINE;
19use common_sql::default_constraint::parse_column_default_constraint;
20use datatypes::json::{JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS, JsonSettings};
21use datatypes::prelude::ConcreteDataType;
22use datatypes::schema::{
23 ColumnDefaultConstraint, FulltextOptions, SkippingIndexOptions, VectorDistanceMetric,
24 VectorIndexEngineType, VectorIndexOptions,
25};
26use itertools::Itertools;
27use serde::Serialize;
28use snafu::ResultExt;
29use sqlparser::ast::{ColumnOption, ColumnOptionDef, DataType, Expr};
30use sqlparser_derive::{Visit, VisitMut};
31
32use crate::ast::{ColumnDef, Ident, ObjectName, Value as SqlValue};
33use crate::dialect::GreptimeDbDialect;
34use crate::error::{
35 InvalidFlowQuerySnafu, InvalidSqlSnafu, Result, SetFulltextOptionSnafu,
36 SetSkippingIndexOptionSnafu,
37};
38use crate::parser::ParserContext;
39use crate::statements::query::Query as GtQuery;
40use crate::statements::statement::Statement;
41use crate::statements::tql::Tql;
42use crate::statements::{OptionMap, sql_data_type_to_concrete_data_type, value_to_sql_value};
43
44const LINE_SEP: &str = ",\n";
45const COMMA_SEP: &str = ", ";
46const INDENT: usize = 2;
47pub const VECTOR_OPT_DIM: &str = "dim";
48
49macro_rules! format_indent {
50 ($fmt: expr, $arg: expr) => {
51 format!($fmt, format_args!("{: >1$}", "", INDENT), $arg)
52 };
53 ($arg: expr) => {
54 format_indent!("{}{}", $arg)
55 };
56}
57
58macro_rules! format_list_indent {
59 ($list: expr) => {
60 $list.iter().map(|e| format_indent!(e)).join(LINE_SEP)
61 };
62}
63
64macro_rules! format_list_comma {
65 ($list: expr) => {
66 $list.iter().map(|e| format!("{}", e)).join(COMMA_SEP)
67 };
68}
69
70#[cfg(feature = "enterprise")]
71pub mod trigger;
72
73fn format_table_constraint(constraints: &[TableConstraint]) -> String {
74 constraints.iter().map(|c| format_indent!(c)).join(LINE_SEP)
75}
76
77#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
79pub enum TableConstraint {
80 PrimaryKey { columns: Vec<Ident> },
82 TimeIndex { column: Ident },
84}
85
86impl Display for TableConstraint {
87 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
88 match self {
89 TableConstraint::PrimaryKey { columns } => {
90 write!(f, "PRIMARY KEY ({})", format_list_comma!(columns))
91 }
92 TableConstraint::TimeIndex { column } => {
93 write!(f, "TIME INDEX ({})", column)
94 }
95 }
96 }
97}
98
99#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
100pub struct CreateTable {
101 pub if_not_exists: bool,
103 pub table_id: u32,
104 pub name: ObjectName,
106 pub columns: Vec<Column>,
107 pub engine: String,
108 pub constraints: Vec<TableConstraint>,
109 pub options: OptionMap,
111 pub partitions: Option<Partitions>,
112}
113
114#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
116pub struct Column {
117 pub column_def: ColumnDef,
119 pub extensions: ColumnExtensions,
121}
122
123#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Default, Serialize)]
125pub struct ColumnExtensions {
126 pub vector_options: Option<OptionMap>,
128
129 pub fulltext_index_options: Option<OptionMap>,
131 pub skipping_index_options: Option<OptionMap>,
133 pub inverted_index_options: Option<OptionMap>,
137 pub vector_index_options: Option<OptionMap>,
139 pub json2_options: Option<Json2Options>,
141}
142
143#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
145pub struct Json2Options {
146 pub(crate) max_auto_expanded_paths: Option<u32>,
148 pub(crate) type_hints: Vec<JsonTypeHint>,
150}
151
152impl Display for Json2Options {
153 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154 let mut options = Vec::with_capacity(self.type_hints.len() + 1);
155 if let Some(max) = self.max_auto_expanded_paths {
156 options.push(format!("max_auto_expanded_paths = {max}"));
157 }
158 options.extend(self.type_hints.iter().map(format_json_type_hint));
159 write!(f, "(\n {}\n )", options.iter().join(",\n "))
160 }
161}
162
163#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
164pub struct JsonTypeHint {
165 pub path: Vec<String>,
166 pub data_type: DataType,
167 pub nullable: bool,
168 pub default: Option<Expr>,
169 pub inverted_index: bool,
170}
171
172impl Column {
173 pub fn name(&self) -> &Ident {
174 &self.column_def.name
175 }
176
177 pub fn data_type(&self) -> &DataType {
178 &self.column_def.data_type
179 }
180
181 pub fn mut_data_type(&mut self) -> &mut DataType {
182 &mut self.column_def.data_type
183 }
184
185 pub fn options(&self) -> &[ColumnOptionDef] {
186 &self.column_def.options
187 }
188
189 pub fn mut_options(&mut self) -> &mut Vec<ColumnOptionDef> {
190 &mut self.column_def.options
191 }
192}
193
194impl Display for Column {
195 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
196 if let Some(vector_options) = &self.extensions.vector_options
197 && let Some(dim) = vector_options.get(VECTOR_OPT_DIM)
198 {
199 write!(f, "{} VECTOR({})", self.column_def.name, dim)?;
200 return Ok(());
201 }
202
203 write!(f, "{} {}", self.column_def.name, self.column_def.data_type)?;
204 if let Some(options) = &self.extensions.json2_options {
205 write!(f, "{options}")?;
206 }
207 for option in &self.column_def.options {
208 write!(f, " {option}")?;
209 }
210
211 if let Some(fulltext_options) = &self.extensions.fulltext_index_options {
212 if !fulltext_options.is_empty() {
213 let options = fulltext_options.kv_pairs();
214 write!(f, " FULLTEXT INDEX WITH({})", format_list_comma!(options))?;
215 } else {
216 write!(f, " FULLTEXT INDEX")?;
217 }
218 }
219
220 if let Some(skipping_index_options) = &self.extensions.skipping_index_options {
221 if !skipping_index_options.is_empty() {
222 let options = skipping_index_options.kv_pairs();
223 write!(f, " SKIPPING INDEX WITH({})", format_list_comma!(options))?;
224 } else {
225 write!(f, " SKIPPING INDEX")?;
226 }
227 }
228
229 if let Some(inverted_index_options) = &self.extensions.inverted_index_options {
230 if !inverted_index_options.is_empty() {
231 let options = inverted_index_options.kv_pairs();
232 write!(f, " INVERTED INDEX WITH({})", format_list_comma!(options))?;
233 } else {
234 write!(f, " INVERTED INDEX")?;
235 }
236 }
237
238 if let Some(vector_index_options) = &self.extensions.vector_index_options {
239 if !vector_index_options.is_empty() {
240 let options = vector_index_options.kv_pairs();
241 write!(f, " VECTOR INDEX WITH({})", format_list_comma!(options))?;
242 } else {
243 write!(f, " VECTOR INDEX")?;
244 }
245 }
246 Ok(())
247 }
248}
249
250impl ColumnExtensions {
251 pub fn build_fulltext_options(&self) -> Result<Option<FulltextOptions>> {
252 let Some(options) = self.fulltext_index_options.as_ref() else {
253 return Ok(None);
254 };
255
256 let options: HashMap<String, String> = options.clone().into_map();
257 Ok(Some(options.try_into().context(SetFulltextOptionSnafu)?))
258 }
259
260 pub fn build_skipping_index_options(&self) -> Result<Option<SkippingIndexOptions>> {
261 let Some(options) = self.skipping_index_options.as_ref() else {
262 return Ok(None);
263 };
264
265 let options: HashMap<String, String> = options.clone().into_map();
266 Ok(Some(
267 options.try_into().context(SetSkippingIndexOptionSnafu)?,
268 ))
269 }
270
271 pub fn build_vector_index_options(&self) -> Result<Option<VectorIndexOptions>> {
272 let Some(options) = self.vector_index_options.as_ref() else {
273 return Ok(None);
274 };
275
276 let options_map: HashMap<String, String> = options.clone().into_map();
277 let mut result = VectorIndexOptions::default();
278
279 if let Some(s) = options_map.get("engine") {
280 result.engine = s.parse::<VectorIndexEngineType>().map_err(|e| {
281 InvalidSqlSnafu {
282 msg: format!("invalid VECTOR INDEX engine: {e}"),
283 }
284 .build()
285 })?;
286 }
287
288 if let Some(s) = options_map.get("metric") {
289 result.metric = s.parse::<VectorDistanceMetric>().map_err(|e| {
290 InvalidSqlSnafu {
291 msg: format!("invalid VECTOR INDEX metric: {e}"),
292 }
293 .build()
294 })?;
295 }
296
297 if let Some(s) = options_map.get("connectivity") {
298 let value = s.parse::<u32>().map_err(|_| {
299 InvalidSqlSnafu {
300 msg: format!(
301 "invalid VECTOR INDEX connectivity: {s}, expected positive integer"
302 ),
303 }
304 .build()
305 })?;
306 if !(2..=2048).contains(&value) {
307 return InvalidSqlSnafu {
308 msg: "VECTOR INDEX connectivity must be in the range [2, 2048].".to_string(),
309 }
310 .fail();
311 }
312 result.connectivity = value;
313 }
314
315 if let Some(s) = options_map.get("expansion_add") {
316 let value = s.parse::<u32>().map_err(|_| {
317 InvalidSqlSnafu {
318 msg: format!(
319 "invalid VECTOR INDEX expansion_add: {s}, expected positive integer"
320 ),
321 }
322 .build()
323 })?;
324 if value == 0 {
325 return InvalidSqlSnafu {
326 msg: "VECTOR INDEX expansion_add must be greater than 0".to_string(),
327 }
328 .fail();
329 }
330 result.expansion_add = value;
331 }
332
333 if let Some(s) = options_map.get("expansion_search") {
334 let value = s.parse::<u32>().map_err(|_| {
335 InvalidSqlSnafu {
336 msg: format!(
337 "invalid VECTOR INDEX expansion_search: {s}, expected positive integer"
338 ),
339 }
340 .build()
341 })?;
342 if value == 0 {
343 return InvalidSqlSnafu {
344 msg: "VECTOR INDEX expansion_search must be greater than 0".to_string(),
345 }
346 .fail();
347 }
348 result.expansion_search = value;
349 }
350
351 Ok(Some(result))
352 }
353
354 pub fn build_json_settings(&self) -> Result<Option<JsonSettings>> {
355 let Some(options) = &self.json2_options else {
356 return Ok(None);
357 };
358
359 let type_hints = options
360 .type_hints
361 .iter()
362 .map(|hint| {
363 Ok(datatypes::json::JsonTypeHint {
364 path: hint.path.clone(),
365 data_type: json_type_hint_concrete_data_type(&hint.data_type)?,
366 nullable: hint.nullable,
367 default_constraint: build_json_type_hint_default_constraint(hint)?,
368 inverted_index: hint.inverted_index,
369 })
370 })
371 .collect::<Result<Vec<_>>>()?;
372 let settings = JsonSettings::try_new(
373 type_hints,
374 options
375 .max_auto_expanded_paths
376 .or(Some(JSON2_DEFAULT_MAX_AUTO_EXPANDED_PATHS)),
377 )?;
378 Ok(Some(settings))
379 }
380
381 pub fn set_json_settings(&mut self, settings: JsonSettings) -> Result<()> {
382 let (type_hints, max_auto_expanded_paths) = settings.into_parts();
383 let type_hints = type_hints
384 .into_iter()
385 .map(|hint| {
386 let data_type = json_type_hint_sql_data_type(&hint.data_type)?;
387 let default = hint
388 .default_constraint
389 .map(|constraint| column_default_constraint_to_expr(&constraint))
390 .transpose()?;
391 Ok(JsonTypeHint {
392 path: hint.path,
393 data_type,
394 nullable: hint.nullable,
395 default,
396 inverted_index: hint.inverted_index,
397 })
398 })
399 .collect::<Result<Vec<_>>>()?;
400 self.json2_options = (max_auto_expanded_paths.is_some() || !type_hints.is_empty())
401 .then_some(Json2Options {
402 max_auto_expanded_paths,
403 type_hints,
404 });
405 Ok(())
406 }
407}
408
409fn build_json_type_hint_default_constraint(
410 hint: &JsonTypeHint,
411) -> Result<Option<ColumnDefaultConstraint>> {
412 let Some(default) = &hint.default else {
413 return Ok(None);
414 };
415
416 let data_type = json_type_hint_concrete_data_type(&hint.data_type)?;
417 let opts = [ColumnOptionDef {
418 name: None,
419 option: ColumnOption::Default(default.clone()),
420 }];
421
422 let json_path = hint.path.join(".");
424 let default_constraint = parse_column_default_constraint(&json_path, &data_type, &opts, None)
425 .context(crate::error::SqlCommonSnafu)?;
426
427 if let Some(constraint) = &default_constraint {
428 constraint
429 .validate(&data_type, hint.nullable)
430 .map_err(|e| {
431 InvalidSqlSnafu {
432 msg: format!("invalid DEFAULT for JSON2 type hint '{}': {e}", json_path),
433 }
434 .build()
435 })?;
436 }
437
438 Ok(default_constraint)
439}
440
441fn json_type_hint_concrete_data_type(data_type: &DataType) -> Result<ConcreteDataType> {
442 let data_type = sql_data_type_to_concrete_data_type(data_type)?;
443 normalize_json_type_hint_concrete_data_type(&data_type)
444}
445
446fn normalize_json_type_hint_concrete_data_type(
447 data_type: &ConcreteDataType,
448) -> Result<ConcreteDataType> {
449 let normalized = match data_type {
450 ConcreteDataType::String(_) => ConcreteDataType::string_datatype(),
451 ConcreteDataType::Int8(_)
452 | ConcreteDataType::Int16(_)
453 | ConcreteDataType::Int32(_)
454 | ConcreteDataType::Int64(_) => ConcreteDataType::int64_datatype(),
455 ConcreteDataType::UInt8(_)
456 | ConcreteDataType::UInt16(_)
457 | ConcreteDataType::UInt32(_)
458 | ConcreteDataType::UInt64(_) => ConcreteDataType::uint64_datatype(),
459 ConcreteDataType::Float32(_) | ConcreteDataType::Float64(_) => {
460 ConcreteDataType::float64_datatype()
461 }
462 ConcreteDataType::Boolean(_) => ConcreteDataType::boolean_datatype(),
463 _ => {
464 return InvalidSqlSnafu {
465 msg: format!("unsupported JSON2 type hint data type: {data_type}"),
466 }
467 .fail();
468 }
469 };
470 Ok(normalized)
471}
472
473fn json_type_hint_sql_data_type(data_type: &ConcreteDataType) -> Result<DataType> {
474 let data_type = normalize_json_type_hint_concrete_data_type(data_type)?;
475 let sql_type = match data_type {
476 ConcreteDataType::String(_) => DataType::String(None),
477 ConcreteDataType::Int64(_) => DataType::BigInt(None),
478 ConcreteDataType::UInt64(_) => DataType::BigIntUnsigned(None),
479 ConcreteDataType::Float64(_) => DataType::Double(sqlparser::ast::ExactNumberInfo::None),
480 ConcreteDataType::Boolean(_) => DataType::Boolean,
481 _ => unreachable!("JSON2 type hint data type should have been normalized"),
482 };
483 Ok(sql_type)
484}
485
486fn column_default_constraint_to_expr(constraint: &ColumnDefaultConstraint) -> Result<Expr> {
487 match constraint {
488 ColumnDefaultConstraint::Value(value) => Ok(Expr::Value(value_to_sql_value(value)?.into())),
489 ColumnDefaultConstraint::Function(function) => {
490 ParserContext::parse_function(function, &GreptimeDbDialect {})
491 }
492 }
493}
494
495fn format_json_type_hint(hint: &JsonTypeHint) -> String {
496 let path = hint
497 .path
498 .iter()
499 .map(|segment| format_json_path_segment(segment))
500 .join(".");
501 let nullability = if hint.nullable { " NULL" } else { " NOT NULL" };
502 let default = hint
503 .default
504 .as_ref()
505 .map(|expr| format!(" DEFAULT {expr}"))
506 .unwrap_or_default();
507 let inverted_index = if hint.inverted_index {
508 " INVERTED INDEX"
509 } else {
510 ""
511 };
512 format!(
513 "{} {}{}{}{}",
514 path, hint.data_type, nullability, default, inverted_index
515 )
516}
517
518fn format_json_path_segment(segment: &str) -> String {
519 format!("\"{}\"", segment.replace('"', "\"\""))
520}
521
522#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
529pub struct Partitions {
530 pub column_list: Vec<Ident>,
531 pub exprs: Vec<Expr>,
532}
533
534impl Partitions {
535 pub fn set_quote(&mut self, quote_style: char) {
537 self.column_list
538 .iter_mut()
539 .for_each(|c| c.quote_style = Some(quote_style));
540 }
541}
542
543#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut)]
544pub struct PartitionEntry {
545 pub name: Ident,
546 pub value_list: Vec<SqlValue>,
547}
548
549impl Display for PartitionEntry {
550 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
551 write!(
552 f,
553 "PARTITION {} VALUES LESS THAN ({})",
554 self.name,
555 format_list_comma!(self.value_list),
556 )
557 }
558}
559
560impl Display for Partitions {
561 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
562 if !self.column_list.is_empty() {
563 write!(
564 f,
565 "PARTITION ON COLUMNS ({}) (\n{}\n)",
566 format_list_comma!(self.column_list),
567 format_list_indent!(self.exprs),
568 )?;
569 }
570 Ok(())
571 }
572}
573
574impl Display for CreateTable {
575 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
576 write!(f, "CREATE ")?;
577 if self.engine == FILE_ENGINE {
578 write!(f, "EXTERNAL ")?;
579 }
580 write!(f, "TABLE ")?;
581 if self.if_not_exists {
582 write!(f, "IF NOT EXISTS ")?;
583 }
584 writeln!(f, "{} (", &self.name)?;
585 writeln!(f, "{},", format_list_indent!(self.columns))?;
586 writeln!(f, "{}", format_table_constraint(&self.constraints))?;
587 writeln!(f, ")")?;
588 if let Some(partitions) = &self.partitions {
589 writeln!(f, "{partitions}")?;
590 }
591 writeln!(f, "ENGINE={}", &self.engine)?;
592 if !self.options.is_empty() {
593 let options = self.options.kv_pairs();
594 write!(f, "WITH(\n{}\n)", format_list_indent!(options))?;
595 }
596 Ok(())
597 }
598}
599
600#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
601pub struct CreateDatabase {
602 pub name: ObjectName,
603 pub if_not_exists: bool,
605 pub options: OptionMap,
606}
607
608impl CreateDatabase {
609 pub fn new(name: ObjectName, if_not_exists: bool, options: OptionMap) -> Self {
611 Self {
612 name,
613 if_not_exists,
614 options,
615 }
616 }
617}
618
619impl Display for CreateDatabase {
620 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
621 write!(f, "CREATE DATABASE ")?;
622 if self.if_not_exists {
623 write!(f, "IF NOT EXISTS ")?;
624 }
625 write!(f, "{}", &self.name)?;
626 if !self.options.is_empty() {
627 let options = self.options.kv_pairs();
628 write!(f, "\nWITH(\n{}\n)", format_list_indent!(options))?;
629 }
630 Ok(())
631 }
632}
633
634#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
635pub struct CreateExternalTable {
636 pub name: ObjectName,
638 pub columns: Vec<Column>,
639 pub constraints: Vec<TableConstraint>,
640 pub options: OptionMap,
642 pub if_not_exists: bool,
643 pub engine: String,
644}
645
646impl Display for CreateExternalTable {
647 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
648 write!(f, "CREATE EXTERNAL TABLE ")?;
649 if self.if_not_exists {
650 write!(f, "IF NOT EXISTS ")?;
651 }
652 writeln!(f, "{} (", &self.name)?;
653 writeln!(f, "{},", format_list_indent!(self.columns))?;
654 writeln!(f, "{}", format_table_constraint(&self.constraints))?;
655 writeln!(f, ")")?;
656 writeln!(f, "ENGINE={}", &self.engine)?;
657 if !self.options.is_empty() {
658 let options = self.options.kv_pairs();
659 write!(f, "WITH(\n{}\n)", format_list_indent!(options))?;
660 }
661 Ok(())
662 }
663}
664
665#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
666pub struct CreateTableLike {
667 pub table_name: ObjectName,
669 pub source_name: ObjectName,
671}
672
673impl Display for CreateTableLike {
674 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
675 let table_name = &self.table_name;
676 let source_name = &self.source_name;
677 write!(f, r#"CREATE TABLE {table_name} LIKE {source_name}"#)
678 }
679}
680
681#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
682pub struct CreateFlow {
683 pub flow_name: ObjectName,
685 pub sink_table_name: ObjectName,
687 pub or_replace: bool,
689 pub if_not_exists: bool,
691 pub expire_after: Option<i64>,
694 pub eval_interval: Option<i64>,
698 pub comment: Option<String>,
700 pub flow_options: OptionMap,
702 pub query: Box<SqlOrTql>,
704}
705
706#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
708pub enum SqlOrTql {
709 Sql(GtQuery, String),
710 Tql(Tql, String),
711}
712
713impl std::fmt::Display for SqlOrTql {
714 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
715 match self {
716 Self::Sql(_, s) => write!(f, "{}", s),
717 Self::Tql(_, s) => write!(f, "{}", s),
718 }
719 }
720}
721
722impl SqlOrTql {
723 pub fn try_from_statement(
724 value: Statement,
725 original_query: &str,
726 ) -> std::result::Result<Self, crate::error::Error> {
727 match value {
728 Statement::Query(query) => Ok(Self::Sql(*query, original_query.to_string())),
729 Statement::Tql(tql) => Ok(Self::Tql(tql, original_query.to_string())),
730 _ => InvalidFlowQuerySnafu {
731 reason: format!("Expect either sql query or promql query, found {:?}", value),
732 }
733 .fail(),
734 }
735 }
736}
737
738impl Display for CreateFlow {
739 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
740 write!(f, "CREATE ")?;
741 if self.or_replace {
742 write!(f, "OR REPLACE ")?;
743 }
744 write!(f, "FLOW ")?;
745 if self.if_not_exists {
746 write!(f, "IF NOT EXISTS ")?;
747 }
748 writeln!(f, "{}", &self.flow_name)?;
749 writeln!(f, "SINK TO {}", &self.sink_table_name)?;
750 if let Some(expire_after) = &self.expire_after {
751 writeln!(f, "EXPIRE AFTER '{} s'", expire_after)?;
752 }
753 if let Some(eval_interval) = &self.eval_interval {
754 writeln!(f, "EVAL INTERVAL '{} s'", eval_interval)?;
755 }
756 if let Some(comment) = &self.comment {
757 writeln!(f, "COMMENT '{}'", comment)?;
758 }
759 if !self.flow_options.is_empty() {
760 let options = self.flow_options.kv_pairs();
761 writeln!(f, "WITH ({})", format_list_comma!(options))?;
762 }
763 write!(f, "AS {}", &self.query)
764 }
765}
766
767#[derive(Debug, PartialEq, Eq, Clone, Visit, VisitMut, Serialize)]
769pub struct CreateView {
770 pub name: ObjectName,
772 pub columns: Vec<Ident>,
774 pub query: Box<Statement>,
777 pub or_replace: bool,
779 pub if_not_exists: bool,
781}
782
783impl Display for CreateView {
784 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
785 write!(f, "CREATE ")?;
786 if self.or_replace {
787 write!(f, "OR REPLACE ")?;
788 }
789 write!(f, "VIEW ")?;
790 if self.if_not_exists {
791 write!(f, "IF NOT EXISTS ")?;
792 }
793 write!(f, "{} ", &self.name)?;
794 if !self.columns.is_empty() {
795 write!(f, "({}) ", format_list_comma!(self.columns))?;
796 }
797 write!(f, "AS {}", &self.query)
798 }
799}
800
801#[cfg(test)]
802mod tests {
803 use std::assert_matches;
804
805 use datatypes::json::{JsonSettings, JsonTypeHint as DatatypeJsonTypeHint};
806 use datatypes::prelude::ConcreteDataType;
807 use datatypes::schema::ColumnDefaultConstraint;
808 use datatypes::value::Value;
809
810 use super::*;
811 use crate::dialect::GreptimeDbDialect;
812 use crate::error::Error;
813 use crate::parser::{ParseOptions, ParserContext};
814 use crate::statements::statement::Statement;
815
816 #[test]
817 fn test_display_create_table() {
818 let sql = r"create table if not exists demo(
819 host string,
820 ts timestamp,
821 cpu double default 0,
822 memory double,
823 TIME INDEX (ts),
824 PRIMARY KEY(host)
825 )
826 PARTITION ON COLUMNS (host) (
827 host = 'a',
828 host > 'a',
829 )
830 engine=mito
831 with(ttl='7d', storage='File');
832 ";
833 let result =
834 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
835 .unwrap();
836 assert_eq!(1, result.len());
837
838 match &result[0] {
839 Statement::CreateTable(c) => {
840 let new_sql = format!("\n{}", c);
841 assert_eq!(
842 r#"
843CREATE TABLE IF NOT EXISTS demo (
844 host STRING,
845 ts TIMESTAMP,
846 cpu DOUBLE DEFAULT 0,
847 memory DOUBLE,
848 TIME INDEX (ts),
849 PRIMARY KEY (host)
850)
851PARTITION ON COLUMNS (host) (
852 host = 'a',
853 host > 'a'
854)
855ENGINE=mito
856WITH(
857 storage = 'File',
858 ttl = '7d'
859)"#,
860 &new_sql
861 );
862
863 let new_result = ParserContext::create_with_dialect(
864 &new_sql,
865 &GreptimeDbDialect {},
866 ParseOptions::default(),
867 )
868 .unwrap();
869 assert_eq!(result, new_result);
870 }
871 _ => unreachable!(),
872 }
873 }
874
875 #[test]
876 fn test_display_empty_partition_column() {
877 let sql = r"create table if not exists demo(
878 host string,
879 ts timestamp,
880 cpu double default 0,
881 memory double,
882 TIME INDEX (ts),
883 PRIMARY KEY(ts, host)
884 );
885 ";
886 let result =
887 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
888 .unwrap();
889 assert_eq!(1, result.len());
890
891 match &result[0] {
892 Statement::CreateTable(c) => {
893 let new_sql = format!("\n{}", c);
894 assert_eq!(
895 r#"
896CREATE TABLE IF NOT EXISTS demo (
897 host STRING,
898 ts TIMESTAMP,
899 cpu DOUBLE DEFAULT 0,
900 memory DOUBLE,
901 TIME INDEX (ts),
902 PRIMARY KEY (ts, host)
903)
904ENGINE=mito
905"#,
906 &new_sql
907 );
908
909 let new_result = ParserContext::create_with_dialect(
910 &new_sql,
911 &GreptimeDbDialect {},
912 ParseOptions::default(),
913 )
914 .unwrap();
915 assert_eq!(result, new_result);
916 }
917 _ => unreachable!(),
918 }
919 }
920
921 #[test]
922 fn test_validate_table_options() {
923 let sql = r"create table if not exists demo(
924 host string,
925 ts timestamp,
926 cpu double default 0,
927 memory double,
928 TIME INDEX (ts),
929 PRIMARY KEY(host)
930 )
931 PARTITION ON COLUMNS (host) ()
932 engine=mito
933 with(ttl='7d', 'compaction.type'='world');
934";
935 let result =
936 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
937 .unwrap();
938 match &result[0] {
939 Statement::CreateTable(c) => {
940 assert_eq!(2, c.options.len());
941 }
942 _ => unreachable!(),
943 }
944
945 let sql = r"create table if not exists demo(
946 host string,
947 ts timestamp,
948 cpu double default 0,
949 memory double,
950 TIME INDEX (ts),
951 PRIMARY KEY(host)
952 )
953 PARTITION ON COLUMNS (host) ()
954 engine=mito
955 with(ttl='7d', hello='world');
956";
957 let result =
958 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
959 assert_matches!(result, Err(Error::InvalidTableOption { .. }));
960
961 let semantic = |with: &str| {
963 let sql =
964 format!("create table demo(host string, ts timestamp time index) with({with});");
965 ParserContext::create_with_dialect(&sql, &GreptimeDbDialect {}, ParseOptions::default())
966 };
967 assert!(semantic("'greptime.semantic.signal_type'='metric'").is_ok());
968 assert_matches!(
970 semantic("'greptime.semantic.signal_type'='spans'"),
971 Err(Error::InvalidTableOption { .. })
972 );
973 assert_matches!(
975 semantic("'greptime.semantic.bogus'='x'"),
976 Err(Error::InvalidTableOption { .. })
977 );
978 }
979
980 #[test]
981 fn test_display_json2_type_hints_quotes_path_segments() {
982 let sql = r#"CREATE TABLE traces (
983 log_json_data JSON2 (
984 "service.name" STRING,
985 "a.b"."c" INT64 NOT NULL,
986 a."b.c" STRING
987 ),
988 ts TIMESTAMP TIME INDEX
989 )"#;
990 let result =
991 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
992 .unwrap();
993
994 match &result[0] {
995 Statement::CreateTable(c) => {
996 let new_sql = format!("\n{}", c);
997 assert_eq!(
998 r#"
999CREATE TABLE traces (
1000 log_json_data JSON2(
1001 "service.name" STRING NULL,
1002 "a.b"."c" BIGINT NOT NULL,
1003 "a"."b.c" STRING NULL
1004 ),
1005 ts TIMESTAMP NOT NULL,
1006 TIME INDEX (ts)
1007)
1008ENGINE=mito
1009"#,
1010 &new_sql
1011 );
1012
1013 let new_result = ParserContext::create_with_dialect(
1014 &new_sql,
1015 &GreptimeDbDialect {},
1016 ParseOptions::default(),
1017 )
1018 .unwrap();
1019 assert_eq!(result, new_result);
1020 }
1021 _ => unreachable!(),
1022 }
1023 }
1024
1025 #[test]
1026 fn test_parse_json2_max_auto_expanded_paths_option() -> Result<()> {
1027 let sql = r#"CREATE TABLE traces (
1028 log_json_data JSON2 (
1029 status_code INT64 NOT NULL,
1030 max_auto_expanded_paths = 1
1031 ),
1032 ts TIMESTAMP TIME INDEX
1033 )"#;
1034 let result = ParserContext::create_with_dialect(
1035 sql,
1036 &GreptimeDbDialect {},
1037 ParseOptions::default(),
1038 )?;
1039 let Statement::CreateTable(create_table) = &result[0] else {
1040 unreachable!()
1041 };
1042 let settings = create_table.columns[0]
1043 .extensions
1044 .build_json_settings()?
1045 .unwrap();
1046 assert_eq!(settings.max_auto_expanded_paths(), Some(1));
1047 Ok(())
1048 }
1049
1050 #[test]
1051 fn test_display_json2_type_hints_quotes_numeric_segments() {
1052 let sql = r#"CREATE TABLE traces (
1053 log_json_data JSON2 (
1054 "1abc" STRING,
1055 a."2b" INT64 NOT NULL
1056 ),
1057 ts TIMESTAMP TIME INDEX
1058 )"#;
1059 let result =
1060 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1061 .unwrap();
1062
1063 match &result[0] {
1064 Statement::CreateTable(c) => {
1065 let new_sql = format!("\n{}", c);
1066 assert_eq!(
1067 r#"
1068CREATE TABLE traces (
1069 log_json_data JSON2(
1070 "1abc" STRING NULL,
1071 "a"."2b" BIGINT NOT NULL
1072 ),
1073 ts TIMESTAMP NOT NULL,
1074 TIME INDEX (ts)
1075)
1076ENGINE=mito
1077"#,
1078 &new_sql
1079 );
1080
1081 let new_result = ParserContext::create_with_dialect(
1082 &new_sql,
1083 &GreptimeDbDialect {},
1084 ParseOptions::default(),
1085 )
1086 .unwrap();
1087 assert_eq!(result, new_result);
1088 }
1089 _ => unreachable!(),
1090 }
1091 }
1092
1093 #[test]
1094 fn test_json2_type_hint_default_builds_default_constraint() {
1095 let sql = r#"CREATE TABLE traces (
1096 log_json_data JSON2 (
1097 status_code INT64 DEFAULT -5,
1098 duration FLOAT64 DEFAULT +1.5,
1099 error BOOLEAN DEFAULT false,
1100 message STRING DEFAULT 'unknown'
1101 ),
1102 ts TIMESTAMP TIME INDEX
1103 )"#;
1104 let result =
1105 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1106 .unwrap();
1107
1108 let Statement::CreateTable(create_table) = &result[0] else {
1109 unreachable!()
1110 };
1111 let settings = create_table.columns[0]
1112 .extensions
1113 .build_json_settings()
1114 .unwrap()
1115 .unwrap();
1116 let hints = settings.type_hints();
1117
1118 assert_eq!(hints[0].data_type, ConcreteDataType::int64_datatype());
1119 assert_eq!(
1120 hints[0].default_constraint,
1121 Some(ColumnDefaultConstraint::Value(Value::Int64(-5)))
1122 );
1123 assert_eq!(hints[1].data_type, ConcreteDataType::float64_datatype());
1124 assert_eq!(
1125 hints[1].default_constraint,
1126 Some(ColumnDefaultConstraint::Value(Value::Float64(1.5.into())))
1127 );
1128 assert_eq!(hints[2].data_type, ConcreteDataType::boolean_datatype());
1129 assert_eq!(
1130 hints[2].default_constraint,
1131 Some(ColumnDefaultConstraint::Value(Value::Boolean(false)))
1132 );
1133 assert_eq!(hints[3].data_type, ConcreteDataType::string_datatype());
1134 assert_eq!(
1135 hints[3].default_constraint,
1136 Some(ColumnDefaultConstraint::Value(Value::String(
1137 "unknown".into()
1138 )))
1139 );
1140 }
1141
1142 #[test]
1143 fn test_json2_type_hint_not_null_default_null_is_rejected() {
1144 let sql = r#"CREATE TABLE traces (
1145 log_json_data JSON2 (
1146 status_code INT64 NOT NULL DEFAULT NULL
1147 ),
1148 ts TIMESTAMP TIME INDEX
1149 )"#;
1150 let result =
1151 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1152 .unwrap();
1153
1154 let Statement::CreateTable(create_table) = &result[0] else {
1155 unreachable!()
1156 };
1157 let err = create_table.columns[0]
1158 .extensions
1159 .build_json_settings()
1160 .unwrap_err();
1161 assert!(
1162 err.to_string()
1163 .contains("Default value should not be null for non null column")
1164 );
1165 }
1166
1167 #[test]
1168 fn test_set_json_settings_normalizes_type_hint_sql_types() -> Result<()> {
1169 let mut extensions = super::ColumnExtensions::default();
1170 let settings = JsonSettings::try_new(
1171 vec![
1172 DatatypeJsonTypeHint {
1173 path: vec!["i".to_string()],
1174 data_type: ConcreteDataType::int32_datatype(),
1175 nullable: true,
1176 default_constraint: None,
1177 inverted_index: false,
1178 },
1179 DatatypeJsonTypeHint {
1180 path: vec!["f".to_string()],
1181 data_type: ConcreteDataType::float32_datatype(),
1182 nullable: true,
1183 default_constraint: None,
1184 inverted_index: false,
1185 },
1186 DatatypeJsonTypeHint {
1187 path: vec!["u".to_string()],
1188 data_type: ConcreteDataType::uint32_datatype(),
1189 nullable: true,
1190 default_constraint: None,
1191 inverted_index: false,
1192 },
1193 DatatypeJsonTypeHint {
1194 path: vec!["s".to_string()],
1195 data_type: ConcreteDataType::string_datatype(),
1196 nullable: true,
1197 default_constraint: None,
1198 inverted_index: false,
1199 },
1200 DatatypeJsonTypeHint {
1201 path: vec!["b".to_string()],
1202 data_type: ConcreteDataType::boolean_datatype(),
1203 nullable: true,
1204 default_constraint: None,
1205 inverted_index: false,
1206 },
1207 ],
1208 None,
1209 )?;
1210 extensions.set_json_settings(settings)?;
1211
1212 assert_eq!(
1213 extensions
1214 .json2_options
1215 .unwrap()
1216 .type_hints
1217 .iter()
1218 .map(|hint| hint.data_type.to_string())
1219 .collect::<Vec<_>>(),
1220 vec!["BIGINT", "DOUBLE", "BIGINT UNSIGNED", "STRING", "BOOLEAN"]
1221 );
1222 Ok(())
1223 }
1224
1225 #[test]
1226 fn test_set_json_settings_rejects_unsupported_type_hint_type() -> Result<()> {
1227 let err = JsonSettings::try_new(
1228 vec![DatatypeJsonTypeHint {
1229 path: vec!["u".to_string()],
1230 data_type: ConcreteDataType::date_datatype(),
1231 nullable: true,
1232 default_constraint: None,
1233 inverted_index: false,
1234 }],
1235 None,
1236 )
1237 .unwrap_err();
1238
1239 assert!(
1240 err.to_string()
1241 .contains("unsupported JSON2 type hint data type")
1242 );
1243 Ok(())
1244 }
1245
1246 #[test]
1247 fn test_set_empty_json_settings_omits_json2_options() -> Result<()> {
1248 let mut extensions = ColumnExtensions::default();
1249 extensions.set_json_settings(JsonSettings::default())?;
1250 assert!(extensions.json2_options.is_none());
1251 Ok(())
1252 }
1253
1254 #[test]
1255 fn test_display_create_database() {
1256 let sql = r"create database test;";
1257 let stmts =
1258 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1259 .unwrap();
1260 assert_eq!(1, stmts.len());
1261 assert_matches!(&stmts[0], Statement::CreateDatabase { .. });
1262
1263 match &stmts[0] {
1264 Statement::CreateDatabase(set) => {
1265 let new_sql = format!("\n{}", set);
1266 assert_eq!(
1267 r#"
1268CREATE DATABASE test"#,
1269 &new_sql
1270 );
1271 }
1272 _ => {
1273 unreachable!();
1274 }
1275 }
1276
1277 let sql = r"create database if not exists test;";
1278 let stmts =
1279 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1280 .unwrap();
1281 assert_eq!(1, stmts.len());
1282 assert_matches!(&stmts[0], Statement::CreateDatabase { .. });
1283
1284 match &stmts[0] {
1285 Statement::CreateDatabase(set) => {
1286 let new_sql = format!("\n{}", set);
1287 assert_eq!(
1288 r#"
1289CREATE DATABASE IF NOT EXISTS test"#,
1290 &new_sql
1291 );
1292 }
1293 _ => {
1294 unreachable!();
1295 }
1296 }
1297
1298 let sql = r#"CREATE DATABASE IF NOT EXISTS test WITH (ttl='1h');"#;
1299 let stmts =
1300 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1301 .unwrap();
1302 assert_eq!(1, stmts.len());
1303 assert_matches!(&stmts[0], Statement::CreateDatabase { .. });
1304
1305 match &stmts[0] {
1306 Statement::CreateDatabase(set) => {
1307 let new_sql = format!("\n{}", set);
1308 assert_eq!(
1309 r#"
1310CREATE DATABASE IF NOT EXISTS test
1311WITH(
1312 ttl = '1h'
1313)"#,
1314 &new_sql
1315 );
1316 }
1317 _ => {
1318 unreachable!();
1319 }
1320 }
1321 }
1322
1323 #[test]
1324 fn test_display_create_table_like() {
1325 let sql = r"create table t2 like t1;";
1326 let stmts =
1327 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1328 .unwrap();
1329 assert_eq!(1, stmts.len());
1330 assert_matches!(&stmts[0], Statement::CreateTableLike { .. });
1331
1332 match &stmts[0] {
1333 Statement::CreateTableLike(create) => {
1334 let new_sql = format!("\n{}", create);
1335 assert_eq!(
1336 r#"
1337CREATE TABLE t2 LIKE t1"#,
1338 &new_sql
1339 );
1340 }
1341 _ => {
1342 unreachable!();
1343 }
1344 }
1345 }
1346
1347 #[test]
1348 fn test_display_create_external_table() {
1349 let sql = r#"CREATE EXTERNAL TABLE city (
1350 host string,
1351 ts timestamp,
1352 cpu float64 default 0,
1353 memory float64,
1354 TIME INDEX (ts),
1355 PRIMARY KEY(host)
1356) WITH (location='/var/data/city.csv', format='csv');"#;
1357 let stmts =
1358 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1359 .unwrap();
1360 assert_eq!(1, stmts.len());
1361 assert_matches!(&stmts[0], Statement::CreateExternalTable { .. });
1362
1363 match &stmts[0] {
1364 Statement::CreateExternalTable(create) => {
1365 let new_sql = format!("\n{}", create);
1366 assert_eq!(
1367 r#"
1368CREATE EXTERNAL TABLE city (
1369 host STRING,
1370 ts TIMESTAMP,
1371 cpu DOUBLE DEFAULT 0,
1372 memory DOUBLE,
1373 TIME INDEX (ts),
1374 PRIMARY KEY (host)
1375)
1376ENGINE=file
1377WITH(
1378 format = 'csv',
1379 location = '/var/data/city.csv'
1380)"#,
1381 &new_sql
1382 );
1383 }
1384 _ => {
1385 unreachable!();
1386 }
1387 }
1388 }
1389
1390 #[test]
1391 fn test_display_create_flow() {
1392 let sql = r"CREATE FLOW filter_numbers
1393 SINK TO out_num_cnt
1394 AS SELECT number FROM numbers_input where number > 10;";
1395 let result =
1396 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1397 .unwrap();
1398 assert_eq!(1, result.len());
1399
1400 match &result[0] {
1401 Statement::CreateFlow(c) => {
1402 let new_sql = format!("\n{}", c);
1403 assert_eq!(
1404 r#"
1405CREATE FLOW filter_numbers
1406SINK TO out_num_cnt
1407AS SELECT number FROM numbers_input where number > 10"#,
1408 &new_sql
1409 );
1410
1411 let new_result = ParserContext::create_with_dialect(
1412 &new_sql,
1413 &GreptimeDbDialect {},
1414 ParseOptions::default(),
1415 )
1416 .unwrap();
1417 assert_eq!(result, new_result);
1418 }
1419 _ => unreachable!(),
1420 }
1421 }
1422
1423 #[test]
1424 fn test_vector_index_options_validation() {
1425 use super::{ColumnExtensions, OptionMap};
1426
1427 let extensions = ColumnExtensions {
1429 vector_index_options: Some(OptionMap::from([(
1430 "connectivity".to_string(),
1431 "0".to_string(),
1432 )])),
1433 ..Default::default()
1434 };
1435 let result = extensions.build_vector_index_options();
1436 assert!(result.is_err());
1437 assert!(
1438 result
1439 .unwrap_err()
1440 .to_string()
1441 .contains("connectivity must be in the range [2, 2048]")
1442 );
1443
1444 let extensions = ColumnExtensions {
1446 vector_index_options: Some(OptionMap::from([(
1447 "expansion_add".to_string(),
1448 "0".to_string(),
1449 )])),
1450 ..Default::default()
1451 };
1452 let result = extensions.build_vector_index_options();
1453 assert!(result.is_err());
1454 assert!(
1455 result
1456 .unwrap_err()
1457 .to_string()
1458 .contains("expansion_add must be greater than 0")
1459 );
1460
1461 let extensions = ColumnExtensions {
1463 vector_index_options: Some(OptionMap::from([(
1464 "expansion_search".to_string(),
1465 "0".to_string(),
1466 )])),
1467 ..Default::default()
1468 };
1469 let result = extensions.build_vector_index_options();
1470 assert!(result.is_err());
1471 assert!(
1472 result
1473 .unwrap_err()
1474 .to_string()
1475 .contains("expansion_search must be greater than 0")
1476 );
1477
1478 let extensions = ColumnExtensions {
1480 vector_index_options: Some(OptionMap::from([
1481 ("connectivity".to_string(), "32".to_string()),
1482 ("expansion_add".to_string(), "200".to_string()),
1483 ("expansion_search".to_string(), "100".to_string()),
1484 ])),
1485 ..Default::default()
1486 };
1487 let result = extensions.build_vector_index_options();
1488 assert!(result.is_ok());
1489 let options = result.unwrap().unwrap();
1490 assert_eq!(options.connectivity, 32);
1491 assert_eq!(options.expansion_add, 200);
1492 assert_eq!(options.expansion_search, 100);
1493 }
1494}