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