sql/parsers/
alter_parser.rs

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