From 1bbde15a15266e099e5ca578e85614746502092a Mon Sep 17 00:00:00 2001 From: tison Date: Sun, 28 Apr 2024 19:20:17 +0800 Subject: [PATCH] feat: improve error message for typo in IF NOT EXISTS (#3817) * refactor: improve error message for typo in IF NOT EXISTS Signed-off-by: tison * support table name 'if' Signed-off-by: tison * add sqlness cases Signed-off-by: tison --------- Signed-off-by: tison --- src/sql/src/parsers/create_parser.rs | 46 ++++++++++++++----- .../common/create/create_if_not_exists.result | 40 ++++++++++++++++ .../common/create/create_if_not_exists.sql | 19 ++++++++ 3 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 tests/cases/standalone/common/create/create_if_not_exists.result create mode 100644 tests/cases/standalone/common/create/create_if_not_exists.sql diff --git a/src/sql/src/parsers/create_parser.rs b/src/sql/src/parsers/create_parser.rs index 20d2ef8eab30..f6a967581fde 100644 --- a/src/sql/src/parsers/create_parser.rs +++ b/src/sql/src/parsers/create_parser.rs @@ -28,7 +28,7 @@ use table::requests::validate_table_option; use crate::ast::{ColumnDef, Ident, TableConstraint}; use crate::error::{ self, InvalidColumnOptionSnafu, InvalidTableOptionSnafu, InvalidTimeIndexSnafu, - MissingTimeIndexSnafu, Result, SyntaxSnafu, + MissingTimeIndexSnafu, Result, SyntaxSnafu, UnexpectedSnafu, UnsupportedSnafu, }; use crate::parser::ParserContext; use crate::statements::create::{ @@ -63,9 +63,7 @@ impl<'a> ParserContext<'a> { self.parser .expect_keyword(Keyword::TABLE) .context(SyntaxSnafu)?; - let if_not_exists = - self.parser - .parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + let if_not_exists = self.parse_if_not_exist()?; let table_name = self.intern_parse_table_name()?; let (columns, constraints) = self.parse_columns()?; if !columns.is_empty() { @@ -86,11 +84,7 @@ impl<'a> ParserContext<'a> { fn parse_create_database(&mut self) -> Result { let _ = self.parser.next_token(); - - let if_not_exists = - self.parser - .parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - + let if_not_exists = self.parse_if_not_exist()?; let database_name = self.parse_object_name().context(error::UnexpectedSnafu { sql: self.sql, expected: "a database name", @@ -105,9 +99,8 @@ impl<'a> ParserContext<'a> { fn parse_create_table(&mut self) -> Result { let _ = self.parser.next_token(); - let if_not_exists = - self.parser - .parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); + + let if_not_exists = self.parse_if_not_exist()?; let table_name = self.intern_parse_table_name()?; @@ -144,6 +137,35 @@ impl<'a> ParserContext<'a> { Ok(Statement::CreateTable(create_table)) } + fn parse_if_not_exist(&mut self) -> Result { + match self.parser.peek_token().token { + Token::Word(w) if Keyword::IF != w.keyword => return Ok(false), + _ => {} + } + + if self.parser.parse_keywords(&[Keyword::IF, Keyword::NOT]) { + return self + .parser + .expect_keyword(Keyword::EXISTS) + .map(|_| true) + .context(UnexpectedSnafu { + sql: self.sql, + expected: "EXISTS", + actual: self.peek_token_as_string(), + }); + } + + if self.parser.parse_keywords(&[Keyword::IF, Keyword::EXISTS]) { + return UnsupportedSnafu { + sql: self.sql, + keyword: "EXISTS", + } + .fail(); + } + + Ok(false) + } + fn parse_create_table_options(&mut self) -> Result { let options = self .parser diff --git a/tests/cases/standalone/common/create/create_if_not_exists.result b/tests/cases/standalone/common/create/create_if_not_exists.result new file mode 100644 index 000000000000..e07b7b45dcb4 --- /dev/null +++ b/tests/cases/standalone/common/create/create_if_not_exists.result @@ -0,0 +1,40 @@ +CREATE TABLE IF NOT EXIST t(); + +Error: 2000(InvalidSyntax), Unexpected token while parsing SQL statement: CREATE TABLE IF NOT EXIST t();, expected: 'EXISTS', found: EXIST: sql parser error: Expected EXISTS, found: EXIST at Line: 1, Column 21 + +CREATE TABLE IF NOT t(); + +Error: 2000(InvalidSyntax), Unexpected token while parsing SQL statement: CREATE TABLE IF NOT t();, expected: 'EXISTS', found: t: sql parser error: Expected EXISTS, found: t at Line: 1, Column 21 + +CREATE TABLE IF EXISTS t(); + +Error: 1001(Unsupported), SQL statement is not supported: CREATE TABLE IF EXISTS t();, keyword: EXISTS + +CREATE TABLE IF NOT EXISTS t(); + +Error: 2000(InvalidSyntax), Missing time index constraint + +CREATE TABLE t(); + +Error: 2000(InvalidSyntax), Missing time index constraint + +CREATE TABLE t(ts TIMESTAMP TIME INDEX); + +Affected Rows: 0 + +CREATE TABLE IF(); + +Error: 2000(InvalidSyntax), Missing time index constraint + +CREATE TABLE IF(ts TIMESTAMP TIME INDEX); + +Affected Rows: 0 + +DROP TABLE t; + +Affected Rows: 0 + +DROP TABLE IF; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/create/create_if_not_exists.sql b/tests/cases/standalone/common/create/create_if_not_exists.sql new file mode 100644 index 000000000000..9d6366c73542 --- /dev/null +++ b/tests/cases/standalone/common/create/create_if_not_exists.sql @@ -0,0 +1,19 @@ +CREATE TABLE IF NOT EXIST t(); + +CREATE TABLE IF NOT t(); + +CREATE TABLE IF EXISTS t(); + +CREATE TABLE IF NOT EXISTS t(); + +CREATE TABLE t(); + +CREATE TABLE t(ts TIMESTAMP TIME INDEX); + +CREATE TABLE IF(); + +CREATE TABLE IF(ts TIMESTAMP TIME INDEX); + +DROP TABLE t; + +DROP TABLE IF;