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