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