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