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 {
395        let next_token = parser.peek_token();
396        if let Token::Number(number_as_string, _) = next_token.token {
397            parser.advance_token();
398            number_as_string
399        } else {
400            parser.parse_literal_string().map_err(|_|{
401                ParserError::ParserError(format!("Unexpected option value for alter table statements, expect string literal, numeric literal or NULL, got: `{}`", next_token))
402            })?
403        }
404    };
405    Ok((name, value))
406}
407
408fn parse_add_columns(parser: &mut Parser) -> std::result::Result<AddColumn, ParserError> {
409    parser.expect_keyword(Keyword::ADD)?;
410    let _ = parser.parse_keyword(Keyword::COLUMN);
411    let add_if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
412    let mut column_def = parser.parse_column_def()?;
413    column_def.name = ParserContext::canonicalize_identifier(column_def.name);
414    let location = if parser.parse_keyword(Keyword::FIRST) {
415        Some(AddColumnLocation::First)
416    } else if let Token::Word(word) = parser.peek_token().token {
417        if word.value.eq_ignore_ascii_case("AFTER") {
418            let _ = parser.next_token();
419            let name = ParserContext::canonicalize_identifier(parser.parse_identifier()?);
420            Some(AddColumnLocation::After {
421                column_name: name.value,
422            })
423        } else {
424            None
425        }
426    } else {
427        None
428    };
429    Ok(AddColumn {
430        column_def,
431        location,
432        add_if_not_exists,
433    })
434}
435
436/// Parses a comma separated list of string literals.
437fn parse_string_option_names(parser: &mut Parser) -> std::result::Result<String, ParserError> {
438    parser.parse_literal_string()
439}
440
441#[cfg(test)]
442mod tests {
443    use std::assert_matches::assert_matches;
444
445    use common_error::ext::ErrorExt;
446    use datatypes::schema::{FulltextAnalyzer, FulltextBackend, FulltextOptions};
447    use sqlparser::ast::{ColumnDef, ColumnOption, ColumnOptionDef, DataType};
448
449    use super::*;
450    use crate::dialect::GreptimeDbDialect;
451    use crate::parser::ParseOptions;
452    use crate::statements::alter::AlterDatabaseOperation;
453
454    #[test]
455    fn test_parse_alter_database() {
456        let sql = "ALTER DATABASE test_db SET 'a'='A', 'b' = 'B'";
457        let mut result =
458            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
459                .unwrap();
460        assert_eq!(1, result.len());
461
462        let statement = result.remove(0);
463        assert_matches!(statement, Statement::AlterDatabase { .. });
464        match statement {
465            Statement::AlterDatabase(alter_database) => {
466                assert_eq!("test_db", alter_database.database_name().0[0].value);
467
468                let alter_operation = alter_database.alter_operation();
469                assert_matches!(
470                    alter_operation,
471                    AlterDatabaseOperation::SetDatabaseOption { .. }
472                );
473                match alter_operation {
474                    AlterDatabaseOperation::SetDatabaseOption { options } => {
475                        assert_eq!(2, options.len());
476                        assert_eq!("a", options[0].key);
477                        assert_eq!("A", options[0].value);
478                        assert_eq!("b", options[1].key);
479                        assert_eq!("B", options[1].value);
480                    }
481                    _ => unreachable!(),
482                }
483            }
484            _ => unreachable!(),
485        }
486        let sql = "ALTER DATABASE test_db UNSET 'a', 'b'";
487        let mut result =
488            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
489                .unwrap();
490        assert_eq!(1, result.len());
491        let statement = result.remove(0);
492        assert_matches!(statement, Statement::AlterDatabase { .. });
493        match statement {
494            Statement::AlterDatabase(alter_database) => {
495                assert_eq!("test_db", alter_database.database_name().0[0].value);
496                let alter_operation = alter_database.alter_operation();
497                assert_matches!(
498                    alter_operation,
499                    AlterDatabaseOperation::UnsetDatabaseOption { .. }
500                );
501                match alter_operation {
502                    AlterDatabaseOperation::UnsetDatabaseOption { keys } => {
503                        assert_eq!(2, keys.len());
504                        assert_eq!("a", keys[0]);
505                        assert_eq!("b", keys[1]);
506                    }
507                    _ => unreachable!(),
508                }
509            }
510            _ => unreachable!(),
511        }
512    }
513
514    #[test]
515    fn test_parse_alter_add_column() {
516        let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null;";
517        let mut result =
518            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
519                .unwrap();
520        assert_eq!(1, result.len());
521
522        let statement = result.remove(0);
523        assert_matches!(statement, Statement::AlterTable { .. });
524        match statement {
525            Statement::AlterTable(alter_table) => {
526                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
527
528                let alter_operation = alter_table.alter_operation();
529                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
530                match alter_operation {
531                    AlterTableOperation::AddColumns { add_columns } => {
532                        assert_eq!(add_columns.len(), 1);
533                        assert_eq!("tagk_i", add_columns[0].column_def.name.value);
534                        assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
535                        assert!(add_columns[0]
536                            .column_def
537                            .options
538                            .iter()
539                            .any(|o| matches!(o.option, ColumnOption::Null)));
540                        assert_eq!(&None, &add_columns[0].location);
541                    }
542                    _ => unreachable!(),
543                }
544            }
545            _ => unreachable!(),
546        }
547    }
548
549    #[test]
550    fn test_parse_alter_add_column_with_first() {
551        let sql = "ALTER TABLE my_metric_1 ADD tagk_i STRING Null FIRST;";
552        let mut result =
553            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
554                .unwrap();
555        assert_eq!(1, result.len());
556
557        let statement = result.remove(0);
558        assert_matches!(statement, Statement::AlterTable { .. });
559        match statement {
560            Statement::AlterTable(alter_table) => {
561                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
562
563                let alter_operation = alter_table.alter_operation();
564                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
565                match alter_operation {
566                    AlterTableOperation::AddColumns { add_columns } => {
567                        assert_eq!("tagk_i", add_columns[0].column_def.name.value);
568                        assert_eq!(DataType::String(None), add_columns[0].column_def.data_type);
569                        assert!(add_columns[0]
570                            .column_def
571                            .options
572                            .iter()
573                            .any(|o| matches!(o.option, ColumnOption::Null)));
574                        assert_eq!(&Some(AddColumnLocation::First), &add_columns[0].location);
575                    }
576                    _ => unreachable!(),
577                }
578            }
579            _ => unreachable!(),
580        }
581    }
582
583    #[test]
584    fn test_parse_alter_add_column_with_after() {
585        let sql =
586            "ALTER TABLE my_metric_1 ADD tagk_i STRING Null AFTER ts, add column tagl_i String;";
587        let mut result =
588            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
589                .unwrap();
590        assert_eq!(1, result.len());
591
592        let statement = result.remove(0);
593        assert_matches!(statement, Statement::AlterTable { .. });
594        match statement {
595            Statement::AlterTable(alter_table) => {
596                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
597
598                let alter_operation = alter_table.alter_operation();
599                assert_matches!(alter_operation, AlterTableOperation::AddColumns { .. });
600                match alter_operation {
601                    AlterTableOperation::AddColumns { add_columns } => {
602                        let expecteds: Vec<(Option<AddColumnLocation>, ColumnDef)> = vec![
603                            (
604                                Some(AddColumnLocation::After {
605                                    column_name: "ts".to_string(),
606                                }),
607                                ColumnDef {
608                                    name: Ident::new("tagk_i"),
609                                    data_type: DataType::String(None),
610                                    options: vec![ColumnOptionDef {
611                                        name: None,
612                                        option: ColumnOption::Null,
613                                    }],
614                                    collation: None,
615                                },
616                            ),
617                            (
618                                None,
619                                ColumnDef {
620                                    name: Ident::new("tagl_i"),
621                                    data_type: DataType::String(None),
622                                    options: vec![],
623                                    collation: None,
624                                },
625                            ),
626                        ];
627                        for (add_column, expected) in add_columns
628                            .iter()
629                            .zip(expecteds)
630                            .collect::<Vec<(&AddColumn, (Option<AddColumnLocation>, ColumnDef))>>()
631                        {
632                            assert_eq!(add_column.column_def, expected.1);
633                            assert_eq!(&expected.0, &add_column.location);
634                        }
635                    }
636                    _ => unreachable!(),
637                }
638            }
639            _ => unreachable!(),
640        }
641    }
642
643    #[test]
644    fn test_parse_add_column_if_not_exists() {
645        let sql = "ALTER TABLE test ADD COLUMN IF NOT EXISTS a INTEGER, ADD COLUMN b STRING, ADD COLUMN IF NOT EXISTS c INT;";
646        let mut result =
647            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
648                .unwrap();
649        assert_eq!(result.len(), 1);
650        let statement = result.remove(0);
651        assert_matches!(statement, Statement::AlterTable { .. });
652        match statement {
653            Statement::AlterTable(alter) => {
654                assert_eq!(alter.table_name.0[0].value, "test");
655                assert_matches!(
656                    alter.alter_operation,
657                    AlterTableOperation::AddColumns { .. }
658                );
659                match alter.alter_operation {
660                    AlterTableOperation::AddColumns { add_columns } => {
661                        let expected = vec![
662                            AddColumn {
663                                column_def: ColumnDef {
664                                    name: Ident::new("a"),
665                                    data_type: DataType::Integer(None),
666                                    collation: None,
667                                    options: vec![],
668                                },
669                                location: None,
670                                add_if_not_exists: true,
671                            },
672                            AddColumn {
673                                column_def: ColumnDef {
674                                    name: Ident::new("b"),
675                                    data_type: DataType::String(None),
676                                    collation: None,
677                                    options: vec![],
678                                },
679                                location: None,
680                                add_if_not_exists: false,
681                            },
682                            AddColumn {
683                                column_def: ColumnDef {
684                                    name: Ident::new("c"),
685                                    data_type: DataType::Int(None),
686                                    collation: None,
687                                    options: vec![],
688                                },
689                                location: None,
690                                add_if_not_exists: true,
691                            },
692                        ];
693                        for (idx, add_column) in add_columns.into_iter().enumerate() {
694                            assert_eq!(add_column, expected[idx]);
695                        }
696                    }
697                    _ => unreachable!(),
698                }
699            }
700            _ => unreachable!(),
701        }
702    }
703
704    #[test]
705    fn test_parse_alter_drop_column() {
706        let sql = "ALTER TABLE my_metric_1 DROP a";
707        let result =
708            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
709                .unwrap_err();
710        let err = result.output_msg();
711        assert_eq!(
712            err,
713            "Invalid SQL syntax: sql parser error: Expected: COLUMN, found: a at Line: 1, Column: 30"
714        );
715
716        let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
717        let mut result =
718            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
719                .unwrap();
720        assert_eq!(1, result.len());
721
722        let statement = result.remove(0);
723        assert_matches!(statement, Statement::AlterTable { .. });
724        match statement {
725            Statement::AlterTable(alter_table) => {
726                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
727
728                let alter_operation = alter_table.alter_operation();
729                assert_matches!(alter_operation, AlterTableOperation::DropColumn { .. });
730                match alter_operation {
731                    AlterTableOperation::DropColumn { name } => {
732                        assert_eq!("a", name.value);
733                    }
734                    _ => unreachable!(),
735                }
736            }
737            _ => unreachable!(),
738        }
739    }
740
741    #[test]
742    fn test_parse_alter_modify_column_type() {
743        let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
744        let result_1 = ParserContext::create_with_dialect(
745            sql_1,
746            &GreptimeDbDialect {},
747            ParseOptions::default(),
748        )
749        .unwrap();
750
751        let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
752        let mut result_2 = ParserContext::create_with_dialect(
753            sql_2,
754            &GreptimeDbDialect {},
755            ParseOptions::default(),
756        )
757        .unwrap();
758        assert_eq!(result_1, result_2);
759        assert_eq!(1, result_2.len());
760
761        let statement = result_2.remove(0);
762        assert_matches!(statement, Statement::AlterTable { .. });
763        match statement {
764            Statement::AlterTable(alter_table) => {
765                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
766
767                let alter_operation = alter_table.alter_operation();
768                assert_matches!(
769                    alter_operation,
770                    AlterTableOperation::ModifyColumnType { .. }
771                );
772                match alter_operation {
773                    AlterTableOperation::ModifyColumnType {
774                        column_name,
775                        target_type,
776                    } => {
777                        assert_eq!("a", column_name.value);
778                        assert_eq!(DataType::String(None), *target_type);
779                    }
780                    _ => unreachable!(),
781                }
782            }
783            _ => unreachable!(),
784        }
785    }
786
787    #[test]
788    fn test_parse_alter_change_column_alias_type() {
789        let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a MediumText";
790        let mut result_1 = ParserContext::create_with_dialect(
791            sql_1,
792            &GreptimeDbDialect {},
793            ParseOptions::default(),
794        )
795        .unwrap();
796
797        match result_1.remove(0) {
798            Statement::AlterTable(alter_table) => {
799                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
800
801                let alter_operation = alter_table.alter_operation();
802                assert_matches!(
803                    alter_operation,
804                    AlterTableOperation::ModifyColumnType { .. }
805                );
806                match alter_operation {
807                    AlterTableOperation::ModifyColumnType {
808                        column_name,
809                        target_type,
810                    } => {
811                        assert_eq!("a", column_name.value);
812                        assert_eq!(DataType::MediumText, *target_type);
813                    }
814                    _ => unreachable!(),
815                }
816            }
817            _ => unreachable!(),
818        }
819
820        let sql_2 = "ALTER TABLE my_metric_1 MODIFY COLUMN a TIMESTAMP_US";
821        let mut result_2 = ParserContext::create_with_dialect(
822            sql_2,
823            &GreptimeDbDialect {},
824            ParseOptions::default(),
825        )
826        .unwrap();
827
828        match result_2.remove(0) {
829            Statement::AlterTable(alter_table) => {
830                assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
831
832                let alter_operation = alter_table.alter_operation();
833                assert_matches!(
834                    alter_operation,
835                    AlterTableOperation::ModifyColumnType { .. }
836                );
837                match alter_operation {
838                    AlterTableOperation::ModifyColumnType {
839                        column_name,
840                        target_type,
841                    } => {
842                        assert_eq!("a", column_name.value);
843                        assert!(matches!(target_type, DataType::Timestamp(Some(6), _)));
844                    }
845                    _ => unreachable!(),
846                }
847            }
848            _ => unreachable!(),
849        }
850    }
851
852    #[test]
853    fn test_parse_alter_rename_table() {
854        let sql = "ALTER TABLE test_table table_t";
855        let result =
856            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
857                .unwrap_err();
858        let err = result.output_msg();
859        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");
860
861        let sql = "ALTER TABLE test_table RENAME table_t";
862        let mut result =
863            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
864                .unwrap();
865        assert_eq!(1, result.len());
866
867        let statement = result.remove(0);
868        assert_matches!(statement, Statement::AlterTable { .. });
869        match statement {
870            Statement::AlterTable(alter_table) => {
871                assert_eq!("test_table", alter_table.table_name().0[0].value);
872
873                let alter_operation = alter_table.alter_operation();
874                assert_matches!(alter_operation, AlterTableOperation::RenameTable { .. });
875                match alter_operation {
876                    AlterTableOperation::RenameTable { new_table_name } => {
877                        assert_eq!("table_t", new_table_name);
878                    }
879                    _ => unreachable!(),
880                }
881            }
882            _ => unreachable!(),
883        }
884    }
885
886    fn check_parse_alter_table_set_options(sql: &str, expected: &[(&str, &str)]) {
887        let result =
888            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
889                .unwrap();
890        assert_eq!(1, result.len());
891        let Statement::AlterTable(alter) = &result[0] else {
892            unreachable!()
893        };
894        assert_eq!("test_table", alter.table_name.0[0].value);
895        let AlterTableOperation::SetTableOptions { options } = &alter.alter_operation else {
896            unreachable!()
897        };
898
899        assert_eq!(sql, alter.to_string());
900        let res = options
901            .iter()
902            .map(|o| (o.key.as_str(), o.value.as_str()))
903            .collect::<Vec<_>>();
904        assert_eq!(expected, &res);
905    }
906
907    fn check_parse_alter_table_unset_options(sql: &str, expected: &[&str]) {
908        let result =
909            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
910                .unwrap();
911        assert_eq!(1, result.len());
912        let Statement::AlterTable(alter) = &result[0] else {
913            unreachable!()
914        };
915        assert_eq!("test_table", alter.table_name.0[0].value);
916        let AlterTableOperation::UnsetTableOptions { keys } = &alter.alter_operation else {
917            unreachable!()
918        };
919
920        assert_eq!(sql, alter.to_string());
921        let res = keys.iter().map(|o| o.to_string()).collect::<Vec<_>>();
922        assert_eq!(expected, &res);
923    }
924
925    #[test]
926    fn test_parse_alter_table_set_options() {
927        check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'='A'", &[("a", "A")]);
928        check_parse_alter_table_set_options(
929            "ALTER TABLE test_table SET 'a'='A','b'='B'",
930            &[("a", "A"), ("b", "B")],
931        );
932        check_parse_alter_table_set_options(
933            "ALTER TABLE test_table SET 'a'='A','b'='B','c'='C'",
934            &[("a", "A"), ("b", "B"), ("c", "C")],
935        );
936        check_parse_alter_table_set_options("ALTER TABLE test_table SET 'a'=NULL", &[("a", "")]);
937
938        ParserContext::create_with_dialect(
939            "ALTER TABLE test_table SET a INTEGER",
940            &GreptimeDbDialect {},
941            ParseOptions::default(),
942        )
943        .unwrap_err();
944    }
945
946    #[test]
947    fn test_parse_alter_table_unset_options() {
948        check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a'", &["a"]);
949        check_parse_alter_table_unset_options("ALTER TABLE test_table UNSET 'a','b'", &["a", "b"]);
950        ParserContext::create_with_dialect(
951            "ALTER TABLE test_table UNSET a INTEGER",
952            &GreptimeDbDialect {},
953            ParseOptions::default(),
954        )
955        .unwrap_err();
956    }
957
958    #[test]
959    fn test_parse_alter_column_fulltext() {
960        let sql = "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH(analyzer='English',case_sensitive='false',backend='bloom')";
961        let mut result =
962            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
963                .unwrap();
964
965        assert_eq!(1, result.len());
966        let statement = result.remove(0);
967        assert_matches!(statement, Statement::AlterTable { .. });
968        match statement {
969            Statement::AlterTable(alter_table) => {
970                assert_eq!("test_table", alter_table.table_name().0[0].value);
971
972                let alter_operation = alter_table.alter_operation();
973                match alter_operation {
974                    AlterTableOperation::SetIndex {
975                        options:
976                            SetIndexOperation::Fulltext {
977                                column_name,
978                                options,
979                            },
980                    } => {
981                        assert_eq!("a", column_name.value);
982                        assert_eq!(
983                            FulltextOptions {
984                                enable: true,
985                                analyzer: FulltextAnalyzer::English,
986                                case_sensitive: false,
987                                backend: FulltextBackend::Bloom,
988                            },
989                            *options
990                        );
991                    }
992                    _ => unreachable!(),
993                };
994            }
995            _ => unreachable!(),
996        }
997
998        let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET FULLTEXT INDEX";
999        let mut result =
1000            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1001                .unwrap();
1002        assert_eq!(1, result.len());
1003        let statement = result.remove(0);
1004        assert_matches!(statement, Statement::AlterTable { .. });
1005        match statement {
1006            Statement::AlterTable(alter_table) => {
1007                assert_eq!("test_table", alter_table.table_name().0[0].value);
1008
1009                let alter_operation = alter_table.alter_operation();
1010                assert_eq!(
1011                    alter_operation,
1012                    &AlterTableOperation::UnsetIndex {
1013                        options: UnsetIndexOperation::Fulltext {
1014                            column_name: Ident::new("a"),
1015                        }
1016                    }
1017                );
1018            }
1019            _ => unreachable!(),
1020        }
1021
1022        let invalid_sql =
1023            "ALTER TABLE test_table MODIFY COLUMN a SET FULLTEXT INDEX WITH('abcd'='true')";
1024        let result = ParserContext::create_with_dialect(
1025            invalid_sql,
1026            &GreptimeDbDialect {},
1027            ParseOptions::default(),
1028        )
1029        .unwrap_err();
1030        let err = result.to_string();
1031        assert_eq!(
1032            err,
1033            "Invalid column option, column name: a, error: invalid FULLTEXT option: abcd"
1034        );
1035    }
1036
1037    #[test]
1038    fn test_parse_alter_column_inverted() {
1039        let sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED INDEX";
1040        let mut result =
1041            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1042                .unwrap();
1043
1044        assert_eq!(1, result.len());
1045        let statement = result.remove(0);
1046        assert_matches!(statement, Statement::AlterTable { .. });
1047        match statement {
1048            Statement::AlterTable(alter_table) => {
1049                assert_eq!("test_table", alter_table.table_name().0[0].value);
1050
1051                let alter_operation = alter_table.alter_operation();
1052                match alter_operation {
1053                    AlterTableOperation::SetIndex {
1054                        options: SetIndexOperation::Inverted { column_name },
1055                    } => assert_eq!("a", column_name.value),
1056                    _ => unreachable!(),
1057                };
1058            }
1059            _ => unreachable!(),
1060        }
1061
1062        let sql = "ALTER TABLE test_table MODIFY COLUMN a UNSET INVERTED INDEX";
1063        let mut result =
1064            ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
1065                .unwrap();
1066        assert_eq!(1, result.len());
1067        let statement = result.remove(0);
1068        assert_matches!(statement, Statement::AlterTable { .. });
1069        match statement {
1070            Statement::AlterTable(alter_table) => {
1071                assert_eq!("test_table", alter_table.table_name().0[0].value);
1072
1073                let alter_operation = alter_table.alter_operation();
1074                assert_eq!(
1075                    alter_operation,
1076                    &AlterTableOperation::UnsetIndex {
1077                        options: UnsetIndexOperation::Inverted {
1078                            column_name: Ident::new("a"),
1079                        }
1080                    }
1081                );
1082            }
1083            _ => unreachable!(),
1084        }
1085
1086        let invalid_sql = "ALTER TABLE test_table MODIFY COLUMN a SET INVERTED";
1087        ParserContext::create_with_dialect(
1088            invalid_sql,
1089            &GreptimeDbDialect {},
1090            ParseOptions::default(),
1091        )
1092        .unwrap_err();
1093    }
1094
1095    #[test]
1096    fn test_parse_alter_with_numeric_value() {
1097        for sql in [
1098            "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'=8;",
1099            "ALTER TABLE test SET 'compaction.twcs.trigger_file_num'='8';",
1100        ] {
1101            let mut result = ParserContext::create_with_dialect(
1102                sql,
1103                &GreptimeDbDialect {},
1104                ParseOptions::default(),
1105            )
1106            .unwrap();
1107            assert_eq!(1, result.len());
1108
1109            let statement = result.remove(0);
1110            assert_matches!(statement, Statement::AlterTable { .. });
1111            match statement {
1112                Statement::AlterTable(alter_table) => {
1113                    let alter_operation = alter_table.alter_operation();
1114                    assert_matches!(alter_operation, AlterTableOperation::SetTableOptions { .. });
1115                    match alter_operation {
1116                        AlterTableOperation::SetTableOptions { options } => {
1117                            assert_eq!(options.len(), 1);
1118                            assert_eq!(options[0].key, "compaction.twcs.trigger_file_num");
1119                            assert_eq!(options[0].value, "8");
1120                        }
1121                        _ => unreachable!(),
1122                    }
1123                }
1124                _ => unreachable!(),
1125            }
1126        }
1127    }
1128}