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