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