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    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                        _ => self.expected(
160                            "ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE",
161                            self.parser.peek_token(),
162                        )?,
163                    }
164                }
165            }
166            unexpected => self.unsupported(unexpected.to_string())?,
167        };
168        Ok(AlterTable::new(table_name, alter_operation))
169    }
170
171    fn parse_alter_table_unset(&mut self) -> Result<AlterTableOperation> {
172        let _ = self.parser.next_token();
173        let keys = self
174            .parser
175            .parse_comma_separated(parse_string_option_names)
176            .context(error::SyntaxSnafu)?
177            .into_iter()
178            .collect();
179
180        Ok(AlterTableOperation::UnsetTableOptions { keys })
181    }
182
183    fn parse_alter_table_add(&mut self) -> Result<AlterTableOperation> {
184        let _ = self.parser.next_token();
185        if let Some(constraint) = self
186            .parser
187            .parse_optional_table_constraint()
188            .context(error::SyntaxSnafu)?
189        {
190            Ok(AlterTableOperation::AddConstraint(constraint))
191        } else {
192            self.parser.prev_token();
193            let add_columns = self
194                .parser
195                .parse_comma_separated(parse_add_columns)
196                .context(error::SyntaxSnafu)?;
197            Ok(AlterTableOperation::AddColumns { add_columns })
198        }
199    }
200
201    fn parse_alter_table_modify(&mut self) -> Result<AlterTableOperation> {
202        let _ = self.parser.next_token();
203        self.parser
204            .expect_keyword(Keyword::COLUMN)
205            .context(error::SyntaxSnafu)?;
206        let column_name = Self::canonicalize_identifier(
207            self.parser.parse_identifier().context(error::SyntaxSnafu)?,
208        );
209
210        match self.parser.peek_token().token {
211            Token::Word(w) => {
212                if w.value.eq_ignore_ascii_case("UNSET") {
213                    // consume the current token.
214                    self.parser.next_token();
215                    self.parse_alter_column_unset_index(column_name)
216                } else if w.keyword == Keyword::SET {
217                    // consume the current token.
218                    self.parser.next_token();
219                    self.parse_alter_column_set_index(column_name)
220                } else {
221                    let data_type = self.parser.parse_data_type().context(error::SyntaxSnafu)?;
222                    Ok(AlterTableOperation::ModifyColumnType {
223                        column_name,
224                        target_type: data_type,
225                    })
226                }
227            }
228            _ => self.expected(
229                "SET or UNSET or data type after MODIFY COLUMN",
230                self.parser.peek_token(),
231            )?,
232        }
233    }
234
235    fn parse_alter_column_unset_index(
236        &mut self,
237        column_name: Ident,
238    ) -> Result<AlterTableOperation> {
239        match self.parser.next_token() {
240            TokenWithSpan {
241                token: Token::Word(w),
242                ..
243            } if w.keyword == Keyword::FULLTEXT => {
244                self.parser
245                    .expect_keyword(Keyword::INDEX)
246                    .context(error::SyntaxSnafu)?;
247                Ok(AlterTableOperation::UnsetIndex {
248                    options: UnsetIndexOperation::Fulltext { column_name },
249                })
250            }
251
252            TokenWithSpan {
253                token: Token::Word(w),
254                ..
255            } if w.value.eq_ignore_ascii_case(INVERTED) => {
256                self.parser
257                    .expect_keyword(Keyword::INDEX)
258                    .context(error::SyntaxSnafu)?;
259                Ok(AlterTableOperation::UnsetIndex {
260                    options: UnsetIndexOperation::Inverted { column_name },
261                })
262            }
263
264            TokenWithSpan {
265                token: Token::Word(w),
266                ..
267            } if w.value.eq_ignore_ascii_case("SKIPPING") => {
268                self.parser
269                    .expect_keyword(Keyword::INDEX)
270                    .context(error::SyntaxSnafu)?;
271                Ok(AlterTableOperation::UnsetIndex {
272                    options: UnsetIndexOperation::Skipping { column_name },
273                })
274            }
275            _ => self.expected(
276                format!(
277                    "{:?} OR INVERTED INDEX OR SKIPPING INDEX",
278                    Keyword::FULLTEXT
279                )
280                .as_str(),
281                self.parser.peek_token(),
282            ),
283        }
284    }
285
286    fn parse_alter_column_set_index(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
287        match self.parser.next_token() {
288            TokenWithSpan {
289                token: Token::Word(w),
290                ..
291            } if w.keyword == Keyword::FULLTEXT => {
292                self.parser
293                    .expect_keyword(Keyword::INDEX)
294                    .context(error::SyntaxSnafu)?;
295                self.parse_alter_column_fulltext(column_name)
296            }
297
298            TokenWithSpan {
299                token: Token::Word(w),
300                ..
301            } if w.value.eq_ignore_ascii_case(INVERTED) => {
302                self.parser
303                    .expect_keyword(Keyword::INDEX)
304                    .context(error::SyntaxSnafu)?;
305                Ok(AlterTableOperation::SetIndex {
306                    options: SetIndexOperation::Inverted { column_name },
307                })
308            }
309
310            TokenWithSpan {
311                token: Token::Word(w),
312                ..
313            } if w.value.eq_ignore_ascii_case("SKIPPING") => {
314                self.parser
315                    .expect_keyword(Keyword::INDEX)
316                    .context(error::SyntaxSnafu)?;
317                self.parse_alter_column_skipping(column_name)
318            }
319            _ => self.expected(
320                format!("{:?} OR INVERTED OR SKIPPING INDEX", Keyword::FULLTEXT).as_str(),
321                self.parser.peek_token(),
322            ),
323        }
324    }
325
326    fn parse_alter_column_fulltext(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
327        let mut options = self
328            .parser
329            .parse_options(Keyword::WITH)
330            .context(error::SyntaxSnafu)?
331            .into_iter()
332            .map(parse_option_string)
333            .collect::<Result<HashMap<String, String>>>()?;
334
335        for key in options.keys() {
336            ensure!(
337                validate_column_fulltext_create_option(key),
338                InvalidColumnOptionSnafu {
339                    name: column_name.to_string(),
340                    msg: format!("invalid FULLTEXT option: {key}"),
341                }
342            );
343        }
344
345        options.insert(
346            COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE.to_string(),
347            "true".to_string(),
348        );
349
350        Ok(AlterTableOperation::SetIndex {
351            options: SetIndexOperation::Fulltext {
352                column_name,
353                options: options.try_into().context(SetFulltextOptionSnafu)?,
354            },
355        })
356    }
357
358    fn parse_alter_column_skipping(&mut self, column_name: Ident) -> Result<AlterTableOperation> {
359        let 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_skipping_index_create_option(key),
370                InvalidColumnOptionSnafu {
371                    name: column_name.to_string(),
372                    msg: format!("invalid SKIPPING INDEX option: {key}"),
373                }
374            );
375        }
376
377        Ok(AlterTableOperation::SetIndex {
378            options: SetIndexOperation::Skipping {
379                column_name,
380                options: options
381                    .try_into()
382                    .context(error::SetSkippingIndexOptionSnafu)?,
383            },
384        })
385    }
386}
387
388/// Parses a string literal and an optional string literal value.
389fn parse_string_options(parser: &mut Parser) -> std::result::Result<(String, String), ParserError> {
390    let name = parser.parse_literal_string()?;
391    parser.expect_token(&Token::Eq)?;
392    let value = if parser.parse_keyword(Keyword::NULL) {
393        "".to_string()
394    } else if let Ok(v) = parser.parse_literal_string() {
395        v
396    } else {
397        return Err(ParserError::ParserError(format!(
398            "Unexpected option value for alter table statements, expect string literal or NULL, got: `{}`",
399            parser.next_token()
400        )));
401    };
402    Ok((name, value))
403}
404
405fn parse_add_columns(parser: &mut Parser) -> std::result::Result<AddColumn, ParserError> {
406    parser.expect_keyword(Keyword::ADD)?;
407    let _ = parser.parse_keyword(Keyword::COLUMN);
408    let add_if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
409    let mut column_def = parser.parse_column_def()?;
410    column_def.name = ParserContext::canonicalize_identifier(column_def.name);
411    let location = if parser.parse_keyword(Keyword::FIRST) {
412        Some(AddColumnLocation::First)
413    } else if let Token::Word(word) = parser.peek_token().token {
414        if word.value.eq_ignore_ascii_case("AFTER") {
415            let _ = parser.next_token();
416            let name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
417            Some(AddColumnLocation::After {
418                column_name: name.value,
419            })
420        } else {
421            None
422        }
423    } else {
424        None
425    };
426    Ok(AddColumn {
427        column_def,
428        location,
429        add_if_not_exists,
430    })
431}
432
433/// Parses a comma separated list of string literals.
434fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
435    parser.parse_literal_string()
436}
437
438#[cfg(test)]
439mod tests {
440    use std::assert_matches::assert_matches;
441
442    use common_error::ext::ErrorExt;
443    use datatypes::schema::{FulltextAnalyzer, FulltextBackend, FulltextOptions};
444    use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType};
445
446    use super::*;
447    use crate::dialect::GreptimeDbDialect;
448    use crate::parser::ParseOptions;
449    use crate::statements::alter::AlterDatabaseOperation;
450
451    #[test]
452    fn test_parse_alter_database() {
453        let sql = "ALTER DATABASE test_db SET 'a'='A', 'b' = 'B'";
454        let mut result =
455            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
456                .unwrap();
457        assert_eq!(1, result.len());
458
459        let statement = result.remove(0);
460        assert_matches!(statement, Statement::AlterDatabase { .. });
461        match statement {
462            Statement::AlterDatabase(alter_database) => {
463                assert_eq!("test_db", alter_database.database_name().0[0].value);
464
465                let alter_operation = alter_database.alter_operation();
466                assert_matches!(
467                    alter_operation,
468                    AlterDatabaseOperation::SetDatabaseOption { .. }
469                );
470                match alter_operation {
471                    AlterDatabaseOperation::SetDatabaseOption { options } => {
472                        assert_eq!(2, options.len());
473                        assert_eq!("a", options[0].key);
474                        assert_eq!("A", options[0].value);
475                        assert_eq!("b", options[1].key);
476                        assert_eq!("B", options[1].value);
477                    }
478                    _ => unreachable!(),
479                }
480            }
481            _ => unreachable!(),
482        }
483        let sql = "ALTER DATABASE test_db UNSET 'a', 'b'";
484        let mut result =
485            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
486                .unwrap();
487        assert_eq!(1, result.len());
488        let statement = result.remove(0);
489        assert_matches!(statement, Statement::AlterDatabase { .. });
490        match statement {
491            Statement::AlterDatabase(alter_database) => {
492                assert_eq!("test_db", alter_database.database_name().0[0].value);
493                let alter_operation = alter_database.alter_operation();
494                assert_matches!(
495                    alter_operation,
496                    AlterDatabaseOperation::UnsetDatabaseOption { .. }
497                );
498                match alter_operation {
499                    AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
500                        assert_eq!(2, keys.len());
501                        assert_eq!("a", keys[0]);
502                        assert_eq!("b", keys[1]);
503                    }
504                    _ => unreachable!(),
505                }
506            }
507            _ => unreachable!(),
508        }
509    }
510
511    #[test]
512    fn test_parse_alter_add_column() {
513        let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null;";
514        let mut result =
515            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
516                .unwrap();
517        assert_eq!(1, result.len());
518
519        let statement = result.remove(0);
520        assert_matches!(statement, Statement::AlterTable { .. });
521        match statement {
522            Statement::AlterTable(alter_table) => {
523                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
524
525                let alter_operation = alter_table.alter_operation();
526                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
527                match alter_operation {
528                    AlterTableOperation::AddColumns { add_columns } => {
529                        assert_eq!(add_columns.len(), 1);
530                        assert_eq!("tagk_i", add_columns[0].column_def.name.value);
531                        assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
532                        assert!(add_columns[0]
533                            .column_def
534                            .options
535                            .iter()
536                            .any(|o| matches!(o.option, ColumnOption::Null)));
537                        assert_eq!(&None, &add_columns[0].location);
538                    }
539                    _ => unreachable!(),
540                }
541            }
542            _ => unreachable!(),
543        }
544    }
545
546    #[test]
547    fn test_parse_alter_add_column_with_first() {
548        let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null FIRST;";
549        let mut result =
550            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
551                .unwrap();
552        assert_eq!(1, result.len());
553
554        let statement = result.remove(0);
555        assert_matches!(statement, Statement::AlterTable { .. });
556        match statement {
557            Statement::AlterTable(alter_table) => {
558                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
559
560                let alter_operation = alter_table.alter_operation();
561                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
562                match alter_operation {
563                    AlterTableOperation::AddColumns { add_columns } => {
564                        assert_eq!("tagk_i", add_columns[0].column_def.name.value);
565                        assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
566                        assert!(add_columns[0]
567                            .column_def
568                            .options
569                            .iter()
570                            .any(|o| matches!(o.option, ColumnOption::Null)));
571                        assert_eq!(&Some(AddColumnLocation::First), &add_columns[0].location);
572                    }
573                    _ => unreachable!(),
574                }
575            }
576            _ => unreachable!(),
577        }
578    }
579
580    #[test]
581    fn test_parse_alter_add_column_with_after() {
582        let sql =
583            "ALTER TABLE my_metric_1 ADD tagk_i STRING Null AFTER ts, add column tagl_i String;";
584        let mut result =
585            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
586                .unwrap();
587        assert_eq!(1, result.len());
588
589        let statement = result.remove(0);
590        assert_matches!(statement, Statement::AlterTable { .. });
591        match statement {
592            Statement::AlterTable(alter_table) => {
593                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
594
595                let alter_operation = alter_table.alter_operation();
596                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
597                match alter_operation {
598                    AlterTableOperation::AddColumns { add_columns } => {
599                        let expecteds: Vec<(Option<AddColumnLocation>, ColumnDef)> = vec![
600                            (
601                                Some(AddColumnLocation::After {
602                                    column_name: "ts".to_string(),
603                                }),
604                                ColumnDef {
605                                    name: Ident::new("tagk_i"),
606                                    data_type: DataType::String(None),
607                                    options: vec![ColumnOptionDef {
608                                        name: None,
609                                        option: ColumnOption::Null,
610                                    }],
611                                    collation: None,
612                                },
613                            ),
614                            (
615                                None,
616                                ColumnDef {
617                                    name: Ident::new("tagl_i"),
618                                    data_type: DataType::String(None),
619                                    options: vec![],
620                                    collation: None,
621                                },
622                            ),
623                        ];
624                        for (add_column, expected) in add_columns
625                            .iter()
626                            .zip(expecteds)
627                            .collect::<Vec<(&AddColumn, (Option<AddColumnLocation>, ColumnDef))>>()
628                        {
629                            assert_eq!(add_column.column_def, expected.1);
630                            assert_eq!(&expected.0, &add_column.location);
631                        }
632                    }
633                    _ => unreachable!(),
634                }
635            }
636            _ => unreachable!(),
637        }
638    }
639
640    #[test]
641    fn test_parse_add_column_if_not_exists() {
642        let sql = "ALTER TABLE test ADD COLUMN IF NOT EXISTS a INTEGER, ADD COLUMN b STRING, ADD COLUMN IF NOT EXISTS c INT;";
643        let mut result =
644            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
645                .unwrap();
646        assert_eq!(result.len(), 1);
647        let statement = result.remove(0);
648        assert_matches!(statement, Statement::AlterTable { .. });
649        match statement {
650            Statement::AlterTable(alter) => {
651                assert_eq!(alter.table_name.0[0].value, "test");
652                assert_matches!(
653                    alter.alter_operation,
654                    AlterTableOperation::AddColumns { .. }
655                );
656                match alter.alter_operation {
657                    AlterTableOperation::AddColumns { add_columns } => {
658                        let expected = vec![
659                            AddColumn {
660                                column_def: ColumnDef {
661                                    name: Ident::new("a"),
662                                    data_type: DataType::Integer(None),
663                                    collation: None,
664                                    options: vec![],
665                                },
666                                location: None,
667                                add_if_not_exists: true,
668                            },
669                            AddColumn {
670                                column_def: ColumnDef {
671                                    name: Ident::new("b"),
672                                    data_type: DataType::String(None),
673                                    collation: None,
674                                    options: vec![],
675                                },
676                                location: None,
677                                add_if_not_exists: false,
678                            },
679                            AddColumn {
680                                column_def: ColumnDef {
681                                    name: Ident::new("c"),
682                                    data_type: DataType::Int(None),
683                                    collation: None,
684                                    options: vec![],
685                                },
686                                location: None,
687                                add_if_not_exists: true,
688                            },
689                        ];
690                        for (idx, add_column) in add_columns.into_iter().enumerate() {
691                            assert_eq!(add_column, expected[idx]);
692                        }
693                    }
694                    _ => unreachable!(),
695                }
696            }
697            _ => unreachable!(),
698        }
699    }
700
701    #[test]
702    fn test_parse_alter_drop_column() {
703        let sql = "ALTER TABLE my_metric_1 DROP a";
704        let result =
705            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
706                .unwrap_err();
707        let err = result.output_msg();
708        assert_eq!(
709            err,
710            "Invalid SQL syntax: sql parser error: Expected: COLUMN, found: a at Line: 1, Column: 30"
711        );
712
713        let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
714        let mut result =
715            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
716                .unwrap();
717        assert_eq!(1, result.len());
718
719        let statement = result.remove(0);
720        assert_matches!(statement, Statement::AlterTable { .. });
721        match statement {
722            Statement::AlterTable(alter_table) => {
723                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
724
725                let alter_operation = alter_table.alter_operation();
726                assert_matches!(alter_operation, AlterTableOperation::DropColumn { .. });
727                match alter_operation {
728                    AlterTableOperation::DropColumn { name } => {
729                        assert_eq!("a", name.value);
730                    }
731                    _ => unreachable!(),
732                }
733            }
734            _ => unreachable!(),
735        }
736    }
737
738    #[test]
739    fn test_parse_alter_modify_column_type() {
740        let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
741        let result_1 = ParserContext::create_with_dialect(
742            sql_1,
743            &GreptimeDbDialect {},
744            ParseOptions::default(),
745        )
746        .unwrap();
747
748        let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
749        let mut result_2 = ParserContext::create_with_dialect(
750            sql_2,
751            &GreptimeDbDialect {},
752            ParseOptions::default(),
753        )
754        .unwrap();
755        assert_eq!(result_1, result_2);
756        assert_eq!(1, result_2.len());
757
758        let statement = result_2.remove(0);
759        assert_matches!(statement, Statement::AlterTable { .. });
760        match statement {
761            Statement::AlterTable(alter_table) => {
762                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
763
764                let alter_operation = alter_table.alter_operation();
765                assert_matches!(
766                    alter_operation,
767                    AlterTableOperation::ModifyColumnType { .. }
768                );
769                match alter_operation {
770                    AlterTableOperation::ModifyColumnType {
771                        column_name,
772                        target_type,
773                    } => {
774                        assert_eq!("a", column_name.value);
775                        assert_eq!(DataType::String(None), *target_type);
776                    }
777                    _ => unreachable!(),
778                }
779            }
780            _ => unreachable!(),
781        }
782    }
783
784    #[test]
785    fn test_parse_alter_change_column_alias_type() {
786        let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
787        let mut result_1 = ParserContext::create_with_dialect(
788            sql_1,
789            &GreptimeDbDialect {},
790            ParseOptions::default(),
791        )
792        .unwrap();
793
794        match result_1.remove(0) {
795            Statement::AlterTable(alter_table) => {
796                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
797
798                let alter_operation = alter_table.alter_operation();
799                assert_matches!(
800                    alter_operation,
801                    AlterTableOperation::ModifyColumnType { .. }
802                );
803                match alter_operation {
804                    AlterTableOperation::ModifyColumnType {
805                        column_name,
806                        target_type,
807                    } => {
808                        assert_eq!("a", column_name.value);
809                        assert_eq!(DataType::MediumText, *target_type);
810                    }
811                    _ => unreachable!(),
812                }
813            }
814            _ => unreachable!(),
815        }
816
817        let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a TIMESTAMP_US";
818        let mut result_2 = ParserContext::create_with_dialect(
819            sql_2,
820            &GreptimeDbDialect {},
821            ParseOptions::default(),
822        )
823        .unwrap();
824
825        match result_2.remove(0) {
826            Statement::AlterTable(alter_table) => {
827                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
828
829                let alter_operation = alter_table.alter_operation();
830                assert_matches!(
831                    alter_operation,
832                    AlterTableOperation::ModifyColumnType { .. }
833                );
834                match alter_operation {
835                    AlterTableOperation::ModifyColumnType {
836                        column_name,
837                        target_type,
838                    } => {
839                        assert_eq!("a", column_name.value);
840                        assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
841                    }
842                    _ => unreachable!(),
843                }
844            }
845            _ => unreachable!(),
846        }
847    }
848
849    #[test]
850    fn test_parse_alter_rename_table() {
851        let sql = "ALTER TABLE test_table table_t";
852        let result =
853            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
854                .unwrap_err();
855        let err = result.output_msg();
856        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");
857
858        let sql = "ALTER TABLE test_table RENAME table_t";
859        let mut result =
860            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
861                .unwrap();
862        assert_eq!(1, result.len());
863
864        let statement = result.remove(0);
865        assert_matches!(statement, Statement::AlterTable { .. });
866        match statement {
867            Statement::AlterTable(alter_table) => {
868                assert_eq!("test_table", alter_table.table_name().0[0].value);
869
870                let alter_operation = alter_table.alter_operation();
871                assert_matches!(alter_operation, AlterTableOperation::RenameTable { .. });
872                match alter_operation {
873                    AlterTableOperation::RenameTable { new_table_name } => {
874                        assert_eq!("table_t", new_table_name);
875                    }
876                    _ => unreachable!(),
877                }
878            }
879            _ => unreachable!(),
880        }
881    }
882
883    fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
884        let result =
885            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
886                .unwrap();
887        assert_eq!(1, result.len());
888        let Statement::AlterTable(alter) = &result[0] else {
889            unreachable!()
890        };
891        assert_eq!("test_table", alter.table_name.0[0].value);
892        let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
893            unreachable!()
894        };
895
896        assert_eq!(sql, alter.to_string());
897        let res = options
898            .iter()
899            .map(|o| (o.key.as_str(), o.value.as_str()))
900            .collect::<Vec<_>>();
901        assert_eq!(expected, &res);
902    }
903
904    fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
905        let result =
906            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
907                .unwrap();
908        assert_eq!(1, result.len());
909        let Statement::AlterTable(alter) = &result[0] else {
910            unreachable!()
911        };
912        assert_eq!("test_table", alter.table_name.0[0].value);
913        let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
914            unreachable!()
915        };
916
917        assert_eq!(sql, alter.to_string());
918        let res = keys.iter().map(|o| o.to_string()).collect::<Vec<_>>();
919        assert_eq!(expected, &res);
920    }
921
922    #[test]
923    fn test_parse_alter_table_set_options() {
924        check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
925        check_parse_alter_table_set_options(
926            "ALTER TABLE test_table SET 'a'='A','b'='B'",
927            &[("a", "A"), ("b", "B")],
928        );
929        check_parse_alter_table_set_options(
930            "ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
931            &[("a", "A"), ("b", "B"), ("c", "C")],
932        );
933        check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
934
935        ParserContext::create_with_dialect(
936            "ALTER TABLE test_table SET a INTEGER",
937            &GreptimeDbDialect {},
938            ParseOptions::default(),
939        )
940        .unwrap_err();
941    }
942
943    #[test]
944    fn test_parse_alter_table_unset_options() {
945        check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
946        check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
947        ParserContext::create_with_dialect(
948            "ALTER TABLE test_table UNSET a INTEGER",
949            &GreptimeDbDialect {},
950            ParseOptions::default(),
951        )
952        .unwrap_err();
953    }
954
955    #[test]
956    fn test_parse_alter_column_fulltext() {
957        let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false',backend='bloom')";
958        let mut result =
959            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
960                .unwrap();
961
962        assert_eq!(1, result.len());
963        let statement = result.remove(0);
964        assert_matches!(statement, Statement::AlterTable { .. });
965        match statement {
966            Statement::AlterTable(alter_table) => {
967                assert_eq!("test_table", alter_table.table_name().0[0].value);
968
969                let alter_operation = alter_table.alter_operation();
970                match alter_operation {
971                    AlterTableOperation::SetIndex {
972                        options:
973                            SetIndexOperation::Fulltext {
974                                column_name,
975                                options,
976                            },
977                    } => {
978                        assert_eq!("a", column_name.value);
979                        assert_eq!(
980                            FulltextOptions {
981                                enable: true,
982                                analyzer: FulltextAnalyzer::English,
983                                case_sensitive: false,
984                                backend: FulltextBackend::Bloom,
985                            },
986                            *options
987                        );
988                    }
989                    _ => unreachable!(),
990                };
991            }
992            _ => unreachable!(),
993        }
994
995        let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
996        let mut result =
997            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
998                .unwrap();
999        assert_eq!(1, result.len());
1000        let statement = result.remove(0);
1001        assert_matches!(statement, Statement::AlterTable { .. });
1002        match statement {
1003            Statement::AlterTable(alter_table) => {
1004                assert_eq!("test_table", alter_table.table_name().0[0].value);
1005
1006                let alter_operation = alter_table.alter_operation();
1007                assert_eq!(
1008                    alter_operation,
1009                    &AlterTableOperation::UnsetIndex {
1010                        options: UnsetIndexOperation::Fulltext {
1011                            column_name: Ident::new("a"),
1012                        }
1013                    }
1014                );
1015            }
1016            _ => unreachable!(),
1017        }
1018
1019        let invalid_sql =
1020            "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
1021        let result = ParserContext::create_with_dialect(
1022            invalid_sql,
1023            &GreptimeDbDialect {},
1024            ParseOptions::default(),
1025        )
1026        .unwrap_err();
1027        let err = result.to_string();
1028        assert_eq!(
1029            err,
1030            "Invalid column option, column name: a, error: invalid FULLTEXT option: abcd"
1031        );
1032    }
1033
1034    #[test]
1035    fn test_parse_alter_column_inverted() {
1036        let sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED INDEX";
1037        let mut result =
1038            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1039                .unwrap();
1040
1041        assert_eq!(1, result.len());
1042        let statement = result.remove(0);
1043        assert_matches!(statement, Statement::AlterTable { .. });
1044        match statement {
1045            Statement::AlterTable(alter_table) => {
1046                assert_eq!("test_table", alter_table.table_name().0[0].value);
1047
1048                let alter_operation = alter_table.alter_operation();
1049                match alter_operation {
1050                    AlterTableOperation::SetIndex {
1051                        options: SetIndexOperation::Inverted { column_name },
1052                    } => assert_eq!("a", column_name.value),
1053                    _ => unreachable!(),
1054                };
1055            }
1056            _ => unreachable!(),
1057        }
1058
1059        let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET INVERTED INDEX";
1060        let mut result =
1061            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1062                .unwrap();
1063        assert_eq!(1, result.len());
1064        let statement = result.remove(0);
1065        assert_matches!(statement, Statement::AlterTable { .. });
1066        match statement {
1067            Statement::AlterTable(alter_table) => {
1068                assert_eq!("test_table", alter_table.table_name().0[0].value);
1069
1070                let alter_operation = alter_table.alter_operation();
1071                assert_eq!(
1072                    alter_operation,
1073                    &AlterTableOperation::UnsetIndex {
1074                        options: UnsetIndexOperation::Inverted {
1075                            column_name: Ident::new("a"),
1076                        }
1077                    }
1078                );
1079            }
1080            _ => unreachable!(),
1081        }
1082
1083        let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED";
1084        ParserContext::create_with_dialect(
1085            invalid_sql,
1086            &GreptimeDbDialect {},
1087            ParseOptions::default(),
1088        )
1089        .unwrap_err();
1090    }
1091}