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