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