1#[cfg(feature = "enterprise")]
16pub mod trigger;
17
18use std::collections::HashMap;
19
20use common_query::AddColumnLocation;
21use datatypes::schema::COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE;
22use snafu::{ResultExt, ensure};
23use sqlparser::ast::Ident;
24use sqlparser::keywords::Keyword;
25use sqlparser::parser::{Parser, ParserError};
26use sqlparser::tokenizer::{Token, TokenWithSpan};
27
28use crate::ast::ObjectNamePartExt;
29use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnafu};
30use crate::parser::ParserContext;
31use crate::parsers::create_parser::INVERTED;
32use crate::parsers::utils::{
33 validate_column_fulltext_create_option, validate_column_skipping_index_create_option,
34};
35use crate::statements::alter::{
36 AddColumn, AlterDatabase, AlterDatabaseOperation, AlterTable, AlterTableOperation,
37 DropDefaultsOperation, KeyValueOption, SetDefaultsOperation, SetIndexOperation,
38 UnsetIndexOperation,
39};
40use crate::statements::statement::Statement;
41use crate::util::parse_option_string;
42
43impl ParserContext<'_> {
44 pub(crate) fn parse_alter(&mut self) -> Result<Statement> {
45 let _ = self.parser.expect_keyword(Keyword::ALTER);
46 match self.parser.peek_token().token {
47 Token::Word(w) => match w.keyword {
48 Keyword::DATABASE => self.parse_alter_database().map(Statement::AlterDatabase),
49 Keyword::TABLE => self.parse_alter_table().map(Statement::AlterTable),
50 #[cfg(feature = "enterprise")]
51 Keyword::TRIGGER => {
52 self.parser.next_token();
53 self.parse_alter_trigger()
54 }
55 _ => self.expected("DATABASE or TABLE after ALTER", self.parser.peek_token()),
56 },
57 unexpected => self.unsupported(unexpected.to_string()),
58 }
59 }
60
61 fn parse_alter_database(&mut self) -> Result<AlterDatabase> {
62 self.parser
63 .expect_keyword(Keyword::DATABASE)
64 .context(error::SyntaxSnafu)?;
65
66 let database_name = self
67 .parser
68 .parse_object_name(false)
69 .context(error::SyntaxSnafu)?;
70 let database_name = Self::canonicalize_object_name(database_name);
71
72 match self.parser.peek_token().token {
73 Token::Word(w) => {
74 if w.value.eq_ignore_ascii_case("UNSET") {
75 let _ = self.parser.next_token();
76 let keys = self
77 .parser
78 .parse_comma_separated(parse_string_option_names)
79 .context(error::SyntaxSnafu)?
80 .into_iter()
81 .collect();
82 Ok(AlterDatabase::new(
83 database_name,
84 AlterDatabaseOperation::UnsetDatabaseOption { keys },
85 ))
86 } else if w.keyword == Keyword::SET {
87 let _ = self.parser.next_token();
88 let options = self
89 .parser
90 .parse_comma_separated(parse_string_options)
91 .context(error::SyntaxSnafu)?
92 .into_iter()
93 .map(|(key, value)| KeyValueOption { key, value })
94 .collect();
95 Ok(AlterDatabase::new(
96 database_name,
97 AlterDatabaseOperation::SetDatabaseOption { options },
98 ))
99 } else {
100 self.expected(
101 "SET or UNSET after ALTER DATABASE",
102 self.parser.peek_token(),
103 )
104 }
105 }
106 unexpected => self.unsupported(unexpected.to_string()),
107 }
108 }
109
110 fn parse_alter_table(&mut self) -> Result<AlterTable> {
111 self.parser
112 .expect_keyword(Keyword::TABLE)
113 .context(error::SyntaxSnafu)?;
114
115 let raw_table_name = self
116 .parser
117 .parse_object_name(false)
118 .context(error::SyntaxSnafu)?;
119 let table_name = Self::canonicalize_object_name(raw_table_name);
120
121 let alter_operation = match self.parser.peek_token().token {
122 Token::Word(w) => {
123 if w.value.eq_ignore_ascii_case("MODIFY") {
124 self.parse_alter_table_modify()?
125 } else if w.value.eq_ignore_ascii_case("UNSET") {
126 self.parse_alter_table_unset()?
127 } else {
128 match w.keyword {
129 Keyword::ADD => self.parse_alter_table_add()?,
130 Keyword::DROP => {
131 let _ = self.parser.next_token();
132 self.parser
133 .expect_keyword(Keyword::COLUMN)
134 .context(error::SyntaxSnafu)?;
135 let name = Self::canonicalize_identifier(
136 self.parser.parse_identifier().context(error::SyntaxSnafu)?,
137 );
138 AlterTableOperation::DropColumn { name }
139 }
140 Keyword::RENAME => {
141 let _ = self.parser.next_token();
142 let new_table_name_obj_raw =
143 self.parse_object_name().context(error::SyntaxSnafu)?;
144 let new_table_name_obj =
145 Self::canonicalize_object_name(new_table_name_obj_raw);
146 let new_table_name = match &new_table_name_obj.0[..] {
147 [table] => table.to_string_unquoted(),
148 _ => {
149 return Err(ParserError::ParserError(format!(
150 "expect table name, actual: {new_table_name_obj}"
151 )))
152 .context(error::SyntaxSnafu);
153 }
154 };
155 AlterTableOperation::RenameTable { new_table_name }
156 }
157 Keyword::SET => {
158 let _ = self.parser.next_token();
159 let options = self
160 .parser
161 .parse_comma_separated(parse_string_options)
162 .context(error::SyntaxSnafu)?
163 .into_iter()
164 .map(|(key, value)| KeyValueOption { key, value })
165 .collect();
166 AlterTableOperation::SetTableOptions { options }
167 }
168 _ => self.expected(
169 "ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE",
170 self.parser.peek_token(),
171 )?,
172 }
173 }
174 }
175 unexpected => self.unsupported(unexpected.to_string())?,
176 };
177 Ok(AlterTable::new(table_name, alter_operation))
178 }
179
180 fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
181 let _ = self.parser.next_token();
182 let keys = self
183 .parser
184 .parse_comma_separated(parse_string_option_names)
185 .context(error::SyntaxSnafu)?
186 .into_iter()
187 .collect();
188
189 Ok(AlterTableOperation::UnsetTableOptions { keys })
190 }
191
192 fn parse_alter_table_add(&mut self) -> Result<AlterTableOperation> {
193 let _ = self.parser.next_token();
194 if let Some(constraint) = self
195 .parser
196 .parse_optional_table_constraint()
197 .context(error::SyntaxSnafu)?
198 {
199 Ok(AlterTableOperation::AddConstraint(constraint))
200 } else {
201 self.parser.prev_token();
202 let add_columns = self
203 .parser
204 .parse_comma_separated(parse_add_columns)
205 .context(error::SyntaxSnafu)?;
206 Ok(AlterTableOperation::AddColumns { add_columns })
207 }
208 }
209
210 fn parse_alter_table_drop_default(
211 &mut self,
212 column_name: Ident,
213 ) -> Result<AlterTableOperation> {
214 let drop_default = DropDefaultsOperation(column_name);
215 if self.parser.consume_token(&Token::Comma) {
216 let mut columns = self
217 .parser
218 .parse_comma_separated(parse_alter_column_drop_default)
219 .context(error::SyntaxSnafu)?;
220 columns.insert(0, drop_default);
221 Ok(AlterTableOperation::DropDefaults { columns })
222 } else {
223 Ok(AlterTableOperation::DropDefaults {
224 columns: vec![drop_default],
225 })
226 }
227 }
228
229 fn parse_alter_table_set_default(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
230 let default_constraint = self.parser.parse_expr().context(error::SyntaxSnafu)?;
231 let set_default = SetDefaultsOperation {
232 column_name,
233 default_constraint,
234 };
235 if self.parser.consume_token(&Token::Comma) {
236 let mut defaults = self
237 .parser
238 .parse_comma_separated(parse_alter_column_set_default)
239 .context(error::SyntaxSnafu)?;
240 defaults.insert(0, set_default);
241 Ok(AlterTableOperation::SetDefaults { defaults })
242 } else {
243 Ok(AlterTableOperation::SetDefaults {
244 defaults: vec![set_default],
245 })
246 }
247 }
248
249 fn parse_alter_table_modify(&mut self) -> Result<AlterTableOperation> {
250 let _ = self.parser.next_token();
251 self.parser
252 .expect_keyword(Keyword::COLUMN)
253 .context(error::SyntaxSnafu)?;
254 let column_name = Self::canonicalize_identifier(
255 self.parser.parse_identifier().context(error::SyntaxSnafu)?,
256 );
257
258 match self.parser.peek_token().token {
259 Token::Word(w) => {
260 if w.value.eq_ignore_ascii_case("UNSET") {
261 self.parser.next_token();
263 self.parse_alter_column_unset_index(column_name)
264 } else if w.keyword == Keyword::SET {
265 self.parser.next_token();
267 if let Token::Word(w) = self.parser.peek_token().token
268 && matches!(w.keyword, Keyword::DEFAULT)
269 {
270 self.parser
271 .expect_keyword(Keyword::DEFAULT)
272 .context(error::SyntaxSnafu)?;
273 self.parse_alter_table_set_default(column_name)
274 } else {
275 self.parse_alter_column_set_index(column_name)
276 }
277 } else if w.keyword == Keyword::DROP {
278 self.parser.next_token();
280 self.parser
281 .expect_keyword(Keyword::DEFAULT)
282 .context(error::SyntaxSnafu)?;
283 self.parse_alter_table_drop_default(column_name)
284 } else {
285 let data_type = self.parser.parse_data_type().context(error::SyntaxSnafu)?;
286 Ok(AlterTableOperation::ModifyColumnType {
287 column_name,
288 target_type: data_type,
289 })
290 }
291 }
292 _ => self.expected(
293 "SET or UNSET or data type after MODIFY COLUMN",
294 self.parser.peek_token(),
295 )?,
296 }
297 }
298
299 fn parse_alter_column_unset_index(
300 &mut self,
301 column_name: Ident,
302 ) -> Result<AlterTableOperation> {
303 match self.parser.next_token() {
304 TokenWithSpan {
305 token: Token::Word(w),
306 ..
307 } if w.keyword == Keyword::FULLTEXT => {
308 self.parser
309 .expect_keyword(Keyword::INDEX)
310 .context(error::SyntaxSnafu)?;
311 Ok(AlterTableOperation::UnsetIndex {
312 options: UnsetIndexOperation::Fulltext { column_name },
313 })
314 }
315
316 TokenWithSpan {
317 token: Token::Word(w),
318 ..
319 } if w.value.eq_ignore_ascii_case(INVERTED) => {
320 self.parser
321 .expect_keyword(Keyword::INDEX)
322 .context(error::SyntaxSnafu)?;
323 Ok(AlterTableOperation::UnsetIndex {
324 options: UnsetIndexOperation::Inverted { column_name },
325 })
326 }
327
328 TokenWithSpan {
329 token: Token::Word(w),
330 ..
331 } if w.value.eq_ignore_ascii_case("SKIPPING") => {
332 self.parser
333 .expect_keyword(Keyword::INDEX)
334 .context(error::SyntaxSnafu)?;
335 Ok(AlterTableOperation::UnsetIndex {
336 options: UnsetIndexOperation::Skipping { column_name },
337 })
338 }
339 _ => self.expected(
340 format!(
341 "{:?} OR INVERTED INDEX OR SKIPPING INDEX",
342 Keyword::FULLTEXT
343 )
344 .as_str(),
345 self.parser.peek_token(),
346 ),
347 }
348 }
349
350 fn parse_alter_column_set_index(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
351 match self.parser.next_token() {
352 TokenWithSpan {
353 token: Token::Word(w),
354 ..
355 } if w.keyword == Keyword::FULLTEXT => {
356 self.parser
357 .expect_keyword(Keyword::INDEX)
358 .context(error::SyntaxSnafu)?;
359 self.parse_alter_column_fulltext(column_name)
360 }
361
362 TokenWithSpan {
363 token: Token::Word(w),
364 ..
365 } if w.value.eq_ignore_ascii_case(INVERTED) => {
366 self.parser
367 .expect_keyword(Keyword::INDEX)
368 .context(error::SyntaxSnafu)?;
369 Ok(AlterTableOperation::SetIndex {
370 options: SetIndexOperation::Inverted { column_name },
371 })
372 }
373
374 TokenWithSpan {
375 token: Token::Word(w),
376 ..
377 } if w.value.eq_ignore_ascii_case("SKIPPING") => {
378 self.parser
379 .expect_keyword(Keyword::INDEX)
380 .context(error::SyntaxSnafu)?;
381 self.parse_alter_column_skipping(column_name)
382 }
383 t => self.expected(
384 format!("{:?} OR INVERTED OR SKIPPING INDEX", Keyword::FULLTEXT).as_str(),
385 t,
386 ),
387 }
388 }
389
390 fn parse_alter_column_fulltext(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
391 let mut options = self
392 .parser
393 .parse_options(Keyword::WITH)
394 .context(error::SyntaxSnafu)?
395 .into_iter()
396 .map(parse_option_string)
397 .collect::<Result<HashMap<String, String>>>()?;
398
399 for key in options.keys() {
400 ensure!(
401 validate_column_fulltext_create_option(key),
402 InvalidColumnOptionSnafu {
403 name: column_name.to_string(),
404 msg: format!("invalid FULLTEXT option: {key}"),
405 }
406 );
407 }
408
409 options.insert(
410 COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE.to_string(),
411 "true".to_string(),
412 );
413
414 Ok(AlterTableOperation::SetIndex {
415 options: SetIndexOperation::Fulltext {
416 column_name,
417 options: options.try_into().context(SetFulltextOptionSnafu)?,
418 },
419 })
420 }
421
422 fn parse_alter_column_skipping(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
423 let options = self
424 .parser
425 .parse_options(Keyword::WITH)
426 .context(error::SyntaxSnafu)?
427 .into_iter()
428 .map(parse_option_string)
429 .collect::<Result<HashMap<String, String>>>()?;
430
431 for key in options.keys() {
432 ensure!(
433 validate_column_skipping_index_create_option(key),
434 InvalidColumnOptionSnafu {
435 name: column_name.to_string(),
436 msg: format!("invalid SKIPPING INDEX option: {key}"),
437 }
438 );
439 }
440
441 Ok(AlterTableOperation::SetIndex {
442 options: SetIndexOperation::Skipping {
443 column_name,
444 options: options
445 .try_into()
446 .context(error::SetSkippingIndexOptionSnafu)?,
447 },
448 })
449 }
450}
451
452fn parse_alter_column_drop_default(
453 parser: &mut Parser,
454) -> std::result::Result<DropDefaultsOperation, ParserError> {
455 parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
456 let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
457 let t = parser.next_token();
458 match t.token {
459 Token::Word(w) if w.keyword == Keyword::DROP => {
460 parser.expect_keyword(Keyword::DEFAULT)?;
461 Ok(DropDefaultsOperation(column_name))
462 }
463 _ => Err(ParserError::ParserError(format!(
464 "Unexpected keyword, expect DROP, got: `{t}`"
465 ))),
466 }
467}
468
469fn parse_alter_column_set_default(
470 parser: &mut Parser,
471) -> std::result::Result<SetDefaultsOperation, ParserError> {
472 parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
473 let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
474 let t = parser.next_token();
475 match t.token {
476 Token::Word(w) if w.keyword == Keyword::SET => {
477 parser.expect_keyword(Keyword::DEFAULT)?;
478 if let Ok(default_constraint) = parser.parse_expr() {
479 Ok(SetDefaultsOperation {
480 column_name,
481 default_constraint,
482 })
483 } else {
484 Err(ParserError::ParserError(format!(
485 "Invalid default value after SET DEFAULT, got: `{}`",
486 parser.peek_token()
487 )))
488 }
489 }
490 _ => Err(ParserError::ParserError(format!(
491 "Unexpected keyword, expect SET, got: `{t}`"
492 ))),
493 }
494}
495
496fn parse_string_options(parser: &mut Parser) -> std::result::Result<(String, String), ParserError> {
498 let name = parser.parse_literal_string()?;
499 parser.expect_token(&Token::Eq)?;
500 let value = if parser.parse_keyword(Keyword::NULL) {
501 "".to_string()
502 } else {
503 let next_token = parser.peek_token();
504 if let Token::Number(number_as_string, _) = next_token.token {
505 parser.advance_token();
506 number_as_string
507 } else {
508 parser.parse_literal_string().map_err(|_|{
509 ParserError::ParserError(format!("Unexpected option value for alter table statements, expect string literal, numeric literal or NULL, got: `{}`", next_token))
510 })?
511 }
512 };
513 Ok((name, value))
514}
515
516fn parse_add_columns(parser: &mut Parser) -> std::result::Result<AddColumn, ParserError> {
517 parser.expect_keyword(Keyword::ADD)?;
518 let _ = parser.parse_keyword(Keyword::COLUMN);
519 let add_if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
520 let mut column_def = parser.parse_column_def()?;
521 column_def.name = ParserContext::canonicalize_identifier(column_def.name);
522 let location = if parser.parse_keyword(Keyword::FIRST) {
523 Some(AddColumnLocation::First)
524 } else if let Token::Word(word) = parser.peek_token().token {
525 if word.value.eq_ignore_ascii_case("AFTER") {
526 let _ = parser.next_token();
527 let name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
528 Some(AddColumnLocation::After {
529 column_name: name.value,
530 })
531 } else {
532 None
533 }
534 } else {
535 None
536 };
537 Ok(AddColumn {
538 column_def,
539 location,
540 add_if_not_exists,
541 })
542}
543
544fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
546 parser.parse_literal_string()
547}
548
549#[cfg(test)]
550mod tests {
551 use std::assert_matches::assert_matches;
552
553 use common_error::ext::ErrorExt;
554 use datatypes::schema::{FulltextAnalyzer, FulltextBackend, FulltextOptions};
555 use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType};
556
557 use super::*;
558 use crate::ast::ObjectNamePartExt;
559 use crate::dialect::GreptimeDbDialect;
560 use crate::parser::ParseOptions;
561 use crate::statements::alter::AlterDatabaseOperation;
562
563 #[test]
564 fn test_parse_alter_database() {
565 let sql = "ALTER DATABASE test_db SET 'a'='A', 'b' = 'B'";
566 let mut result =
567 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
568 .unwrap();
569 assert_eq!(1, result.len());
570
571 let statement = result.remove(0);
572 assert_matches!(statement, Statement::AlterDatabase { .. });
573 match statement {
574 Statement::AlterDatabase(alter_database) => {
575 assert_eq!("test_db", alter_database.database_name().0[0].to_string());
576
577 let alter_operation = alter_database.alter_operation();
578 assert_matches!(
579 alter_operation,
580 AlterDatabaseOperation::SetDatabaseOption { .. }
581 );
582 match alter_operation {
583 AlterDatabaseOperation::SetDatabaseOption { options } => {
584 assert_eq!(2, options.len());
585 assert_eq!("a", options[0].key);
586 assert_eq!("A", options[0].value);
587 assert_eq!("b", options[1].key);
588 assert_eq!("B", options[1].value);
589 }
590 _ => unreachable!(),
591 }
592 }
593 _ => unreachable!(),
594 }
595 let sql = "ALTER DATABASE test_db UNSET 'a', 'b'";
596 let mut result =
597 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
598 .unwrap();
599 assert_eq!(1, result.len());
600 let statement = result.remove(0);
601 assert_matches!(statement, Statement::AlterDatabase { .. });
602 match statement {
603 Statement::AlterDatabase(alter_database) => {
604 assert_eq!("test_db", alter_database.database_name().0[0].to_string());
605 let alter_operation = alter_database.alter_operation();
606 assert_matches!(
607 alter_operation,
608 AlterDatabaseOperation::UnsetDatabaseOption { .. }
609 );
610 match alter_operation {
611 AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
612 assert_eq!(2, keys.len());
613 assert_eq!("a", keys[0]);
614 assert_eq!("b", keys[1]);
615 }
616 _ => unreachable!(),
617 }
618 }
619 _ => unreachable!(),
620 }
621 }
622
623 #[test]
624 fn test_parse_alter_add_column() {
625 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null;";
626 let mut result =
627 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
628 .unwrap();
629 assert_eq!(1, result.len());
630
631 let statement = result.remove(0);
632 assert_matches!(statement, Statement::AlterTable { .. });
633 match statement {
634 Statement::AlterTable(alter_table) => {
635 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
636
637 let alter_operation = alter_table.alter_operation();
638 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
639 match alter_operation {
640 AlterTableOperation::AddColumns { add_columns } => {
641 assert_eq!(add_columns.len(), 1);
642 assert_eq!("tagk_i", add_columns[0].column_def.name.value);
643 assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
644 assert!(
645 add_columns[0]
646 .column_def
647 .options
648 .iter()
649 .any(|o| matches!(o.option, ColumnOption::Null))
650 );
651 assert_eq!(&None, &add_columns[0].location);
652 }
653 _ => unreachable!(),
654 }
655 }
656 _ => unreachable!(),
657 }
658 }
659
660 #[test]
661 fn test_parse_alter_add_column_with_first() {
662 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null FIRST;";
663 let mut result =
664 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
665 .unwrap();
666 assert_eq!(1, result.len());
667
668 let statement = result.remove(0);
669 assert_matches!(statement, Statement::AlterTable { .. });
670 match statement {
671 Statement::AlterTable(alter_table) => {
672 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
673
674 let alter_operation = alter_table.alter_operation();
675 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
676 match alter_operation {
677 AlterTableOperation::AddColumns { add_columns } => {
678 assert_eq!("tagk_i", add_columns[0].column_def.name.value);
679 assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
680 assert!(
681 add_columns[0]
682 .column_def
683 .options
684 .iter()
685 .any(|o| matches!(o.option, ColumnOption::Null))
686 );
687 assert_eq!(&Some(AddColumnLocation::First), &add_columns[0].location);
688 }
689 _ => unreachable!(),
690 }
691 }
692 _ => unreachable!(),
693 }
694 }
695
696 #[test]
697 fn test_parse_alter_add_column_with_after() {
698 let sql =
699 "ALTER TABLE my_metric_1 ADD tagk_i STRING Null AFTER ts, add column tagl_i String;";
700 let mut result =
701 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
702 .unwrap();
703 assert_eq!(1, result.len());
704
705 let statement = result.remove(0);
706 assert_matches!(statement, Statement::AlterTable { .. });
707 match statement {
708 Statement::AlterTable(alter_table) => {
709 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
710
711 let alter_operation = alter_table.alter_operation();
712 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
713 match alter_operation {
714 AlterTableOperation::AddColumns { add_columns } => {
715 let expecteds: Vec<(Option<AddColumnLocation>, ColumnDef)> = vec![
716 (
717 Some(AddColumnLocation::After {
718 column_name: "ts".to_string(),
719 }),
720 ColumnDef {
721 name: Ident::new("tagk_i"),
722 data_type: DataType::String(None),
723 options: vec![ColumnOptionDef {
724 name: None,
725 option: ColumnOption::Null,
726 }],
727 },
728 ),
729 (
730 None,
731 ColumnDef {
732 name: Ident::new("tagl_i"),
733 data_type: DataType::String(None),
734 options: vec![],
735 },
736 ),
737 ];
738 for (add_column, expected) in add_columns
739 .iter()
740 .zip(expecteds)
741 .collect::<Vec<(&AddColumn, (Option<AddColumnLocation>, ColumnDef))>>()
742 {
743 assert_eq!(add_column.column_def, expected.1);
744 assert_eq!(&expected.0, &add_column.location);
745 }
746 }
747 _ => unreachable!(),
748 }
749 }
750 _ => unreachable!(),
751 }
752 }
753
754 #[test]
755 fn test_parse_add_column_if_not_exists() {
756 let sql = "ALTER TABLE test ADD COLUMN IF NOT EXISTS a INTEGER, ADD COLUMN b STRING, ADD COLUMN IF NOT EXISTS c INT;";
757 let mut result =
758 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
759 .unwrap();
760 assert_eq!(result.len(), 1);
761 let statement = result.remove(0);
762 assert_matches!(statement, Statement::AlterTable { .. });
763 match statement {
764 Statement::AlterTable(alter) => {
765 assert_eq!(alter.table_name.0[0].to_string(), "test");
766 assert_matches!(
767 alter.alter_operation,
768 AlterTableOperation::AddColumns { .. }
769 );
770 match alter.alter_operation {
771 AlterTableOperation::AddColumns { add_columns } => {
772 let expected = [
773 AddColumn {
774 column_def: ColumnDef {
775 name: Ident::new("a"),
776 data_type: DataType::Integer(None),
777 options: vec![],
778 },
779 location: None,
780 add_if_not_exists: true,
781 },
782 AddColumn {
783 column_def: ColumnDef {
784 name: Ident::new("b"),
785 data_type: DataType::String(None),
786 options: vec![],
787 },
788 location: None,
789 add_if_not_exists: false,
790 },
791 AddColumn {
792 column_def: ColumnDef {
793 name: Ident::new("c"),
794 data_type: DataType::Int(None),
795 options: vec![],
796 },
797 location: None,
798 add_if_not_exists: true,
799 },
800 ];
801 for (idx, add_column) in add_columns.into_iter().enumerate() {
802 assert_eq!(add_column, expected[idx]);
803 }
804 }
805 _ => unreachable!(),
806 }
807 }
808 _ => unreachable!(),
809 }
810 }
811
812 #[test]
813 fn test_parse_alter_drop_column() {
814 let sql = "ALTER TABLE my_metric_1 DROP a";
815 let result =
816 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
817 .unwrap_err();
818 let err = result.output_msg();
819 assert_eq!(
820 err,
821 "Invalid SQL syntax: sql parser error: Expected: COLUMN, found: a at Line: 1, Column: 30"
822 );
823
824 let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
825 let mut result =
826 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
827 .unwrap();
828 assert_eq!(1, result.len());
829
830 let statement = result.remove(0);
831 assert_matches!(statement, Statement::AlterTable { .. });
832 match statement {
833 Statement::AlterTable(alter_table) => {
834 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
835
836 let alter_operation = alter_table.alter_operation();
837 assert_matches!(alter_operation, AlterTableOperation::DropColumn { .. });
838 match alter_operation {
839 AlterTableOperation::DropColumn { name } => {
840 assert_eq!("a", name.value);
841 }
842 _ => unreachable!(),
843 }
844 }
845 _ => unreachable!(),
846 }
847 }
848
849 #[test]
850 fn test_parse_alter_modify_column_type() {
851 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
852 let result_1 = ParserContext::create_with_dialect(
853 sql_1,
854 &GreptimeDbDialect {},
855 ParseOptions::default(),
856 )
857 .unwrap();
858
859 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
860 let mut result_2 = ParserContext::create_with_dialect(
861 sql_2,
862 &GreptimeDbDialect {},
863 ParseOptions::default(),
864 )
865 .unwrap();
866 assert_eq!(result_1, result_2);
867 assert_eq!(1, result_2.len());
868
869 let statement = result_2.remove(0);
870 assert_matches!(statement, Statement::AlterTable { .. });
871 match statement {
872 Statement::AlterTable(alter_table) => {
873 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
874
875 let alter_operation = alter_table.alter_operation();
876 assert_matches!(
877 alter_operation,
878 AlterTableOperation::ModifyColumnType { .. }
879 );
880 match alter_operation {
881 AlterTableOperation::ModifyColumnType {
882 column_name,
883 target_type,
884 } => {
885 assert_eq!("a", column_name.value);
886 assert_eq!(DataType::String(None), *target_type);
887 }
888 _ => unreachable!(),
889 }
890 }
891 _ => unreachable!(),
892 }
893 }
894
895 #[test]
896 fn test_parse_alter_change_column_alias_type() {
897 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
898 let mut result_1 = ParserContext::create_with_dialect(
899 sql_1,
900 &GreptimeDbDialect {},
901 ParseOptions::default(),
902 )
903 .unwrap();
904
905 match result_1.remove(0) {
906 Statement::AlterTable(alter_table) => {
907 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
908
909 let alter_operation = alter_table.alter_operation();
910 assert_matches!(
911 alter_operation,
912 AlterTableOperation::ModifyColumnType { .. }
913 );
914 match alter_operation {
915 AlterTableOperation::ModifyColumnType {
916 column_name,
917 target_type,
918 } => {
919 assert_eq!("a", column_name.value);
920 assert_eq!(DataType::MediumText, *target_type);
921 }
922 _ => unreachable!(),
923 }
924 }
925 _ => unreachable!(),
926 }
927
928 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a TIMESTAMP_US";
929 let mut result_2 = ParserContext::create_with_dialect(
930 sql_2,
931 &GreptimeDbDialect {},
932 ParseOptions::default(),
933 )
934 .unwrap();
935
936 match result_2.remove(0) {
937 Statement::AlterTable(alter_table) => {
938 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
939
940 let alter_operation = alter_table.alter_operation();
941 assert_matches!(
942 alter_operation,
943 AlterTableOperation::ModifyColumnType { .. }
944 );
945 match alter_operation {
946 AlterTableOperation::ModifyColumnType {
947 column_name,
948 target_type,
949 } => {
950 assert_eq!("a", column_name.value);
951 assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
952 }
953 _ => unreachable!(),
954 }
955 }
956 _ => unreachable!(),
957 }
958 }
959
960 #[test]
961 fn test_parse_alter_rename_table() {
962 let sql = "ALTER TABLE test_table table_t";
963 let result =
964 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
965 .unwrap_err();
966 let err = result.output_msg();
967 assert_eq!(
968 err,
969 "Invalid SQL syntax: sql parser error: Expected ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE, found: table_t"
970 );
971
972 let sql = "ALTER TABLE test_table RENAME table_t";
973 let mut result =
974 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
975 .unwrap();
976 assert_eq!(1, result.len());
977
978 let statement = result.remove(0);
979 assert_matches!(statement, Statement::AlterTable { .. });
980 match statement {
981 Statement::AlterTable(alter_table) => {
982 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
983
984 let alter_operation = alter_table.alter_operation();
985 assert_matches!(alter_operation, AlterTableOperation::RenameTable { .. });
986 match alter_operation {
987 AlterTableOperation::RenameTable { new_table_name } => {
988 assert_eq!("table_t", new_table_name);
989 }
990 _ => unreachable!(),
991 }
992 }
993 _ => unreachable!(),
994 }
995 }
996
997 fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
998 let result =
999 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1000 .unwrap();
1001 assert_eq!(1, result.len());
1002 let Statement::AlterTable(alter) = &result[0] else {
1003 unreachable!()
1004 };
1005 assert_eq!("test_table", alter.table_name.0[0].to_string());
1006 let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
1007 unreachable!()
1008 };
1009
1010 assert_eq!(sql, alter.to_string());
1011 let res = options
1012 .iter()
1013 .map(|o| (o.key.as_str(), o.value.as_str()))
1014 .collect::<Vec<_>>();
1015 assert_eq!(expected, &res);
1016 }
1017
1018 fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
1019 let result =
1020 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1021 .unwrap();
1022 assert_eq!(1, result.len());
1023 let Statement::AlterTable(alter) = &result[0] else {
1024 unreachable!()
1025 };
1026 assert_eq!("test_table", alter.table_name.0[0].to_string());
1027 let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
1028 unreachable!()
1029 };
1030
1031 assert_eq!(sql, alter.to_string());
1032 assert_eq!(expected, keys);
1033 }
1034
1035 #[test]
1036 fn test_parse_alter_table_set_options() {
1037 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
1038 check_parse_alter_table_set_options(
1039 "ALTER TABLE test_table SET 'a'='A','b'='B'",
1040 &[("a", "A"), ("b", "B")],
1041 );
1042 check_parse_alter_table_set_options(
1043 "ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
1044 &[("a", "A"), ("b", "B"), ("c", "C")],
1045 );
1046 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
1047
1048 ParserContext::create_with_dialect(
1049 "ALTER TABLE test_table SET a INTEGER",
1050 &GreptimeDbDialect {},
1051 ParseOptions::default(),
1052 )
1053 .unwrap_err();
1054 }
1055
1056 #[test]
1057 fn test_parse_alter_table_unset_options() {
1058 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
1059 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
1060 ParserContext::create_with_dialect(
1061 "ALTER TABLE test_table UNSET a INTEGER",
1062 &GreptimeDbDialect {},
1063 ParseOptions::default(),
1064 )
1065 .unwrap_err();
1066 }
1067
1068 #[test]
1069 fn test_parse_alter_column_fulltext() {
1070 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false',backend='bloom',granularity=1000,false_positive_rate=0.01)";
1071 let mut result =
1072 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1073 .unwrap();
1074
1075 assert_eq!(1, result.len());
1076 let statement = result.remove(0);
1077 assert_matches!(statement, Statement::AlterTable { .. });
1078 match statement {
1079 Statement::AlterTable(alter_table) => {
1080 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1081
1082 let alter_operation = alter_table.alter_operation();
1083 match alter_operation {
1084 AlterTableOperation::SetIndex {
1085 options:
1086 SetIndexOperation::Fulltext {
1087 column_name,
1088 options,
1089 },
1090 } => {
1091 assert_eq!("a", column_name.value);
1092 assert_eq!(
1093 FulltextOptions::new_unchecked(
1094 true,
1095 FulltextAnalyzer::English,
1096 false,
1097 FulltextBackend::Bloom,
1098 1000,
1099 0.01,
1100 ),
1101 *options
1102 );
1103 }
1104 _ => unreachable!(),
1105 };
1106 }
1107 _ => unreachable!(),
1108 }
1109
1110 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
1111 let mut result =
1112 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1113 .unwrap();
1114 assert_eq!(1, result.len());
1115 let statement = result.remove(0);
1116 assert_matches!(statement, Statement::AlterTable { .. });
1117 match statement {
1118 Statement::AlterTable(alter_table) => {
1119 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1120
1121 let alter_operation = alter_table.alter_operation();
1122 assert_eq!(
1123 alter_operation,
1124 &AlterTableOperation::UnsetIndex {
1125 options: UnsetIndexOperation::Fulltext {
1126 column_name: Ident::new("a"),
1127 }
1128 }
1129 );
1130 }
1131 _ => unreachable!(),
1132 }
1133
1134 let invalid_sql =
1135 "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
1136 let result = ParserContext::create_with_dialect(
1137 invalid_sql,
1138 &GreptimeDbDialect {},
1139 ParseOptions::default(),
1140 )
1141 .unwrap_err();
1142 let err = result.to_string();
1143 assert_eq!(
1144 err,
1145 "Invalid column option, column name: a, error: invalid FULLTEXT option: abcd"
1146 );
1147 }
1148
1149 #[test]
1150 fn test_parse_alter_column_inverted() {
1151 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED INDEX";
1152 let mut result =
1153 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1154 .unwrap();
1155
1156 assert_eq!(1, result.len());
1157 let statement = result.remove(0);
1158 assert_matches!(statement, Statement::AlterTable { .. });
1159 match statement {
1160 Statement::AlterTable(alter_table) => {
1161 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1162
1163 let alter_operation = alter_table.alter_operation();
1164 match alter_operation {
1165 AlterTableOperation::SetIndex {
1166 options: SetIndexOperation::Inverted { column_name },
1167 } => assert_eq!("a", column_name.value),
1168 _ => unreachable!(),
1169 };
1170 }
1171 _ => unreachable!(),
1172 }
1173
1174 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET INVERTED INDEX";
1175 let mut result =
1176 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1177 .unwrap();
1178 assert_eq!(1, result.len());
1179 let statement = result.remove(0);
1180 assert_matches!(statement, Statement::AlterTable { .. });
1181 match statement {
1182 Statement::AlterTable(alter_table) => {
1183 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1184
1185 let alter_operation = alter_table.alter_operation();
1186 assert_eq!(
1187 alter_operation,
1188 &AlterTableOperation::UnsetIndex {
1189 options: UnsetIndexOperation::Inverted {
1190 column_name: Ident::new("a"),
1191 }
1192 }
1193 );
1194 }
1195 _ => unreachable!(),
1196 }
1197
1198 let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED";
1199 ParserContext::create_with_dialect(
1200 invalid_sql,
1201 &GreptimeDbDialect {},
1202 ParseOptions::default(),
1203 )
1204 .unwrap_err();
1205 }
1206
1207 #[test]
1208 fn test_parse_alter_with_numeric_value() {
1209 for sql in [
1210 "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'=8;",
1211 "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'='8';",
1212 ] {
1213 let mut result = ParserContext::create_with_dialect(
1214 sql,
1215 &GreptimeDbDialect {},
1216 ParseOptions::default(),
1217 )
1218 .unwrap();
1219 assert_eq!(1, result.len());
1220
1221 let statement = result.remove(0);
1222 assert_matches!(statement, Statement::AlterTable { .. });
1223 match statement {
1224 Statement::AlterTable(alter_table) => {
1225 let alter_operation = alter_table.alter_operation();
1226 assert_matches!(alter_operation, AlterTableOperation::SetTableOptions { .. });
1227 match alter_operation {
1228 AlterTableOperation::SetTableOptions { options } => {
1229 assert_eq!(options.len(), 1);
1230 assert_eq!(options[0].key, "compaction.twcs.trigger_file_num");
1231 assert_eq!(options[0].value, "8");
1232 }
1233 _ => unreachable!(),
1234 }
1235 }
1236 _ => unreachable!(),
1237 }
1238 }
1239 }
1240
1241 #[test]
1242 fn test_parse_alter_drop_default() {
1243 let columns = vec![vec!["a"], vec!["a", "b", "c"]];
1244 for col in columns {
1245 let sql = col
1246 .iter()
1247 .map(|x| format!("MODIFY COLUMN {x} DROP DEFAULT"))
1248 .collect::<Vec<String>>()
1249 .join(",");
1250 let sql = format!("ALTER TABLE test_table {sql}");
1251 let mut result = ParserContext::create_with_dialect(
1252 &sql,
1253 &GreptimeDbDialect {},
1254 ParseOptions::default(),
1255 )
1256 .unwrap();
1257 assert_eq!(1, result.len());
1258 let statement = result.remove(0);
1259 assert_matches!(statement, Statement::AlterTable { .. });
1260 match statement {
1261 Statement::AlterTable(alter_table) => {
1262 assert_eq!(
1263 "test_table",
1264 alter_table.table_name().0[0].to_string_unquoted()
1265 );
1266 let alter_operation = alter_table.alter_operation();
1267 match alter_operation {
1268 AlterTableOperation::DropDefaults { columns } => {
1269 assert_eq!(col.len(), columns.len());
1270 for i in 0..columns.len() {
1271 assert_eq!(col[i], columns[i].0.value);
1272 }
1273 }
1274 _ => unreachable!(),
1275 }
1276 }
1277 _ => unreachable!(),
1278 }
1279 }
1280 }
1281
1282 #[test]
1283 fn test_parse_alter_set_default() {
1284 let columns = vec![vec!["a"], vec!["a", "b"], vec!["a", "b", "c"]];
1285 for col in columns {
1286 let sql = col
1287 .iter()
1288 .map(|x| format!("MODIFY COLUMN {x} SET DEFAULT 100"))
1289 .collect::<Vec<String>>()
1290 .join(",");
1291 let sql = format!("ALTER TABLE test_table {sql}");
1292 let mut result = ParserContext::create_with_dialect(
1293 &sql,
1294 &GreptimeDbDialect {},
1295 ParseOptions::default(),
1296 )
1297 .unwrap();
1298 assert_eq!(1, result.len());
1299 let statement = result.remove(0);
1300 assert_matches!(statement, Statement::AlterTable { .. });
1301 match statement {
1302 Statement::AlterTable(alter_table) => {
1303 assert_eq!("test_table", alter_table.table_name().to_string());
1304 let alter_operation = alter_table.alter_operation();
1305 match alter_operation {
1306 AlterTableOperation::SetDefaults { defaults } => {
1307 assert_eq!(col.len(), defaults.len());
1308 for i in 0..defaults.len() {
1309 assert_eq!(col[i], defaults[i].column_name.to_string());
1310 assert_eq!(
1311 "100".to_string(),
1312 defaults[i].default_constraint.to_string()
1313 );
1314 }
1315 }
1316 _ => unreachable!(),
1317 }
1318 }
1319 _ => unreachable!(),
1320 }
1321 }
1322 }
1323
1324 #[test]
1325 fn test_parse_alter_set_default_invalid() {
1326 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET 100;";
1327 let result =
1328 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1329 .unwrap_err();
1330 let err = result.output_msg();
1331 assert_eq!(
1332 err,
1333 "Invalid SQL syntax: sql parser error: Expected FULLTEXT OR INVERTED OR SKIPPING INDEX, found: 100"
1334 );
1335
1336 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, b SET DEFAULT 200";
1337 let result =
1338 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1339 .unwrap_err();
1340 let err = result.output_msg();
1341 assert_eq!(
1342 err,
1343 "Invalid SQL syntax: sql parser error: Expected: MODIFY, found: b at Line: 1, Column: 57"
1344 );
1345
1346 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, MODIFY COLUMN b DROP DEFAULT 200";
1347 let result =
1348 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1349 .unwrap_err();
1350 let err = result.output_msg();
1351 assert_eq!(
1352 err,
1353 "Invalid SQL syntax: sql parser error: Unexpected keyword, expect SET, got: `DROP`"
1354 );
1355 }
1356}