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