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 if let Ok(v) = parser.parse_literal_string() {
395 v
396 } else {
397 return Err(ParserError::ParserError(format!(
398 "Unexpected option value for alter table statements, expect string literal or NULL, got: `{}`",
399 parser.next_token()
400 )));
401 };
402 Ok((name, value))
403}
404
405fn parse_add_columns(parser: &mut Parser) -> std::result::Result<AddColumn, ParserError> {
406 parser.expect_keyword(Keyword::ADD)?;
407 let _ = parser.parse_keyword(Keyword::COLUMN);
408 let add_if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
409 let mut column_def = parser.parse_column_def()?;
410 column_def.name = ParserContext::canonicalize_identifier(column_def.name);
411 let location = if parser.parse_keyword(Keyword::FIRST) {
412 Some(AddColumnLocation::First)
413 } else if let Token::Word(word) = parser.peek_token().token {
414 if word.value.eq_ignore_ascii_case("AFTER") {
415 let _ = parser.next_token();
416 let name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
417 Some(AddColumnLocation::After {
418 column_name: name.value,
419 })
420 } else {
421 None
422 }
423 } else {
424 None
425 };
426 Ok(AddColumn {
427 column_def,
428 location,
429 add_if_not_exists,
430 })
431}
432
433fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
435 parser.parse_literal_string()
436}
437
438#[cfg(test)]
439mod tests {
440 use std::assert_matches::assert_matches;
441
442 use common_error::ext::ErrorExt;
443 use datatypes::schema::{FulltextAnalyzer, FulltextBackend, FulltextOptions};
444 use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType};
445
446 use super::*;
447 use crate::dialect::GreptimeDbDialect;
448 use crate::parser::ParseOptions;
449 use crate::statements::alter::AlterDatabaseOperation;
450
451 #[test]
452 fn test_parse_alter_database() {
453 let sql = "ALTER DATABASE test_db SET 'a'='A', 'b' = 'B'";
454 let mut result =
455 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
456 .unwrap();
457 assert_eq!(1, result.len());
458
459 let statement = result.remove(0);
460 assert_matches!(statement, Statement::AlterDatabase { .. });
461 match statement {
462 Statement::AlterDatabase(alter_database) => {
463 assert_eq!("test_db", alter_database.database_name().0[0].value);
464
465 let alter_operation = alter_database.alter_operation();
466 assert_matches!(
467 alter_operation,
468 AlterDatabaseOperation::SetDatabaseOption { .. }
469 );
470 match alter_operation {
471 AlterDatabaseOperation::SetDatabaseOption { options } => {
472 assert_eq!(2, options.len());
473 assert_eq!("a", options[0].key);
474 assert_eq!("A", options[0].value);
475 assert_eq!("b", options[1].key);
476 assert_eq!("B", options[1].value);
477 }
478 _ => unreachable!(),
479 }
480 }
481 _ => unreachable!(),
482 }
483 let sql = "ALTER DATABASE test_db UNSET 'a', 'b'";
484 let mut result =
485 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
486 .unwrap();
487 assert_eq!(1, result.len());
488 let statement = result.remove(0);
489 assert_matches!(statement, Statement::AlterDatabase { .. });
490 match statement {
491 Statement::AlterDatabase(alter_database) => {
492 assert_eq!("test_db", alter_database.database_name().0[0].value);
493 let alter_operation = alter_database.alter_operation();
494 assert_matches!(
495 alter_operation,
496 AlterDatabaseOperation::UnsetDatabaseOption { .. }
497 );
498 match alter_operation {
499 AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
500 assert_eq!(2, keys.len());
501 assert_eq!("a", keys[0]);
502 assert_eq!("b", keys[1]);
503 }
504 _ => unreachable!(),
505 }
506 }
507 _ => unreachable!(),
508 }
509 }
510
511 #[test]
512 fn test_parse_alter_add_column() {
513 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null;";
514 let mut result =
515 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
516 .unwrap();
517 assert_eq!(1, result.len());
518
519 let statement = result.remove(0);
520 assert_matches!(statement, Statement::AlterTable { .. });
521 match statement {
522 Statement::AlterTable(alter_table) => {
523 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
524
525 let alter_operation = alter_table.alter_operation();
526 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
527 match alter_operation {
528 AlterTableOperation::AddColumns { add_columns } => {
529 assert_eq!(add_columns.len(), 1);
530 assert_eq!("tagk_i", add_columns[0].column_def.name.value);
531 assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
532 assert!(add_columns[0]
533 .column_def
534 .options
535 .iter()
536 .any(|o| matches!(o.option, ColumnOption::Null)));
537 assert_eq!(&None, &add_columns[0].location);
538 }
539 _ => unreachable!(),
540 }
541 }
542 _ => unreachable!(),
543 }
544 }
545
546 #[test]
547 fn test_parse_alter_add_column_with_first() {
548 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null FIRST;";
549 let mut result =
550 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
551 .unwrap();
552 assert_eq!(1, result.len());
553
554 let statement = result.remove(0);
555 assert_matches!(statement, Statement::AlterTable { .. });
556 match statement {
557 Statement::AlterTable(alter_table) => {
558 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
559
560 let alter_operation = alter_table.alter_operation();
561 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
562 match alter_operation {
563 AlterTableOperation::AddColumns { add_columns } => {
564 assert_eq!("tagk_i", add_columns[0].column_def.name.value);
565 assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
566 assert!(add_columns[0]
567 .column_def
568 .options
569 .iter()
570 .any(|o| matches!(o.option, ColumnOption::Null)));
571 assert_eq!(&Some(AddColumnLocation::First), &add_columns[0].location);
572 }
573 _ => unreachable!(),
574 }
575 }
576 _ => unreachable!(),
577 }
578 }
579
580 #[test]
581 fn test_parse_alter_add_column_with_after() {
582 let sql =
583 "ALTER TABLE my_metric_1 ADD tagk_i STRING Null AFTER ts, add column tagl_i String;";
584 let mut result =
585 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
586 .unwrap();
587 assert_eq!(1, result.len());
588
589 let statement = result.remove(0);
590 assert_matches!(statement, Statement::AlterTable { .. });
591 match statement {
592 Statement::AlterTable(alter_table) => {
593 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
594
595 let alter_operation = alter_table.alter_operation();
596 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
597 match alter_operation {
598 AlterTableOperation::AddColumns { add_columns } => {
599 let expecteds: Vec<(Option<AddColumnLocation>, ColumnDef)> = vec![
600 (
601 Some(AddColumnLocation::After {
602 column_name: "ts".to_string(),
603 }),
604 ColumnDef {
605 name: Ident::new("tagk_i"),
606 data_type: DataType::String(None),
607 options: vec![ColumnOptionDef {
608 name: None,
609 option: ColumnOption::Null,
610 }],
611 collation: None,
612 },
613 ),
614 (
615 None,
616 ColumnDef {
617 name: Ident::new("tagl_i"),
618 data_type: DataType::String(None),
619 options: vec![],
620 collation: None,
621 },
622 ),
623 ];
624 for (add_column, expected) in add_columns
625 .iter()
626 .zip(expecteds)
627 .collect::<Vec<(&AddColumn, (Option<AddColumnLocation>, ColumnDef))>>()
628 {
629 assert_eq!(add_column.column_def, expected.1);
630 assert_eq!(&expected.0, &add_column.location);
631 }
632 }
633 _ => unreachable!(),
634 }
635 }
636 _ => unreachable!(),
637 }
638 }
639
640 #[test]
641 fn test_parse_add_column_if_not_exists() {
642 let sql = "ALTER TABLE test ADD COLUMN IF NOT EXISTS a INTEGER, ADD COLUMN b STRING, ADD COLUMN IF NOT EXISTS c INT;";
643 let mut result =
644 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
645 .unwrap();
646 assert_eq!(result.len(), 1);
647 let statement = result.remove(0);
648 assert_matches!(statement, Statement::AlterTable { .. });
649 match statement {
650 Statement::AlterTable(alter) => {
651 assert_eq!(alter.table_name.0[0].value, "test");
652 assert_matches!(
653 alter.alter_operation,
654 AlterTableOperation::AddColumns { .. }
655 );
656 match alter.alter_operation {
657 AlterTableOperation::AddColumns { add_columns } => {
658 let expected = vec![
659 AddColumn {
660 column_def: ColumnDef {
661 name: Ident::new("a"),
662 data_type: DataType::Integer(None),
663 collation: None,
664 options: vec![],
665 },
666 location: None,
667 add_if_not_exists: true,
668 },
669 AddColumn {
670 column_def: ColumnDef {
671 name: Ident::new("b"),
672 data_type: DataType::String(None),
673 collation: None,
674 options: vec![],
675 },
676 location: None,
677 add_if_not_exists: false,
678 },
679 AddColumn {
680 column_def: ColumnDef {
681 name: Ident::new("c"),
682 data_type: DataType::Int(None),
683 collation: None,
684 options: vec![],
685 },
686 location: None,
687 add_if_not_exists: true,
688 },
689 ];
690 for (idx, add_column) in add_columns.into_iter().enumerate() {
691 assert_eq!(add_column, expected[idx]);
692 }
693 }
694 _ => unreachable!(),
695 }
696 }
697 _ => unreachable!(),
698 }
699 }
700
701 #[test]
702 fn test_parse_alter_drop_column() {
703 let sql = "ALTER TABLE my_metric_1 DROP a";
704 let result =
705 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
706 .unwrap_err();
707 let err = result.output_msg();
708 assert_eq!(
709 err,
710 "Invalid SQL syntax: sql parser error: Expected: COLUMN, found: a at Line: 1, Column: 30"
711 );
712
713 let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
714 let mut result =
715 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
716 .unwrap();
717 assert_eq!(1, result.len());
718
719 let statement = result.remove(0);
720 assert_matches!(statement, Statement::AlterTable { .. });
721 match statement {
722 Statement::AlterTable(alter_table) => {
723 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
724
725 let alter_operation = alter_table.alter_operation();
726 assert_matches!(alter_operation, AlterTableOperation::DropColumn { .. });
727 match alter_operation {
728 AlterTableOperation::DropColumn { name } => {
729 assert_eq!("a", name.value);
730 }
731 _ => unreachable!(),
732 }
733 }
734 _ => unreachable!(),
735 }
736 }
737
738 #[test]
739 fn test_parse_alter_modify_column_type() {
740 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
741 let result_1 = ParserContext::create_with_dialect(
742 sql_1,
743 &GreptimeDbDialect {},
744 ParseOptions::default(),
745 )
746 .unwrap();
747
748 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
749 let mut result_2 = ParserContext::create_with_dialect(
750 sql_2,
751 &GreptimeDbDialect {},
752 ParseOptions::default(),
753 )
754 .unwrap();
755 assert_eq!(result_1, result_2);
756 assert_eq!(1, result_2.len());
757
758 let statement = result_2.remove(0);
759 assert_matches!(statement, Statement::AlterTable { .. });
760 match statement {
761 Statement::AlterTable(alter_table) => {
762 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
763
764 let alter_operation = alter_table.alter_operation();
765 assert_matches!(
766 alter_operation,
767 AlterTableOperation::ModifyColumnType { .. }
768 );
769 match alter_operation {
770 AlterTableOperation::ModifyColumnType {
771 column_name,
772 target_type,
773 } => {
774 assert_eq!("a", column_name.value);
775 assert_eq!(DataType::String(None), *target_type);
776 }
777 _ => unreachable!(),
778 }
779 }
780 _ => unreachable!(),
781 }
782 }
783
784 #[test]
785 fn test_parse_alter_change_column_alias_type() {
786 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
787 let mut result_1 = ParserContext::create_with_dialect(
788 sql_1,
789 &GreptimeDbDialect {},
790 ParseOptions::default(),
791 )
792 .unwrap();
793
794 match result_1.remove(0) {
795 Statement::AlterTable(alter_table) => {
796 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
797
798 let alter_operation = alter_table.alter_operation();
799 assert_matches!(
800 alter_operation,
801 AlterTableOperation::ModifyColumnType { .. }
802 );
803 match alter_operation {
804 AlterTableOperation::ModifyColumnType {
805 column_name,
806 target_type,
807 } => {
808 assert_eq!("a", column_name.value);
809 assert_eq!(DataType::MediumText, *target_type);
810 }
811 _ => unreachable!(),
812 }
813 }
814 _ => unreachable!(),
815 }
816
817 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a TIMESTAMP_US";
818 let mut result_2 = ParserContext::create_with_dialect(
819 sql_2,
820 &GreptimeDbDialect {},
821 ParseOptions::default(),
822 )
823 .unwrap();
824
825 match result_2.remove(0) {
826 Statement::AlterTable(alter_table) => {
827 assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
828
829 let alter_operation = alter_table.alter_operation();
830 assert_matches!(
831 alter_operation,
832 AlterTableOperation::ModifyColumnType { .. }
833 );
834 match alter_operation {
835 AlterTableOperation::ModifyColumnType {
836 column_name,
837 target_type,
838 } => {
839 assert_eq!("a", column_name.value);
840 assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
841 }
842 _ => unreachable!(),
843 }
844 }
845 _ => unreachable!(),
846 }
847 }
848
849 #[test]
850 fn test_parse_alter_rename_table() {
851 let sql = "ALTER TABLE test_table table_t";
852 let result =
853 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
854 .unwrap_err();
855 let err = result.output_msg();
856 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");
857
858 let sql = "ALTER TABLE test_table RENAME table_t";
859 let mut result =
860 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
861 .unwrap();
862 assert_eq!(1, result.len());
863
864 let statement = result.remove(0);
865 assert_matches!(statement, Statement::AlterTable { .. });
866 match statement {
867 Statement::AlterTable(alter_table) => {
868 assert_eq!("test_table", alter_table.table_name().0[0].value);
869
870 let alter_operation = alter_table.alter_operation();
871 assert_matches!(alter_operation, AlterTableOperation::RenameTable { .. });
872 match alter_operation {
873 AlterTableOperation::RenameTable { new_table_name } => {
874 assert_eq!("table_t", new_table_name);
875 }
876 _ => unreachable!(),
877 }
878 }
879 _ => unreachable!(),
880 }
881 }
882
883 fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
884 let result =
885 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
886 .unwrap();
887 assert_eq!(1, result.len());
888 let Statement::AlterTable(alter) = &result[0] else {
889 unreachable!()
890 };
891 assert_eq!("test_table", alter.table_name.0[0].value);
892 let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
893 unreachable!()
894 };
895
896 assert_eq!(sql, alter.to_string());
897 let res = options
898 .iter()
899 .map(|o| (o.key.as_str(), o.value.as_str()))
900 .collect::<Vec<_>>();
901 assert_eq!(expected, &res);
902 }
903
904 fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
905 let result =
906 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
907 .unwrap();
908 assert_eq!(1, result.len());
909 let Statement::AlterTable(alter) = &result[0] else {
910 unreachable!()
911 };
912 assert_eq!("test_table", alter.table_name.0[0].value);
913 let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
914 unreachable!()
915 };
916
917 assert_eq!(sql, alter.to_string());
918 let res = keys.iter().map(|o| o.to_string()).collect::<Vec<_>>();
919 assert_eq!(expected, &res);
920 }
921
922 #[test]
923 fn test_parse_alter_table_set_options() {
924 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
925 check_parse_alter_table_set_options(
926 "ALTER TABLE test_table SET 'a'='A','b'='B'",
927 &[("a", "A"), ("b", "B")],
928 );
929 check_parse_alter_table_set_options(
930 "ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
931 &[("a", "A"), ("b", "B"), ("c", "C")],
932 );
933 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
934
935 ParserContext::create_with_dialect(
936 "ALTER TABLE test_table SET a INTEGER",
937 &GreptimeDbDialect {},
938 ParseOptions::default(),
939 )
940 .unwrap_err();
941 }
942
943 #[test]
944 fn test_parse_alter_table_unset_options() {
945 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
946 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
947 ParserContext::create_with_dialect(
948 "ALTER TABLE test_table UNSET a INTEGER",
949 &GreptimeDbDialect {},
950 ParseOptions::default(),
951 )
952 .unwrap_err();
953 }
954
955 #[test]
956 fn test_parse_alter_column_fulltext() {
957 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false',backend='bloom')";
958 let mut result =
959 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
960 .unwrap();
961
962 assert_eq!(1, result.len());
963 let statement = result.remove(0);
964 assert_matches!(statement, Statement::AlterTable { .. });
965 match statement {
966 Statement::AlterTable(alter_table) => {
967 assert_eq!("test_table", alter_table.table_name().0[0].value);
968
969 let alter_operation = alter_table.alter_operation();
970 match alter_operation {
971 AlterTableOperation::SetIndex {
972 options:
973 SetIndexOperation::Fulltext {
974 column_name,
975 options,
976 },
977 } => {
978 assert_eq!("a", column_name.value);
979 assert_eq!(
980 FulltextOptions {
981 enable: true,
982 analyzer: FulltextAnalyzer::English,
983 case_sensitive: false,
984 backend: FulltextBackend::Bloom,
985 },
986 *options
987 );
988 }
989 _ => unreachable!(),
990 };
991 }
992 _ => unreachable!(),
993 }
994
995 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
996 let mut result =
997 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
998 .unwrap();
999 assert_eq!(1, result.len());
1000 let statement = result.remove(0);
1001 assert_matches!(statement, Statement::AlterTable { .. });
1002 match statement {
1003 Statement::AlterTable(alter_table) => {
1004 assert_eq!("test_table", alter_table.table_name().0[0].value);
1005
1006 let alter_operation = alter_table.alter_operation();
1007 assert_eq!(
1008 alter_operation,
1009 &AlterTableOperation::UnsetIndex {
1010 options: UnsetIndexOperation::Fulltext {
1011 column_name: Ident::new("a"),
1012 }
1013 }
1014 );
1015 }
1016 _ => unreachable!(),
1017 }
1018
1019 let invalid_sql =
1020 "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
1021 let result = ParserContext::create_with_dialect(
1022 invalid_sql,
1023 &GreptimeDbDialect {},
1024 ParseOptions::default(),
1025 )
1026 .unwrap_err();
1027 let err = result.to_string();
1028 assert_eq!(
1029 err,
1030 "Invalid column option, column name: a, error: invalid FULLTEXT option: abcd"
1031 );
1032 }
1033
1034 #[test]
1035 fn test_parse_alter_column_inverted() {
1036 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED INDEX";
1037 let mut result =
1038 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1039 .unwrap();
1040
1041 assert_eq!(1, result.len());
1042 let statement = result.remove(0);
1043 assert_matches!(statement, Statement::AlterTable { .. });
1044 match statement {
1045 Statement::AlterTable(alter_table) => {
1046 assert_eq!("test_table", alter_table.table_name().0[0].value);
1047
1048 let alter_operation = alter_table.alter_operation();
1049 match alter_operation {
1050 AlterTableOperation::SetIndex {
1051 options: SetIndexOperation::Inverted { column_name },
1052 } => assert_eq!("a", column_name.value),
1053 _ => unreachable!(),
1054 };
1055 }
1056 _ => unreachable!(),
1057 }
1058
1059 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET INVERTED INDEX";
1060 let mut result =
1061 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1062 .unwrap();
1063 assert_eq!(1, result.len());
1064 let statement = result.remove(0);
1065 assert_matches!(statement, Statement::AlterTable { .. });
1066 match statement {
1067 Statement::AlterTable(alter_table) => {
1068 assert_eq!("test_table", alter_table.table_name().0[0].value);
1069
1070 let alter_operation = alter_table.alter_operation();
1071 assert_eq!(
1072 alter_operation,
1073 &AlterTableOperation::UnsetIndex {
1074 options: UnsetIndexOperation::Inverted {
1075 column_name: Ident::new("a"),
1076 }
1077 }
1078 );
1079 }
1080 _ => unreachable!(),
1081 }
1082
1083 let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED";
1084 ParserContext::create_with_dialect(
1085 invalid_sql,
1086 &GreptimeDbDialect {},
1087 ParseOptions::default(),
1088 )
1089 .unwrap_err();
1090 }
1091}