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