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