Skip to main content

sql/parsers/
alter_parser.rs

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