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