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::{BinaryOperator, Expr, Ident};
24use sqlparser::keywords::Keyword;
25use sqlparser::parser::IsOptional::Mandatory;
26use sqlparser::parser::{Parser, ParserError};
27use sqlparser::tokenizer::{Token, TokenWithSpan};
28
29use crate::ast::{ColumnOption, ObjectNamePartExt};
30use crate::error::{self, InvalidColumnOptionSnafu, Result, SetFulltextOptionSnafu};
31use crate::parser::ParserContext;
32use crate::parsers::create_parser::{INVERTED, parse_json2_type_and_options};
33use crate::parsers::utils::{
34 parse_with_options, validate_column_fulltext_create_option,
35 validate_column_skipping_index_create_option,
36};
37use crate::statements::OptionMap;
38use crate::statements::alter::{
39 AddColumn, AlterDatabase, AlterDatabaseOperation, AlterTable, AlterTableOperation,
40 DropDefaultsOperation, KeyValueOption, RepartitionOperation, SetDefaultsOperation,
41 SetIndexOperation, UnsetIndexOperation,
42};
43use crate::statements::statement::Statement;
44use crate::util::{OptionValue, parse_option_string};
45
46impl ParserContext<'_> {
47 pub(crate) fn parse_alter(&mut self) -> Result<Statement> {
48 let _ = self.parser.expect_keyword(Keyword::ALTER);
49 match self.parser.peek_token().token {
50 Token::Word(w) => match w.keyword {
51 Keyword::DATABASE => self.parse_alter_database().map(Statement::AlterDatabase),
52 Keyword::TABLE => self.parse_alter_table().map(Statement::AlterTable),
53 #[cfg(feature = "enterprise")]
54 Keyword::TRIGGER => {
55 self.parser.next_token();
56 self.parse_alter_trigger()
57 }
58 _ => self.expected("DATABASE or TABLE after ALTER", self.parser.peek_token()),
59 },
60 unexpected => self.unsupported(unexpected.to_string()),
61 }
62 }
63
64 fn parse_alter_database(&mut self) -> Result<AlterDatabase> {
65 self.parser
66 .expect_keyword(Keyword::DATABASE)
67 .context(error::SyntaxSnafu)?;
68
69 let database_name = self
70 .parser
71 .parse_object_name(false)
72 .context(error::SyntaxSnafu)?;
73 let database_name = Self::canonicalize_object_name(database_name)?;
74
75 match self.parser.peek_token().token {
76 Token::Word(w) => {
77 if w.value.eq_ignore_ascii_case("UNSET") {
78 let _ = self.parser.next_token();
79 let keys = self
80 .parser
81 .parse_comma_separated(parse_string_option_names)
82 .context(error::SyntaxSnafu)?
83 .into_iter()
84 .collect();
85 Ok(AlterDatabase::new(
86 database_name,
87 AlterDatabaseOperation::UnsetDatabaseOption { keys },
88 ))
89 } else if w.keyword == Keyword::SET {
90 let _ = self.parser.next_token();
91 let options = self
92 .parser
93 .parse_comma_separated(parse_string_options)
94 .context(error::SyntaxSnafu)?
95 .into_iter()
96 .map(|(key, value)| KeyValueOption { key, value })
97 .collect();
98 Ok(AlterDatabase::new(
99 database_name,
100 AlterDatabaseOperation::SetDatabaseOption { options },
101 ))
102 } else {
103 self.expected(
104 "SET or UNSET after ALTER DATABASE",
105 self.parser.peek_token(),
106 )
107 }
108 }
109 unexpected => self.unsupported(unexpected.to_string()),
110 }
111 }
112
113 fn parse_alter_table(&mut self) -> Result<AlterTable> {
114 self.parser
115 .expect_keyword(Keyword::TABLE)
116 .context(error::SyntaxSnafu)?;
117
118 let raw_table_name = self
119 .parser
120 .parse_object_name(false)
121 .context(error::SyntaxSnafu)?;
122 let table_name = Self::canonicalize_object_name(raw_table_name)?;
123
124 let alter_operation = match self.parser.peek_token().token {
125 Token::Word(w) => {
126 if w.value.eq_ignore_ascii_case("MODIFY") {
127 self.parse_alter_table_modify()?
128 } else if w.value.eq_ignore_ascii_case("UNSET") {
129 self.parse_alter_table_unset()?
130 } else if w.value.eq_ignore_ascii_case("REPARTITION") {
131 self.parse_alter_table_repartition()?
132 } else if w.value.eq_ignore_ascii_case("SPLIT") {
133 self.parse_alter_table_split_partition()?
134 } else if w.value.eq_ignore_ascii_case("MERGE") {
135 self.parse_alter_table_merge_partition()?
136 } else {
137 match w.keyword {
138 Keyword::PARTITION => self.parse_alter_table_partition()?,
139 Keyword::ADD => self.parse_alter_table_add()?,
140 Keyword::DROP => {
141 let _ = self.parser.next_token();
142 self.parser
143 .expect_keyword(Keyword::COLUMN)
144 .context(error::SyntaxSnafu)?;
145 let name = Self::canonicalize_identifier(
146 self.parser.parse_identifier().context(error::SyntaxSnafu)?,
147 );
148 AlterTableOperation::DropColumn { name }
149 }
150 Keyword::RENAME => {
151 let _ = self.parser.next_token();
152 let new_table_name_obj_raw =
153 self.parse_object_name().context(error::SyntaxSnafu)?;
154 let new_table_name_obj =
155 Self::canonicalize_object_name(new_table_name_obj_raw)?;
156 let new_table_name = match &new_table_name_obj.0[..] {
157 [table] => table.to_string_unquoted(),
158 _ => {
159 return Err(ParserError::ParserError(format!(
160 "expect table name, actual: {new_table_name_obj}"
161 )))
162 .context(error::SyntaxSnafu);
163 }
164 };
165 AlterTableOperation::RenameTable { new_table_name }
166 }
167 Keyword::SET => {
168 let _ = self.parser.next_token();
169 let options = self
170 .parser
171 .parse_comma_separated(parse_string_options)
172 .context(error::SyntaxSnafu)?
173 .into_iter()
174 .map(|(key, value)| KeyValueOption { key, value })
175 .collect();
176 AlterTableOperation::SetTableOptions { options }
177 }
178 _ => self.expected(
179 "ADD or DROP or MODIFY or RENAME or SET or UNSET or REPARTITION or SPLIT or MERGE or PARTITION after ALTER TABLE",
180 self.parser.peek_token(),
181 )?,
182 }
183 }
184 }
185 unexpected => self.unsupported(unexpected.to_string())?,
186 };
187
188 let options = parse_with_options(&mut self.parser)?;
189 Ok(AlterTable::new(table_name, alter_operation, options))
191 }
192
193 fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
194 let _ = self.parser.next_token();
195 let keys = self
196 .parser
197 .parse_comma_separated(parse_string_option_names)
198 .context(error::SyntaxSnafu)?
199 .into_iter()
200 .collect();
201
202 Ok(AlterTableOperation::UnsetTableOptions { keys })
203 }
204
205 fn parse_alter_table_repartition(&mut self) -> Result<AlterTableOperation> {
206 let _ = self.parser.next_token();
207
208 let from_exprs = self.parse_repartition_expr_list()?;
209 let partition_columns = self.parse_optional_repartition_columns()?;
210
211 self.parser
212 .expect_keyword(Keyword::INTO)
213 .context(error::SyntaxSnafu)?;
214 let into_exprs = self.parse_repartition_expr_list()?;
215
216 if matches!(self.parser.peek_token().token, Token::Comma) {
217 return self.expected("end of REPARTITION clause", self.parser.peek_token());
218 }
219
220 Ok(AlterTableOperation::Repartition {
221 operation: match partition_columns {
222 Some(partition_columns) => RepartitionOperation::with_partition_columns(
223 from_exprs,
224 into_exprs,
225 partition_columns,
226 ),
227 None => RepartitionOperation::new(from_exprs, into_exprs),
228 },
229 })
230 }
231
232 fn parse_alter_table_partition(&mut self) -> Result<AlterTableOperation> {
233 let _ = self.parser.next_token();
234 let partitions = self.parse_partition_on_columns()?;
235 if partitions.exprs.is_empty() {
236 return Err(ParserError::ParserError(
237 "PARTITION ON COLUMNS requires at least one partition expression".to_string(),
238 ))
239 .context(error::SyntaxSnafu);
240 }
241
242 Ok(AlterTableOperation::Partition { partitions })
243 }
244
245 fn parse_alter_table_split_partition(&mut self) -> Result<AlterTableOperation> {
246 let _ = self.parser.next_token();
247 self.parser
248 .expect_keyword(Keyword::PARTITION)
249 .context(error::SyntaxSnafu)?;
250
251 let from_exprs = self.parse_repartition_expr_list()?;
252 if from_exprs.len() != 1 {
253 return self.expected(
254 "single partition expression inside SPLIT PARTITION clause",
255 self.parser.peek_token(),
256 );
257 }
258
259 let partition_columns = self.parse_optional_repartition_columns()?;
260
261 self.parser
262 .expect_keyword(Keyword::INTO)
263 .context(error::SyntaxSnafu)?;
264 let into_exprs = self.parse_repartition_expr_list()?;
265
266 if matches!(self.parser.peek_token().token, Token::Comma) {
267 return self.expected("end of SPLIT PARTITION clause", self.parser.peek_token());
268 }
269 if matches!(&self.parser.peek_token().token, Token::Word(w) if w.keyword == Keyword::ON) {
270 return self.expected("end of SPLIT PARTITION clause", self.parser.peek_token());
271 }
272
273 Ok(AlterTableOperation::Repartition {
274 operation: match partition_columns {
275 Some(partition_columns) => RepartitionOperation::with_partition_columns(
276 from_exprs,
277 into_exprs,
278 partition_columns,
279 ),
280 None => RepartitionOperation::new(from_exprs, into_exprs),
281 },
282 })
283 }
284
285 fn parse_optional_repartition_columns(&mut self) -> Result<Option<Vec<Ident>>> {
286 if !self.parser.parse_keywords(&[Keyword::ON, Keyword::COLUMNS]) {
287 return Ok(None);
288 }
289
290 let raw_column_list = self
291 .parser
292 .parse_parenthesized_column_list(Mandatory, false)
293 .context(error::SyntaxSnafu)?;
294 let column_list = raw_column_list
295 .into_iter()
296 .map(Self::canonicalize_identifier)
297 .collect();
298
299 Ok(Some(column_list))
300 }
301
302 fn parse_alter_table_merge_partition(&mut self) -> Result<AlterTableOperation> {
303 let _ = self.parser.next_token();
304 self.parser
305 .expect_keyword(Keyword::PARTITION)
306 .context(error::SyntaxSnafu)?;
307
308 let from_exprs = self.parse_repartition_expr_list()?;
309 let mut expr_iter = from_exprs.iter().cloned();
310 let Some(first) = expr_iter.next() else {
311 return self.expected(
312 "expression inside MERGE PARTITION clause",
313 self.parser.peek_token(),
314 );
315 };
316 let merged_expr = expr_iter.fold(first, |left, right| Expr::BinaryOp {
317 left: Box::new(left),
318 op: BinaryOperator::Or,
319 right: Box::new(right),
320 });
321
322 if matches!(self.parser.peek_token().token, Token::Comma) {
323 return self.expected("end of MERGE PARTITION clause", self.parser.peek_token());
324 }
325
326 Ok(AlterTableOperation::Repartition {
327 operation: RepartitionOperation::new(from_exprs, vec![merged_expr]),
328 })
329 }
330
331 fn parse_repartition_expr_list(&mut self) -> Result<Vec<Expr>> {
332 self.parser
333 .expect_token(&Token::LParen)
334 .context(error::SyntaxSnafu)?;
335
336 if matches!(self.parser.peek_token().token, Token::RParen) {
337 return self.expected(
338 "expression inside REPARTITION clause",
339 self.parser.peek_token(),
340 );
341 }
342
343 let mut exprs = Vec::new();
344 loop {
345 let expr = self.parser.parse_expr().context(error::SyntaxSnafu)?;
346 exprs.push(expr);
347
348 match self.parser.peek_token().token {
349 Token::Comma => {
350 self.parser.next_token();
351 if matches!(self.parser.peek_token().token, Token::RParen) {
352 self.parser.next_token();
353 break;
354 }
355 }
356 Token::RParen => {
357 self.parser.next_token();
358 break;
359 }
360 _ => {
361 return self.expected(
362 "comma or right parenthesis after repartition expression",
363 self.parser.peek_token(),
364 );
365 }
366 }
367 }
368
369 Ok(exprs)
370 }
371
372 fn parse_alter_table_add(&mut self) -> Result<AlterTableOperation> {
373 let _ = self.parser.next_token();
374 if let Some(constraint) = self
375 .parser
376 .parse_optional_table_constraint()
377 .context(error::SyntaxSnafu)?
378 {
379 Ok(AlterTableOperation::AddConstraint(constraint))
380 } else {
381 self.parser.prev_token();
382 let mut add_columns = vec![self.parse_add_column()?];
383 while self.parser.consume_token(&Token::Comma) {
384 if matches!(
385 self.parser.peek_token().token,
386 Token::SemiColon | Token::EOF
387 ) || self.matches_keyword(Keyword::WITH)
388 {
389 break;
390 }
391 add_columns.push(self.parse_add_column()?);
392 }
393 Ok(AlterTableOperation::AddColumns { add_columns })
394 }
395 }
396
397 fn parse_add_column(&mut self) -> Result<AddColumn> {
398 self.parser
399 .expect_keyword(Keyword::ADD)
400 .context(error::SyntaxSnafu)?;
401 let _ = self.parser.parse_keyword(Keyword::COLUMN);
402 let add_if_not_exists =
403 self.parser
404 .parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
405 let column = self.parse_column_def(true)?;
406
407 ensure!(
409 column.extensions.fulltext_index_options.is_none()
410 && column.extensions.skipping_index_options.is_none()
411 && column.extensions.inverted_index_options.is_none()
412 && !column
413 .options()
414 .iter()
415 .any(|option| matches!(option.option, ColumnOption::DialectSpecific(_))),
416 InvalidColumnOptionSnafu {
417 name: column.name().to_string(),
418 msg: "ADD COLUMN does not support inline indexes or TIME INDEX",
419 }
420 );
421
422 let location = if self.parser.parse_keyword(Keyword::FIRST) {
423 Some(AddColumnLocation::First)
424 } else if matches!(self.parser.peek_token().token, Token::Word(word) if word.value.eq_ignore_ascii_case("AFTER"))
425 {
426 self.parser.next_token();
427 let name = Self::canonicalize_identifier(
428 self.parser.parse_identifier().context(error::SyntaxSnafu)?,
429 );
430 Some(AddColumnLocation::After {
431 column_name: name.value,
432 })
433 } else {
434 None
435 };
436 Ok(AddColumn {
437 column,
438 location,
439 add_if_not_exists,
440 })
441 }
442
443 fn parse_alter_table_drop_default(
444 &mut self,
445 column_name: Ident,
446 ) -> Result<AlterTableOperation> {
447 let drop_default = DropDefaultsOperation(column_name);
448 if self.parser.consume_token(&Token::Comma) {
449 let mut columns = self
450 .parser
451 .parse_comma_separated(parse_alter_column_drop_default)
452 .context(error::SyntaxSnafu)?;
453 columns.insert(0, drop_default);
454 Ok(AlterTableOperation::DropDefaults { columns })
455 } else {
456 Ok(AlterTableOperation::DropDefaults {
457 columns: vec![drop_default],
458 })
459 }
460 }
461
462 fn parse_alter_table_set_default(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
463 let default_constraint = self.parser.parse_expr().context(error::SyntaxSnafu)?;
464 let set_default = SetDefaultsOperation {
465 column_name,
466 default_constraint,
467 };
468 if self.parser.consume_token(&Token::Comma) {
469 let mut defaults = self
470 .parser
471 .parse_comma_separated(parse_alter_column_set_default)
472 .context(error::SyntaxSnafu)?;
473 defaults.insert(0, set_default);
474 Ok(AlterTableOperation::SetDefaults { defaults })
475 } else {
476 Ok(AlterTableOperation::SetDefaults {
477 defaults: vec![set_default],
478 })
479 }
480 }
481
482 fn parse_alter_table_modify(&mut self) -> Result<AlterTableOperation> {
483 let _ = self.parser.next_token();
484 self.parser
485 .expect_keyword(Keyword::COLUMN)
486 .context(error::SyntaxSnafu)?;
487 let column_name = Self::canonicalize_identifier(
488 self.parser.parse_identifier().context(error::SyntaxSnafu)?,
489 );
490
491 match self.parser.peek_token().token {
492 Token::Word(w) => {
493 if w.value.eq_ignore_ascii_case("UNSET") {
494 self.parser.next_token();
496 self.parse_alter_column_unset_index(column_name)
497 } else if w.keyword == Keyword::SET {
498 self.parser.next_token();
500 if let Token::Word(w) = self.parser.peek_token().token
501 && matches!(w.keyword, Keyword::DEFAULT)
502 {
503 self.parser
504 .expect_keyword(Keyword::DEFAULT)
505 .context(error::SyntaxSnafu)?;
506 self.parse_alter_table_set_default(column_name)
507 } else {
508 self.parse_alter_column_set_index(column_name)
509 }
510 } else if w.keyword == Keyword::DROP {
511 self.parser.next_token();
513 self.parser
514 .expect_keyword(Keyword::DEFAULT)
515 .context(error::SyntaxSnafu)?;
516 self.parse_alter_table_drop_default(column_name)
517 } else {
518 if let Some((_, json2_options)) =
519 parse_json2_type_and_options(&mut self.parser)?
520 {
521 return Ok(AlterTableOperation::SetJsonSettings {
522 column_name,
523 json2_options,
524 });
525 }
526
527 Ok(AlterTableOperation::ModifyColumnType {
528 column_name,
529 target_type: self.parser.parse_data_type().context(error::SyntaxSnafu)?,
530 json2_options: None,
531 })
532 }
533 }
534 _ => self.expected(
535 "SET or UNSET or data type after MODIFY COLUMN",
536 self.parser.peek_token(),
537 )?,
538 }
539 }
540
541 fn parse_alter_column_unset_index(
542 &mut self,
543 column_name: Ident,
544 ) -> Result<AlterTableOperation> {
545 match self.parser.next_token() {
546 TokenWithSpan {
547 token: Token::Word(w),
548 ..
549 } if w.keyword == Keyword::FULLTEXT => {
550 self.parser
551 .expect_keyword(Keyword::INDEX)
552 .context(error::SyntaxSnafu)?;
553 Ok(AlterTableOperation::UnsetIndex {
554 options: UnsetIndexOperation::Fulltext { column_name },
555 })
556 }
557
558 TokenWithSpan {
559 token: Token::Word(w),
560 ..
561 } if w.value.eq_ignore_ascii_case(INVERTED) => {
562 self.parser
563 .expect_keyword(Keyword::INDEX)
564 .context(error::SyntaxSnafu)?;
565 Ok(AlterTableOperation::UnsetIndex {
566 options: UnsetIndexOperation::Inverted { column_name },
567 })
568 }
569
570 TokenWithSpan {
571 token: Token::Word(w),
572 ..
573 } if w.value.eq_ignore_ascii_case("SKIPPING") => {
574 self.parser
575 .expect_keyword(Keyword::INDEX)
576 .context(error::SyntaxSnafu)?;
577 Ok(AlterTableOperation::UnsetIndex {
578 options: UnsetIndexOperation::Skipping { column_name },
579 })
580 }
581 _ => self.expected(
582 format!(
583 "{:?} OR INVERTED INDEX OR SKIPPING INDEX",
584 Keyword::FULLTEXT
585 )
586 .as_str(),
587 self.parser.peek_token(),
588 ),
589 }
590 }
591
592 fn parse_alter_column_set_index(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
593 match self.parser.next_token() {
594 TokenWithSpan {
595 token: Token::Word(w),
596 ..
597 } if w.keyword == Keyword::FULLTEXT => {
598 self.parser
599 .expect_keyword(Keyword::INDEX)
600 .context(error::SyntaxSnafu)?;
601 self.parse_alter_column_fulltext(column_name)
602 }
603
604 TokenWithSpan {
605 token: Token::Word(w),
606 ..
607 } if w.value.eq_ignore_ascii_case(INVERTED) => {
608 self.parser
609 .expect_keyword(Keyword::INDEX)
610 .context(error::SyntaxSnafu)?;
611 Ok(AlterTableOperation::SetIndex {
612 options: SetIndexOperation::Inverted { column_name },
613 })
614 }
615
616 TokenWithSpan {
617 token: Token::Word(w),
618 ..
619 } if w.value.eq_ignore_ascii_case("SKIPPING") => {
620 self.parser
621 .expect_keyword(Keyword::INDEX)
622 .context(error::SyntaxSnafu)?;
623 self.parse_alter_column_skipping(column_name)
624 }
625 t => self.expected(
626 format!("{:?} OR INVERTED OR SKIPPING INDEX", Keyword::FULLTEXT).as_str(),
627 t,
628 ),
629 }
630 }
631
632 fn parse_alter_column_fulltext(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
633 let mut options = self
634 .parser
635 .parse_options(Keyword::WITH)
636 .context(error::SyntaxSnafu)?
637 .into_iter()
638 .map(parse_option_string)
639 .collect::<Result<HashMap<String, OptionValue>>>()?;
640
641 for key in options.keys() {
642 ensure!(
643 validate_column_fulltext_create_option(key),
644 InvalidColumnOptionSnafu {
645 name: column_name.to_string(),
646 msg: format!("invalid FULLTEXT option: {key}"),
647 }
648 );
649 }
650
651 options.insert(
652 COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE.to_string(),
653 "true".to_string().into(),
654 );
655
656 let options = OptionMap::new(options).into_map();
657 Ok(AlterTableOperation::SetIndex {
658 options: SetIndexOperation::Fulltext {
659 column_name,
660 options: options.try_into().context(SetFulltextOptionSnafu)?,
661 },
662 })
663 }
664
665 fn parse_alter_column_skipping(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
666 let options = self
667 .parser
668 .parse_options(Keyword::WITH)
669 .context(error::SyntaxSnafu)?
670 .into_iter()
671 .map(parse_option_string)
672 .collect::<Result<Vec<_>>>()?;
673
674 for (key, _) in options.iter() {
675 ensure!(
676 validate_column_skipping_index_create_option(key),
677 InvalidColumnOptionSnafu {
678 name: column_name.to_string(),
679 msg: format!("invalid SKIPPING INDEX option: {key}"),
680 }
681 );
682 }
683
684 let options = OptionMap::new(options).into_map();
685 Ok(AlterTableOperation::SetIndex {
686 options: SetIndexOperation::Skipping {
687 column_name,
688 options: options
689 .try_into()
690 .context(error::SetSkippingIndexOptionSnafu)?,
691 },
692 })
693 }
694}
695
696fn parse_alter_column_drop_default(
697 parser: &mut Parser,
698) -> std::result::Result<DropDefaultsOperation, ParserError> {
699 parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
700 let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
701 let t = parser.next_token();
702 match t.token {
703 Token::Word(w) if w.keyword == Keyword::DROP => {
704 parser.expect_keyword(Keyword::DEFAULT)?;
705 Ok(DropDefaultsOperation(column_name))
706 }
707 _ => Err(ParserError::ParserError(format!(
708 "Unexpected keyword, expect DROP, got: `{t}`"
709 ))),
710 }
711}
712
713fn parse_alter_column_set_default(
714 parser: &mut Parser,
715) -> std::result::Result<SetDefaultsOperation, ParserError> {
716 parser.expect_keywords(&[Keyword::MODIFY, Keyword::COLUMN])?;
717 let column_name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
718 let t = parser.next_token();
719 match t.token {
720 Token::Word(w) if w.keyword == Keyword::SET => {
721 parser.expect_keyword(Keyword::DEFAULT)?;
722 if let Ok(default_constraint) = parser.parse_expr() {
723 Ok(SetDefaultsOperation {
724 column_name,
725 default_constraint,
726 })
727 } else {
728 Err(ParserError::ParserError(format!(
729 "Invalid default value after SET DEFAULT, got: `{}`",
730 parser.peek_token()
731 )))
732 }
733 }
734 _ => Err(ParserError::ParserError(format!(
735 "Unexpected keyword, expect SET, got: `{t}`"
736 ))),
737 }
738}
739
740fn parse_string_options(parser: &mut Parser) -> std::result::Result<(String, String), ParserError> {
742 let name = parser.parse_literal_string()?;
743 parser.expect_token(&Token::Eq)?;
744 let value = if parser.parse_keyword(Keyword::NULL) {
745 "".to_string()
746 } else {
747 let next_token = parser.peek_token();
748 if let Token::Number(number_as_string, _) = next_token.token {
749 parser.advance_token();
750 number_as_string
751 } else {
752 parser.parse_literal_string().map_err(|_|{
753 ParserError::ParserError(format!("Unexpected option value for alter table statements, expect string literal, numeric literal or NULL, got: `{}`", next_token))
754 })?
755 }
756 };
757 Ok((name, value))
758}
759
760fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
762 parser.parse_literal_string()
763}
764
765#[cfg(test)]
766mod tests {
767 use std::assert_matches;
768
769 use common_error::ext::ErrorExt;
770 use datatypes::schema::{FulltextAnalyzer, FulltextBackend, FulltextOptions};
771 use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType};
772
773 use super::*;
774 use crate::ast::ObjectNamePartExt;
775 use crate::dialect::GreptimeDbDialect;
776 use crate::parser::ParseOptions;
777 use crate::statements::alter::AlterDatabaseOperation;
778 use crate::statements::create::{Column, ColumnExtensions};
779
780 #[test]
781 fn test_add_column_matches_create_table() {
782 for (definition, location) in [
783 (
784 "payload JSON2(max_auto_expanded_paths = 100, service STRING) NOT NULL",
785 "AFTER ts",
786 ),
787 ("payload JSON2(service STRING) NULL", "FIRST"),
788 ("embedding VECTOR(3) NOT NULL", "AFTER ts"),
789 ("reading INT DEFAULT 42 COMMENT 'value'", ""),
790 ("\"select\" STRING NULL", ""),
791 ] {
792 let parse = |sql: &str| {
793 ParserContext::create_with_dialect(
794 sql,
795 &GreptimeDbDialect {},
796 ParseOptions::default(),
797 )
798 .unwrap()
799 .pop()
800 .unwrap()
801 };
802 let Statement::CreateTable(create) = parse(&format!(
803 "CREATE TABLE t ({definition}, n INT, ts TIMESTAMP TIME INDEX)"
804 )) else {
805 unreachable!()
806 };
807 let Statement::AlterTable(alter) = parse(&format!(
808 "ALTER TABLE t ADD COLUMN {definition} {location}, ADD COLUMN n INT"
809 )) else {
810 unreachable!()
811 };
812 let AlterTableOperation::AddColumns { add_columns } = alter.alter_operation() else {
813 unreachable!()
814 };
815 assert_eq!(add_columns.len(), 2);
816 for (expected, actual) in create.columns.iter().zip(add_columns) {
817 assert_eq!(expected, &actual.column);
818 }
819 let Statement::AlterTable(roundtrip) = parse(&alter.to_string()) else {
820 unreachable!()
821 };
822 assert_eq!(alter, roundtrip);
823 }
824 }
825
826 #[test]
827 fn test_add_column_rejects_unsupported_attributes() {
828 for definition in [
829 "payload STRING FULLTEXT INDEX",
830 "payload STRING SKIPPING INDEX",
831 "payload STRING INVERTED INDEX",
832 "ts TIMESTAMP TIME INDEX",
833 ] {
834 let err = ParserContext::create_with_dialect(
835 &format!("ALTER TABLE t ADD COLUMN {definition}"),
836 &GreptimeDbDialect {},
837 ParseOptions::default(),
838 )
839 .unwrap_err();
840 assert!(
841 err.to_string()
842 .contains("ADD COLUMN does not support inline indexes or TIME INDEX"),
843 "{err}"
844 );
845 }
846 }
847
848 #[test]
849 fn test_parse_alter_database() {
850 let sql = "ALTER DATABASE test_db SET 'a'='A', 'b' = 'B'";
851 let mut result =
852 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
853 .unwrap();
854 assert_eq!(1, result.len());
855
856 let statement = result.remove(0);
857 assert_matches!(statement, Statement::AlterDatabase { .. });
858 match statement {
859 Statement::AlterDatabase(alter_database) => {
860 assert_eq!("test_db", alter_database.database_name().0[0].to_string());
861
862 let alter_operation = alter_database.alter_operation();
863 assert_matches!(
864 alter_operation,
865 AlterDatabaseOperation::SetDatabaseOption { .. }
866 );
867 match alter_operation {
868 AlterDatabaseOperation::SetDatabaseOption { options } => {
869 assert_eq!(2, options.len());
870 assert_eq!("a", options[0].key);
871 assert_eq!("A", options[0].value);
872 assert_eq!("b", options[1].key);
873 assert_eq!("B", options[1].value);
874 }
875 _ => unreachable!(),
876 }
877 }
878 _ => unreachable!(),
879 }
880 let sql = "ALTER DATABASE test_db UNSET 'a', 'b'";
881 let mut result =
882 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
883 .unwrap();
884 assert_eq!(1, result.len());
885 let statement = result.remove(0);
886 assert_matches!(statement, Statement::AlterDatabase { .. });
887 match statement {
888 Statement::AlterDatabase(alter_database) => {
889 assert_eq!("test_db", alter_database.database_name().0[0].to_string());
890 let alter_operation = alter_database.alter_operation();
891 assert_matches!(
892 alter_operation,
893 AlterDatabaseOperation::UnsetDatabaseOption { .. }
894 );
895 match alter_operation {
896 AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
897 assert_eq!(2, keys.len());
898 assert_eq!("a", keys[0]);
899 assert_eq!("b", keys[1]);
900 }
901 _ => unreachable!(),
902 }
903 }
904 _ => unreachable!(),
905 }
906 }
907
908 #[test]
909 fn test_parse_alter_add_column() {
910 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null;";
911 let mut result =
912 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
913 .unwrap();
914 assert_eq!(1, result.len());
915
916 let statement = result.remove(0);
917 assert_matches!(statement, Statement::AlterTable { .. });
918 match statement {
919 Statement::AlterTable(alter_table) => {
920 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
921
922 let alter_operation = alter_table.alter_operation();
923 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
924 match alter_operation {
925 AlterTableOperation::AddColumns { add_columns } => {
926 assert_eq!(add_columns.len(), 1);
927 assert_eq!("tagk_i", add_columns[0].column.column_def.name.value);
928 assert_eq!(
929 DataType::String(None),
930 add_columns[0].column.column_def.data_type
931 );
932 assert!(
933 add_columns[0]
934 .column
935 .column_def
936 .options
937 .iter()
938 .any(|o| matches!(o.option, ColumnOption::Null))
939 );
940 assert_eq!(&None, &add_columns[0].location);
941 }
942 _ => unreachable!(),
943 }
944 }
945 _ => unreachable!(),
946 }
947 }
948
949 #[test]
950 fn test_parse_alter_add_column_with_first() {
951 let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null FIRST;";
952 let mut result =
953 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
954 .unwrap();
955 assert_eq!(1, result.len());
956
957 let statement = result.remove(0);
958 assert_matches!(statement, Statement::AlterTable { .. });
959 match statement {
960 Statement::AlterTable(alter_table) => {
961 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
962
963 let alter_operation = alter_table.alter_operation();
964 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
965 match alter_operation {
966 AlterTableOperation::AddColumns { add_columns } => {
967 assert_eq!("tagk_i", add_columns[0].column.column_def.name.value);
968 assert_eq!(
969 DataType::String(None),
970 add_columns[0].column.column_def.data_type
971 );
972 assert!(
973 add_columns[0]
974 .column
975 .column_def
976 .options
977 .iter()
978 .any(|o| matches!(o.option, ColumnOption::Null))
979 );
980 assert_eq!(&Some(AddColumnLocation::First), &add_columns[0].location);
981 }
982 _ => unreachable!(),
983 }
984 }
985 _ => unreachable!(),
986 }
987 }
988
989 #[test]
990 fn test_parse_alter_add_column_with_after() {
991 let sql =
992 "ALTER TABLE my_metric_1 ADD tagk_i STRING Null AFTER ts, add column tagl_i String;";
993 let mut result =
994 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
995 .unwrap();
996 assert_eq!(1, result.len());
997
998 let statement = result.remove(0);
999 assert_matches!(statement, Statement::AlterTable { .. });
1000 match statement {
1001 Statement::AlterTable(alter_table) => {
1002 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
1003
1004 let alter_operation = alter_table.alter_operation();
1005 assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
1006 match alter_operation {
1007 AlterTableOperation::AddColumns { add_columns } => {
1008 let expecteds: Vec<(Option<AddColumnLocation>, ColumnDef)> = vec![
1009 (
1010 Some(AddColumnLocation::After {
1011 column_name: "ts".to_string(),
1012 }),
1013 ColumnDef {
1014 name: Ident::new("tagk_i"),
1015 data_type: DataType::String(None),
1016 options: vec![ColumnOptionDef {
1017 name: None,
1018 option: ColumnOption::Null,
1019 }],
1020 },
1021 ),
1022 (
1023 None,
1024 ColumnDef {
1025 name: Ident::new("tagl_i"),
1026 data_type: DataType::String(None),
1027 options: vec![],
1028 },
1029 ),
1030 ];
1031 for (add_column, expected) in add_columns
1032 .iter()
1033 .zip(expecteds)
1034 .collect::<Vec<(&AddColumn, (Option<AddColumnLocation>, ColumnDef))>>()
1035 {
1036 assert_eq!(add_column.column.column_def, expected.1);
1037 assert_eq!(&expected.0, &add_column.location);
1038 }
1039 }
1040 _ => unreachable!(),
1041 }
1042 }
1043 _ => unreachable!(),
1044 }
1045 }
1046
1047 #[test]
1048 fn test_parse_add_column_if_not_exists() {
1049 let sql = "ALTER TABLE test ADD COLUMN IF NOT EXISTS a INTEGER, ADD COLUMN b STRING, ADD COLUMN IF NOT EXISTS c INT;";
1050 let mut result =
1051 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1052 .unwrap();
1053 assert_eq!(result.len(), 1);
1054 let statement = result.remove(0);
1055 assert_matches!(statement, Statement::AlterTable { .. });
1056 match statement {
1057 Statement::AlterTable(alter) => {
1058 assert_eq!(alter.table_name.0[0].to_string(), "test");
1059 assert_matches!(
1060 alter.alter_operation,
1061 AlterTableOperation::AddColumns { .. }
1062 );
1063 match alter.alter_operation {
1064 AlterTableOperation::AddColumns { add_columns } => {
1065 let expected = [
1066 AddColumn {
1067 column: Column {
1068 column_def: ColumnDef {
1069 name: Ident::new("a"),
1070 data_type: DataType::Integer(None),
1071 options: vec![],
1072 },
1073 extensions: ColumnExtensions::default(),
1074 },
1075 location: None,
1076 add_if_not_exists: true,
1077 },
1078 AddColumn {
1079 column: Column {
1080 column_def: ColumnDef {
1081 name: Ident::new("b"),
1082 data_type: DataType::String(None),
1083 options: vec![],
1084 },
1085 extensions: ColumnExtensions::default(),
1086 },
1087 location: None,
1088 add_if_not_exists: false,
1089 },
1090 AddColumn {
1091 column: Column {
1092 column_def: ColumnDef {
1093 name: Ident::new("c"),
1094 data_type: DataType::Int(None),
1095 options: vec![],
1096 },
1097 extensions: ColumnExtensions::default(),
1098 },
1099 location: None,
1100 add_if_not_exists: true,
1101 },
1102 ];
1103 for (idx, add_column) in add_columns.into_iter().enumerate() {
1104 assert_eq!(add_column, expected[idx]);
1105 }
1106 }
1107 _ => unreachable!(),
1108 }
1109 }
1110 _ => unreachable!(),
1111 }
1112 }
1113
1114 #[test]
1115 fn test_parse_alter_table_repartition() {
1116 let sql = r#"
1117ALTER TABLE t REPARTITION (
1118 device_id < 100
1119) INTO (
1120 device_id < 100 AND area < 'South',
1121 device_id < 100 AND area >= 'South',
1122);"#;
1123 let mut result =
1124 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1125 .unwrap();
1126 assert_eq!(1, result.len());
1127
1128 let statement = result.remove(0);
1129 assert_matches!(statement, Statement::AlterTable { .. });
1130 if let Statement::AlterTable(alter_table) = statement {
1131 assert_matches!(
1132 alter_table.alter_operation(),
1133 AlterTableOperation::Repartition { .. }
1134 );
1135
1136 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1137 assert_eq!(operation.from_exprs.len(), 1);
1138 assert_eq!(operation.from_exprs[0].to_string(), "device_id < 100");
1139 assert_eq!(operation.into_exprs.len(), 2);
1140 assert!(operation.partition_columns.is_none());
1141 assert_eq!(
1142 operation.into_exprs[0].to_string(),
1143 "device_id < 100 AND area < 'South'"
1144 );
1145 assert_eq!(
1146 operation.into_exprs[1].to_string(),
1147 "device_id < 100 AND area >= 'South'"
1148 );
1149 }
1150 }
1151 }
1152
1153 #[test]
1154 fn test_parse_alter_table_repartition_on_columns() {
1155 let sql = r#"
1156ALTER TABLE t REPARTITION (
1157 device_id < 100
1158)
1159ON COLUMNS (device_id, area)
1160INTO (
1161 device_id < 100 AND area < 'South',
1162 device_id < 100 AND area >= 'South'
1163);"#;
1164 let mut result =
1165 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1166 .unwrap();
1167 assert_eq!(1, result.len());
1168
1169 let statement = result.remove(0);
1170 assert_matches!(statement, Statement::AlterTable { .. });
1171 if let Statement::AlterTable(alter_table) = statement {
1172 assert_matches!(
1173 alter_table.alter_operation(),
1174 AlterTableOperation::Repartition { .. }
1175 );
1176
1177 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1178 assert_eq!(operation.from_exprs.len(), 1);
1179 assert_eq!(operation.from_exprs[0].to_string(), "device_id < 100");
1180 assert_eq!(operation.into_exprs.len(), 2);
1181 assert_eq!(
1182 operation
1183 .partition_columns
1184 .as_ref()
1185 .unwrap()
1186 .iter()
1187 .map(|ident| ident.value.as_str())
1188 .collect::<Vec<_>>(),
1189 vec!["device_id", "area"]
1190 );
1191 assert_eq!(
1192 operation.to_string(),
1193 "(device_id < 100) ON COLUMNS (device_id, area) INTO (device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South')"
1194 );
1195 }
1196 }
1197 }
1198
1199 #[test]
1200 fn test_parse_alter_table_repartition_on_columns_with_options() {
1201 let sql = r#"
1202ALTER TABLE t REPARTITION (
1203 device_id < 100
1204)
1205ON COLUMNS (device_id, area)
1206INTO (
1207 device_id < 100 AND area < 'South',
1208 device_id < 100 AND area >= 'South'
1209)
1210WITH (
1211 TIMEOUT = '5m',
1212 WAIT = false
1213);"#;
1214 let mut result =
1215 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1216 .unwrap();
1217 assert_eq!(1, result.len());
1218
1219 let statement = result.remove(0);
1220 assert_matches!(statement, Statement::AlterTable { .. });
1221 if let Statement::AlterTable(alter_table) = statement {
1222 assert_matches!(
1223 alter_table.alter_operation(),
1224 AlterTableOperation::Repartition { .. }
1225 );
1226
1227 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1228 assert_eq!(
1229 operation
1230 .partition_columns
1231 .as_ref()
1232 .unwrap()
1233 .iter()
1234 .map(|ident| ident.value.as_str())
1235 .collect::<Vec<_>>(),
1236 vec!["device_id", "area"]
1237 );
1238 }
1239
1240 let options = alter_table.options().to_str_map();
1241 assert_eq!(options.get("timeout").unwrap(), &"5m");
1242 assert_eq!(options.get("wait").unwrap(), &"false");
1243 assert_eq!(options.len(), 2);
1244 }
1245 }
1246
1247 #[test]
1248 fn test_parse_alter_table_partition_on_columns() {
1249 let sql = r#"
1250ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id, area) (
1251 device_id < 100 AND area < 'South',
1252 device_id < 100 AND area >= 'South',
1253 device_id >= 100 AND area <= 'East',
1254 device_id >= 100 AND area > 'East'
1255);"#;
1256 let mut result =
1257 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1258 .unwrap();
1259 assert_eq!(1, result.len());
1260
1261 let statement = result.remove(0);
1262 assert_matches!(statement, Statement::AlterTable { .. });
1263 if let Statement::AlterTable(alter_table) = statement {
1264 assert_matches!(
1265 alter_table.alter_operation(),
1266 AlterTableOperation::Partition { .. }
1267 );
1268
1269 if let AlterTableOperation::Partition { partitions } = alter_table.alter_operation() {
1270 assert_eq!(partitions.column_list.len(), 2);
1271 assert_eq!(partitions.column_list[0].value, "device_id");
1272 assert_eq!(partitions.column_list[1].value, "area");
1273 assert_eq!(partitions.exprs.len(), 4);
1274 assert_eq!(
1275 partitions.exprs[0].to_string(),
1276 "device_id < 100 AND area < 'South'"
1277 );
1278 assert_eq!(
1279 partitions.exprs[3].to_string(),
1280 "device_id >= 100 AND area > 'East'"
1281 );
1282 }
1283 }
1284 }
1285
1286 #[test]
1287 fn test_parse_alter_table_partition_on_columns_with_options() {
1288 let sql = r#"
1289ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id) (
1290 device_id < 100,
1291 device_id >= 100
1292) WITH (
1293 TIMEOUT = '5m',
1294 WAIT = false
1295);"#;
1296 let mut result =
1297 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1298 .unwrap();
1299 assert_eq!(1, result.len());
1300
1301 let statement = result.remove(0);
1302 assert_matches!(statement, Statement::AlterTable { .. });
1303 if let Statement::AlterTable(alter_table) = statement {
1304 assert_matches!(
1305 alter_table.alter_operation(),
1306 AlterTableOperation::Partition { .. }
1307 );
1308 let options = alter_table.options().to_str_map();
1309 assert_eq!(options.get("timeout").unwrap(), &"5m");
1310 assert_eq!(options.get("wait").unwrap(), &"false");
1311 assert_eq!(options.len(), 2);
1312 }
1313 }
1314
1315 #[test]
1316 fn test_parse_alter_table_partition_on_columns_empty_columns() {
1317 let sql = r#"
1318ALTER TABLE sensor_readings PARTITION ON COLUMNS () (
1319 device_id < 100
1320);"#;
1321 let result =
1322 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1323
1324 assert!(result.is_err());
1325 }
1326
1327 #[test]
1328 fn test_parse_alter_table_partition_on_columns_empty_exprs() {
1329 let sql = r#"
1330ALTER TABLE sensor_readings PARTITION ON COLUMNS (device_id) ();"#;
1331 let result =
1332 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1333 .unwrap_err();
1334
1335 assert_eq!(
1336 result.output_msg(),
1337 "Invalid SQL syntax: sql parser error: PARTITION ON COLUMNS requires at least one partition expression"
1338 );
1339 }
1340
1341 #[test]
1342 fn test_parse_alter_table_split_partition() {
1343 let sql = r#"
1344ALTER TABLE metrics SPLIT PARTITION (
1345 device_id < 100
1346) INTO (
1347 device_id < 100 AND area < 'South',
1348 device_id < 100 AND area >= 'South'
1349);"#;
1350 let mut result =
1351 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1352 .unwrap();
1353 assert_eq!(1, result.len());
1354
1355 let statement = result.remove(0);
1356 assert_matches!(statement, Statement::AlterTable { .. });
1357 if let Statement::AlterTable(alter_table) = statement {
1358 assert_matches!(
1359 alter_table.alter_operation(),
1360 AlterTableOperation::Repartition { .. }
1361 );
1362
1363 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1364 assert_eq!(operation.from_exprs.len(), 1);
1365 assert_eq!(operation.from_exprs[0].to_string(), "device_id < 100");
1366 assert_eq!(operation.into_exprs.len(), 2);
1367 assert!(operation.partition_columns.is_none());
1368 assert_eq!(
1369 operation.into_exprs[0].to_string(),
1370 "device_id < 100 AND area < 'South'"
1371 );
1372 assert_eq!(
1373 operation.into_exprs[1].to_string(),
1374 "device_id < 100 AND area >= 'South'"
1375 );
1376 }
1377 }
1378 }
1379
1380 #[test]
1381 fn test_parse_alter_table_split_partition_on_columns() {
1382 let sql = r#"
1383ALTER TABLE metrics SPLIT PARTITION (
1384 device_id < 100
1385)
1386ON COLUMNS (device_id, area)
1387INTO (
1388 device_id < 100 AND area < 'South',
1389 device_id < 100 AND area >= 'South'
1390);"#;
1391 let mut result =
1392 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1393 .unwrap();
1394 assert_eq!(1, result.len());
1395
1396 let statement = result.remove(0);
1397 assert_matches!(statement, Statement::AlterTable { .. });
1398 if let Statement::AlterTable(alter_table) = statement {
1399 assert_matches!(
1400 alter_table.alter_operation(),
1401 AlterTableOperation::Repartition { .. }
1402 );
1403
1404 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1405 assert_eq!(operation.from_exprs.len(), 1);
1406 assert_eq!(operation.from_exprs[0].to_string(), "device_id < 100");
1407 assert_eq!(operation.into_exprs.len(), 2);
1408 assert_eq!(
1409 operation
1410 .partition_columns
1411 .as_ref()
1412 .unwrap()
1413 .iter()
1414 .map(|ident| ident.value.as_str())
1415 .collect::<Vec<_>>(),
1416 vec!["device_id", "area"]
1417 );
1418 assert_eq!(
1419 operation.to_string(),
1420 "(device_id < 100) ON COLUMNS (device_id, area) INTO (device_id < 100 AND area < 'South', device_id < 100 AND area >= 'South')"
1421 );
1422 }
1423 }
1424 }
1425
1426 #[test]
1427 fn test_parse_alter_table_split_partition_on_columns_empty_columns() {
1428 let sql = r#"
1429ALTER TABLE metrics SPLIT PARTITION (
1430 device_id < 100
1431)
1432ON COLUMNS ()
1433INTO (
1434 device_id < 100 AND area < 'South',
1435 device_id < 100 AND area >= 'South'
1436);"#;
1437 let result =
1438 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default());
1439
1440 assert!(result.is_err());
1441 }
1442
1443 #[test]
1444 fn test_parse_alter_table_split_partition_on_columns_wrong_order() {
1445 let sql = r#"
1446ALTER TABLE metrics SPLIT PARTITION (
1447 device_id < 100
1448)
1449INTO (
1450 device_id < 100 AND area < 'South',
1451 device_id < 100 AND area >= 'South'
1452)
1453ON COLUMNS (device_id, area);"#;
1454 let result =
1455 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1456 .unwrap_err();
1457
1458 assert_eq!(
1459 result.output_msg(),
1460 "Invalid SQL syntax: sql parser error: Expected end of SPLIT PARTITION clause, found: ON"
1461 );
1462 }
1463
1464 #[test]
1465 fn test_parse_alter_table_merge_partition() {
1466 let sql = r#"
1467ALTER TABLE metrics MERGE PARTITION (
1468 device_id < 100,
1469 device_id >= 100
1470);"#;
1471 let mut result =
1472 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1473 .unwrap();
1474 assert_eq!(1, result.len());
1475
1476 let statement = result.remove(0);
1477 assert_matches!(statement, Statement::AlterTable { .. });
1478 if let Statement::AlterTable(alter_table) = statement {
1479 assert_matches!(
1480 alter_table.alter_operation(),
1481 AlterTableOperation::Repartition { .. }
1482 );
1483
1484 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1485 assert_eq!(operation.from_exprs.len(), 2);
1486 assert_eq!(operation.from_exprs[0].to_string(), "device_id < 100");
1487 assert_eq!(operation.from_exprs[1].to_string(), "device_id >= 100");
1488 assert_eq!(operation.into_exprs.len(), 1);
1489 assert_eq!(
1490 operation.into_exprs[0].to_string(),
1491 "device_id < 100 OR device_id >= 100"
1492 );
1493 }
1494 }
1495 }
1496
1497 #[test]
1498 fn test_parse_alter_table_merge_partition_on_columns_rejected() {
1499 let sql = r#"
1500ALTER TABLE metrics MERGE PARTITION (
1501 device_id < 100,
1502 device_id >= 100
1503)
1504ON COLUMNS (device_id, area)
1505INTO (
1506 device_id >= 0
1507);"#;
1508 let result =
1509 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1510 .unwrap_err();
1511
1512 assert_eq!(
1513 result.output_msg(),
1514 "SQL statement is not supported, keyword: ON"
1515 );
1516 }
1517
1518 #[test]
1519 fn test_parse_alter_table_merge_partition_with_options() {
1520 let sql = r#"
1521ALTER TABLE alter_repartition_table MERGE PARTITION (
1522 device_id < 100 AND area < 'South',
1523 device_id < 100 AND area >= 'South'
1524) WITH (
1525 TIMEOUT = '5m',
1526 WAIT = false
1527);"#;
1528 let mut result =
1529 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1530 .unwrap();
1531 assert_eq!(1, result.len());
1532
1533 let statement = result.remove(0);
1534 assert_matches!(statement, Statement::AlterTable { .. });
1535 if let Statement::AlterTable(alter_table) = statement {
1536 assert_matches!(
1537 alter_table.alter_operation(),
1538 AlterTableOperation::Repartition { .. }
1539 );
1540
1541 if let AlterTableOperation::Repartition { operation } = alter_table.alter_operation() {
1542 assert_eq!(operation.from_exprs.len(), 2);
1543 assert_eq!(
1544 operation.from_exprs[0].to_string(),
1545 "device_id < 100 AND area < 'South'"
1546 );
1547 assert_eq!(
1548 operation.from_exprs[1].to_string(),
1549 "device_id < 100 AND area >= 'South'"
1550 );
1551 assert_eq!(operation.into_exprs.len(), 1);
1552 }
1553
1554 let options = alter_table.options().to_str_map();
1556 assert_eq!(options.get("timeout").unwrap(), &"5m");
1557 assert_eq!(options.get("wait").unwrap(), &"false");
1558 assert_eq!(options.len(), 2);
1559 }
1560 }
1561
1562 #[test]
1563 fn test_parse_alter_table_repartition_multiple() {
1564 let sql = r#"
1565ALTER TABLE metrics REPARTITION
1566(
1567 a < 10,
1568 a >= 10
1569) INTO (
1570 a < 20
1571),
1572(
1573 b < 20
1574) INTO (
1575 b < 10,
1576 b >= 10,
1577);"#;
1578
1579 let result =
1580 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1581 .unwrap_err();
1582 assert_eq!(
1583 result.output_msg(),
1584 "Invalid SQL syntax: sql parser error: Expected end of REPARTITION clause, found: ,"
1585 );
1586 }
1587
1588 #[test]
1589 fn test_parse_alter_drop_column() {
1590 let sql = "ALTER TABLE my_metric_1 DROP a";
1591 let result =
1592 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1593 .unwrap_err();
1594 let err = result.output_msg();
1595 assert_eq!(
1596 err,
1597 "Invalid SQL syntax: sql parser error: Expected: COLUMN, found: a at Line: 1, Column: 30"
1598 );
1599
1600 let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
1601 let mut result =
1602 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1603 .unwrap();
1604 assert_eq!(1, result.len());
1605
1606 let statement = result.remove(0);
1607 assert_matches!(statement, Statement::AlterTable { .. });
1608 match statement {
1609 Statement::AlterTable(alter_table) => {
1610 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
1611
1612 let alter_operation = alter_table.alter_operation();
1613 assert_matches!(alter_operation, AlterTableOperation::DropColumn { .. });
1614 match alter_operation {
1615 AlterTableOperation::DropColumn { name } => {
1616 assert_eq!("a", name.value);
1617 }
1618 _ => unreachable!(),
1619 }
1620 }
1621 _ => unreachable!(),
1622 }
1623 }
1624
1625 #[test]
1626 fn test_parse_alter_modify_column_type() {
1627 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
1628 let result_1 = ParserContext::create_with_dialect(
1629 sql_1,
1630 &GreptimeDbDialect {},
1631 ParseOptions::default(),
1632 )
1633 .unwrap();
1634
1635 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
1636 let mut result_2 = ParserContext::create_with_dialect(
1637 sql_2,
1638 &GreptimeDbDialect {},
1639 ParseOptions::default(),
1640 )
1641 .unwrap();
1642 assert_eq!(result_1, result_2);
1643 assert_eq!(1, result_2.len());
1644
1645 let statement = result_2.remove(0);
1646 assert_matches!(statement, Statement::AlterTable { .. });
1647 match statement {
1648 Statement::AlterTable(alter_table) => {
1649 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
1650
1651 let alter_operation = alter_table.alter_operation();
1652 assert_matches!(
1653 alter_operation,
1654 AlterTableOperation::ModifyColumnType { .. }
1655 );
1656 match alter_operation {
1657 AlterTableOperation::ModifyColumnType {
1658 column_name,
1659 target_type,
1660 json2_options,
1661 } => {
1662 assert_eq!("a", column_name.value);
1663 assert_eq!(DataType::String(None), *target_type);
1664 assert!(json2_options.is_none());
1665 }
1666 _ => unreachable!(),
1667 }
1668 }
1669 _ => unreachable!(),
1670 }
1671 }
1672
1673 #[test]
1674 fn test_parse_alter_json2() {
1675 let sql = r#"ALTER TABLE application_logs
1676MODIFY COLUMN attrs JSON2 (
1677 max_auto_expanded_paths = 2000,
1678 trace_id STRING,
1679 user.id STRING,
1680 user.name STRING,
1681 request_id STRING INVERTED INDEX
1682)"#;
1683 let mut statements =
1684 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1685 .unwrap();
1686
1687 let Statement::AlterTable(alter_table) = statements.remove(0) else {
1688 unreachable!()
1689 };
1690 let AlterTableOperation::SetJsonSettings {
1691 column_name,
1692 json2_options: Some(options),
1693 } = alter_table.alter_operation()
1694 else {
1695 unreachable!()
1696 };
1697
1698 assert_eq!("attrs", column_name.value);
1699 assert_eq!(Some(2000), options.max_auto_expanded_paths);
1700 assert_eq!(4, options.type_hints.len());
1701 assert_eq!(vec!["user", "id"], options.type_hints[1].path);
1702 assert!(options.type_hints[3].inverted_index);
1703
1704 let formatted = alter_table.to_string();
1705 let reparsed = ParserContext::create_with_dialect(
1706 &formatted,
1707 &GreptimeDbDialect {},
1708 ParseOptions::default(),
1709 )
1710 .unwrap();
1711 assert_eq!(Statement::AlterTable(alter_table), reparsed[0]);
1712
1713 let mut empty = ParserContext::create_with_dialect(
1714 "ALTER TABLE application_logs MODIFY COLUMN attrs JSON2 ()",
1715 &GreptimeDbDialect {},
1716 ParseOptions::default(),
1717 )
1718 .unwrap();
1719 let Statement::AlterTable(empty) = empty.remove(0) else {
1720 unreachable!()
1721 };
1722 let AlterTableOperation::SetJsonSettings { json2_options, .. } = empty.alter_operation()
1723 else {
1724 unreachable!()
1725 };
1726 assert!(json2_options.is_none());
1727 }
1728
1729 #[test]
1730 fn test_parse_alter_change_column_alias_type() {
1731 let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
1732 let mut result_1 = ParserContext::create_with_dialect(
1733 sql_1,
1734 &GreptimeDbDialect {},
1735 ParseOptions::default(),
1736 )
1737 .unwrap();
1738
1739 match result_1.remove(0) {
1740 Statement::AlterTable(alter_table) => {
1741 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
1742
1743 let alter_operation = alter_table.alter_operation();
1744 assert_matches!(
1745 alter_operation,
1746 AlterTableOperation::ModifyColumnType { .. }
1747 );
1748 match alter_operation {
1749 AlterTableOperation::ModifyColumnType {
1750 column_name,
1751 target_type,
1752 json2_options,
1753 } => {
1754 assert_eq!("a", column_name.value);
1755 assert_eq!(DataType::MediumText, *target_type);
1756 assert!(json2_options.is_none());
1757 }
1758 _ => unreachable!(),
1759 }
1760 }
1761 _ => unreachable!(),
1762 }
1763
1764 let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a TIMESTAMP_US";
1765 let mut result_2 = ParserContext::create_with_dialect(
1766 sql_2,
1767 &GreptimeDbDialect {},
1768 ParseOptions::default(),
1769 )
1770 .unwrap();
1771
1772 match result_2.remove(0) {
1773 Statement::AlterTable(alter_table) => {
1774 assert_eq!("my_metric_1", alter_table.table_name().0[0].to_string());
1775
1776 let alter_operation = alter_table.alter_operation();
1777 assert_matches!(
1778 alter_operation,
1779 AlterTableOperation::ModifyColumnType { .. }
1780 );
1781 match alter_operation {
1782 AlterTableOperation::ModifyColumnType {
1783 column_name,
1784 target_type,
1785 json2_options,
1786 } => {
1787 assert_eq!("a", column_name.value);
1788 assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
1789 assert!(json2_options.is_none());
1790 }
1791 _ => unreachable!(),
1792 }
1793 }
1794 _ => unreachable!(),
1795 }
1796 }
1797
1798 #[test]
1799 fn test_parse_alter_rename_table() {
1800 let sql = "ALTER TABLE test_table table_t";
1801 let result =
1802 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1803 .unwrap_err();
1804 let err = result.output_msg();
1805 assert_eq!(
1806 err,
1807 "Invalid SQL syntax: sql parser error: Expected ADD or DROP or MODIFY or RENAME or SET or UNSET or REPARTITION or SPLIT or MERGE or PARTITION after ALTER TABLE, found: table_t"
1808 );
1809
1810 let sql = "ALTER TABLE test_table RENAME table_t";
1811 let mut result =
1812 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1813 .unwrap();
1814 assert_eq!(1, result.len());
1815
1816 let statement = result.remove(0);
1817 assert_matches!(statement, Statement::AlterTable { .. });
1818 match statement {
1819 Statement::AlterTable(alter_table) => {
1820 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1821
1822 let alter_operation = alter_table.alter_operation();
1823 assert_matches!(alter_operation, AlterTableOperation::RenameTable { .. });
1824 match alter_operation {
1825 AlterTableOperation::RenameTable { new_table_name } => {
1826 assert_eq!("table_t", new_table_name);
1827 }
1828 _ => unreachable!(),
1829 }
1830 }
1831 _ => unreachable!(),
1832 }
1833 }
1834
1835 fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
1836 let result =
1837 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1838 .unwrap();
1839 assert_eq!(1, result.len());
1840 let Statement::AlterTable(alter) = &result[0] else {
1841 unreachable!()
1842 };
1843 assert_eq!("test_table", alter.table_name.0[0].to_string());
1844 let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
1845 unreachable!()
1846 };
1847
1848 assert_eq!(sql, alter.to_string());
1849 let res = options
1850 .iter()
1851 .map(|o| (o.key.as_str(), o.value.as_str()))
1852 .collect::<Vec<_>>();
1853 assert_eq!(expected, &res);
1854 }
1855
1856 fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
1857 let result =
1858 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1859 .unwrap();
1860 assert_eq!(1, result.len());
1861 let Statement::AlterTable(alter) = &result[0] else {
1862 unreachable!()
1863 };
1864 assert_eq!("test_table", alter.table_name.0[0].to_string());
1865 let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
1866 unreachable!()
1867 };
1868
1869 assert_eq!(sql, alter.to_string());
1870 assert_eq!(expected, keys);
1871 }
1872
1873 #[test]
1874 fn test_parse_alter_table_set_options() {
1875 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
1876 check_parse_alter_table_set_options(
1877 "ALTER TABLE test_table SET 'a'='A','b'='B'",
1878 &[("a", "A"), ("b", "B")],
1879 );
1880 check_parse_alter_table_set_options(
1881 "ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
1882 &[("a", "A"), ("b", "B"), ("c", "C")],
1883 );
1884 check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
1885
1886 ParserContext::create_with_dialect(
1887 "ALTER TABLE test_table SET a INTEGER",
1888 &GreptimeDbDialect {},
1889 ParseOptions::default(),
1890 )
1891 .unwrap_err();
1892 }
1893
1894 #[test]
1895 fn test_parse_alter_table_unset_options() {
1896 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
1897 check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
1898 ParserContext::create_with_dialect(
1899 "ALTER TABLE test_table UNSET a INTEGER",
1900 &GreptimeDbDialect {},
1901 ParseOptions::default(),
1902 )
1903 .unwrap_err();
1904 }
1905
1906 #[test]
1907 fn test_parse_alter_column_fulltext() {
1908 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)";
1909 let mut result =
1910 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1911 .unwrap();
1912
1913 assert_eq!(1, result.len());
1914 let statement = result.remove(0);
1915 assert_matches!(statement, Statement::AlterTable { .. });
1916 match statement {
1917 Statement::AlterTable(alter_table) => {
1918 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1919
1920 let alter_operation = alter_table.alter_operation();
1921 match alter_operation {
1922 AlterTableOperation::SetIndex {
1923 options:
1924 SetIndexOperation::Fulltext {
1925 column_name,
1926 options,
1927 },
1928 } => {
1929 assert_eq!("a", column_name.value);
1930 assert_eq!(
1931 FulltextOptions::new_unchecked(
1932 true,
1933 FulltextAnalyzer::English,
1934 false,
1935 FulltextBackend::Bloom,
1936 1000,
1937 0.01,
1938 ),
1939 *options
1940 );
1941 }
1942 _ => unreachable!(),
1943 };
1944 }
1945 _ => unreachable!(),
1946 }
1947
1948 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
1949 let mut result =
1950 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1951 .unwrap();
1952 assert_eq!(1, result.len());
1953 let statement = result.remove(0);
1954 assert_matches!(statement, Statement::AlterTable { .. });
1955 match statement {
1956 Statement::AlterTable(alter_table) => {
1957 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
1958
1959 let alter_operation = alter_table.alter_operation();
1960 assert_eq!(
1961 alter_operation,
1962 &AlterTableOperation::UnsetIndex {
1963 options: UnsetIndexOperation::Fulltext {
1964 column_name: Ident::new("a"),
1965 }
1966 }
1967 );
1968 }
1969 _ => unreachable!(),
1970 }
1971
1972 let invalid_sql =
1973 "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
1974 let result = ParserContext::create_with_dialect(
1975 invalid_sql,
1976 &GreptimeDbDialect {},
1977 ParseOptions::default(),
1978 )
1979 .unwrap_err();
1980 let err = result.to_string();
1981 assert_eq!(
1982 err,
1983 "Invalid column option, column name: a, error: invalid FULLTEXT option: abcd"
1984 );
1985 }
1986
1987 #[test]
1988 fn test_parse_alter_column_inverted() {
1989 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED INDEX";
1990 let mut result =
1991 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1992 .unwrap();
1993
1994 assert_eq!(1, result.len());
1995 let statement = result.remove(0);
1996 assert_matches!(statement, Statement::AlterTable { .. });
1997 match statement {
1998 Statement::AlterTable(alter_table) => {
1999 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
2000
2001 let alter_operation = alter_table.alter_operation();
2002 match alter_operation {
2003 AlterTableOperation::SetIndex {
2004 options: SetIndexOperation::Inverted { column_name },
2005 } => assert_eq!("a", column_name.value),
2006 _ => unreachable!(),
2007 };
2008 }
2009 _ => unreachable!(),
2010 }
2011
2012 let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET INVERTED INDEX";
2013 let mut result =
2014 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
2015 .unwrap();
2016 assert_eq!(1, result.len());
2017 let statement = result.remove(0);
2018 assert_matches!(statement, Statement::AlterTable { .. });
2019 match statement {
2020 Statement::AlterTable(alter_table) => {
2021 assert_eq!("test_table", alter_table.table_name().0[0].to_string());
2022
2023 let alter_operation = alter_table.alter_operation();
2024 assert_eq!(
2025 alter_operation,
2026 &AlterTableOperation::UnsetIndex {
2027 options: UnsetIndexOperation::Inverted {
2028 column_name: Ident::new("a"),
2029 }
2030 }
2031 );
2032 }
2033 _ => unreachable!(),
2034 }
2035
2036 let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED";
2037 ParserContext::create_with_dialect(
2038 invalid_sql,
2039 &GreptimeDbDialect {},
2040 ParseOptions::default(),
2041 )
2042 .unwrap_err();
2043 }
2044
2045 #[test]
2046 fn test_parse_alter_with_numeric_value() {
2047 for sql in [
2048 "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'=8;",
2049 "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'='8';",
2050 ] {
2051 let mut result = ParserContext::create_with_dialect(
2052 sql,
2053 &GreptimeDbDialect {},
2054 ParseOptions::default(),
2055 )
2056 .unwrap();
2057 assert_eq!(1, result.len());
2058
2059 let statement = result.remove(0);
2060 assert_matches!(statement, Statement::AlterTable { .. });
2061 match statement {
2062 Statement::AlterTable(alter_table) => {
2063 let alter_operation = alter_table.alter_operation();
2064 assert_matches!(alter_operation, AlterTableOperation::SetTableOptions { .. });
2065 match alter_operation {
2066 AlterTableOperation::SetTableOptions { options } => {
2067 assert_eq!(options.len(), 1);
2068 assert_eq!(options[0].key, "compaction.twcs.trigger_file_num");
2069 assert_eq!(options[0].value, "8");
2070 }
2071 _ => unreachable!(),
2072 }
2073 }
2074 _ => unreachable!(),
2075 }
2076 }
2077 }
2078
2079 #[test]
2080 fn test_parse_alter_drop_default() {
2081 let columns = vec![vec!["a"], vec!["a", "b", "c"]];
2082 for col in columns {
2083 let sql = col
2084 .iter()
2085 .map(|x| format!("MODIFY COLUMN {x} DROP DEFAULT"))
2086 .collect::<Vec<String>>()
2087 .join(",");
2088 let sql = format!("ALTER TABLE test_table {sql}");
2089 let mut result = ParserContext::create_with_dialect(
2090 &sql,
2091 &GreptimeDbDialect {},
2092 ParseOptions::default(),
2093 )
2094 .unwrap();
2095 assert_eq!(1, result.len());
2096 let statement = result.remove(0);
2097 assert_matches!(statement, Statement::AlterTable { .. });
2098 match statement {
2099 Statement::AlterTable(alter_table) => {
2100 assert_eq!(
2101 "test_table",
2102 alter_table.table_name().0[0].to_string_unquoted()
2103 );
2104 let alter_operation = alter_table.alter_operation();
2105 match alter_operation {
2106 AlterTableOperation::DropDefaults { columns } => {
2107 assert_eq!(col.len(), columns.len());
2108 for i in 0..columns.len() {
2109 assert_eq!(col[i], columns[i].0.value);
2110 }
2111 }
2112 _ => unreachable!(),
2113 }
2114 }
2115 _ => unreachable!(),
2116 }
2117 }
2118 }
2119
2120 #[test]
2121 fn test_parse_alter_set_default() {
2122 let columns = vec![vec!["a"], vec!["a", "b"], vec!["a", "b", "c"]];
2123 for col in columns {
2124 let sql = col
2125 .iter()
2126 .map(|x| format!("MODIFY COLUMN {x} SET DEFAULT 100"))
2127 .collect::<Vec<String>>()
2128 .join(",");
2129 let sql = format!("ALTER TABLE test_table {sql}");
2130 let mut result = ParserContext::create_with_dialect(
2131 &sql,
2132 &GreptimeDbDialect {},
2133 ParseOptions::default(),
2134 )
2135 .unwrap();
2136 assert_eq!(1, result.len());
2137 let statement = result.remove(0);
2138 assert_matches!(statement, Statement::AlterTable { .. });
2139 match statement {
2140 Statement::AlterTable(alter_table) => {
2141 assert_eq!("test_table", alter_table.table_name().to_string());
2142 let alter_operation = alter_table.alter_operation();
2143 match alter_operation {
2144 AlterTableOperation::SetDefaults { defaults } => {
2145 assert_eq!(col.len(), defaults.len());
2146 for i in 0..defaults.len() {
2147 assert_eq!(col[i], defaults[i].column_name.to_string());
2148 assert_eq!(
2149 "100".to_string(),
2150 defaults[i].default_constraint.to_string()
2151 );
2152 }
2153 }
2154 _ => unreachable!(),
2155 }
2156 }
2157 _ => unreachable!(),
2158 }
2159 }
2160 }
2161
2162 #[test]
2163 fn test_parse_alter_set_default_invalid() {
2164 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET 100;";
2165 let result =
2166 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
2167 .unwrap_err();
2168 let err = result.output_msg();
2169 assert_eq!(
2170 err,
2171 "Invalid SQL syntax: sql parser error: Expected FULLTEXT OR INVERTED OR SKIPPING INDEX, found: 100"
2172 );
2173
2174 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, b SET DEFAULT 200";
2175 let result =
2176 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
2177 .unwrap_err();
2178 let err = result.output_msg();
2179 assert_eq!(
2180 err,
2181 "Invalid SQL syntax: sql parser error: Expected: MODIFY, found: b at Line: 1, Column: 57"
2182 );
2183
2184 let sql = "ALTER TABLE test_table MODIFY COLUMN a SET DEFAULT 100, MODIFY COLUMN b DROP DEFAULT 200";
2185 let result =
2186 ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
2187 .unwrap_err();
2188 let err = result.output_msg();
2189 assert_eq!(
2190 err,
2191 "Invalid SQL syntax: sql parser error: Unexpected keyword, expect SET, got: `DROP`"
2192 );
2193 }
2194}