diff --git a/e2e_test/ddl/show.slt b/e2e_test/ddl/show.slt index db9894459071f..abfd5adb45fb8 100644 --- a/e2e_test/ddl/show.slt +++ b/e2e_test/ddl/show.slt @@ -102,12 +102,12 @@ show tables from public; t3 query T -show tables from public like "t_"; +show tables from public like 't_'; ---- t3 query T -show tables from public like "_t"; +show tables from public like '_t'; ---- query T diff --git a/src/frontend/src/handler/create_sql_function.rs b/src/frontend/src/handler/create_sql_function.rs index a9ac2f394c68f..9b5d34c34abe8 100644 --- a/src/frontend/src/handler/create_sql_function.rs +++ b/src/frontend/src/handler/create_sql_function.rs @@ -150,6 +150,9 @@ pub async fn handle_create_sql_function( let body = match ¶ms.as_ { Some(FunctionDefinition::SingleQuotedDef(s)) => s.clone(), Some(FunctionDefinition::DoubleDollarDef(s)) => s.clone(), + Some(FunctionDefinition::Identifier(_)) => { + return Err(ErrorCode::InvalidParameterValue("expect quoted string".to_string()).into()) + } None => { if params.return_.is_none() { return Err(ErrorCode::InvalidParameterValue( diff --git a/src/sqlparser/src/ast/mod.rs b/src/sqlparser/src/ast/mod.rs index c27508e8e80d1..1674f32374dbc 100644 --- a/src/sqlparser/src/ast/mod.rs +++ b/src/sqlparser/src/ast/mod.rs @@ -2885,6 +2885,7 @@ impl fmt::Display for FunctionBehavior { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FunctionDefinition { + Identifier(String), SingleQuotedDef(String), DoubleDollarDef(String), } @@ -2892,6 +2893,7 @@ pub enum FunctionDefinition { impl fmt::Display for FunctionDefinition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + FunctionDefinition::Identifier(s) => write!(f, "{s}")?, FunctionDefinition::SingleQuotedDef(s) => write!(f, "'{s}'")?, FunctionDefinition::DoubleDollarDef(s) => write!(f, "$${s}$$")?, } @@ -2903,6 +2905,7 @@ impl FunctionDefinition { /// Returns the function definition as a string slice. pub fn as_str(&self) -> &str { match self { + FunctionDefinition::Identifier(s) => s, FunctionDefinition::SingleQuotedDef(s) => s, FunctionDefinition::DoubleDollarDef(s) => s, } @@ -2911,6 +2914,7 @@ impl FunctionDefinition { /// Returns the function definition as a string. pub fn into_string(self) -> String { match self { + FunctionDefinition::Identifier(s) => s, FunctionDefinition::SingleQuotedDef(s) => s, FunctionDefinition::DoubleDollarDef(s) => s, } diff --git a/src/sqlparser/src/parser.rs b/src/sqlparser/src/parser.rs index 7015bfac429f0..0bb02b85fecce 100644 --- a/src/sqlparser/src/parser.rs +++ b/src/sqlparser/src/parser.rs @@ -30,7 +30,9 @@ use winnow::{PResult, Parser as _}; use crate::ast::*; use crate::keywords::{self, Keyword}; use crate::parser_v2; -use crate::parser_v2::{keyword, literal_i64, literal_uint, single_quoted_string, ParserExt as _}; +use crate::parser_v2::{ + dollar_quoted_string, keyword, literal_i64, literal_uint, single_quoted_string, ParserExt as _, +}; use crate::tokenizer::*; pub(crate) const UPSTREAM_SOURCE_KEY: &str = "connector"; @@ -3550,16 +3552,13 @@ impl Parser<'_> { } pub fn parse_function_definition(&mut self) -> PResult { - let peek_token = self.peek_token(); - match peek_token.token { - Token::DollarQuotedString(value) => { - self.next_token(); - Ok(FunctionDefinition::DoubleDollarDef(value.value)) - } - _ => Ok(FunctionDefinition::SingleQuotedDef( - self.parse_literal_string()?, - )), - } + alt(( + single_quoted_string.map(FunctionDefinition::SingleQuotedDef), + dollar_quoted_string.map(FunctionDefinition::DoubleDollarDef), + Self::parse_identifier.map(|i| FunctionDefinition::Identifier(i.value)), + fail.expect("function definition"), + )) + .parse_next(self) } /// Parse a literal string @@ -3567,11 +3566,6 @@ impl Parser<'_> { let checkpoint = *self; let token = self.next_token(); match token.token { - Token::Word(Word { - value, - keyword: Keyword::NoKeyword, - .. - }) => Ok(value), Token::SingleQuotedString(s) => Ok(s), _ => self.expected_at(checkpoint, "literal string"), } diff --git a/src/sqlparser/src/parser_v2/mod.rs b/src/sqlparser/src/parser_v2/mod.rs index 729b56a51253a..e57c298396e13 100644 --- a/src/sqlparser/src/parser_v2/mod.rs +++ b/src/sqlparser/src/parser_v2/mod.rs @@ -129,6 +129,19 @@ where .parse_next(input) } +/// Consume an $$ dollar-quoted string $$. +pub fn dollar_quoted_string(input: &mut S) -> PResult +where + S: TokenStream, +{ + token + .verify_map(|t| match &t.token { + Token::DollarQuotedString(s) => Some(s.value.clone()), + _ => None, + }) + .parse_next(input) +} + /// Consume an object name. /// /// FIXME: Object name is extremely complex, we only handle a subset here. diff --git a/src/sqlparser/tests/testdata/select.yaml b/src/sqlparser/tests/testdata/select.yaml index 781f65760258a..f034aa8a58c9f 100644 --- a/src/sqlparser/tests/testdata/select.yaml +++ b/src/sqlparser/tests/testdata/select.yaml @@ -97,12 +97,10 @@ - input: SELECT timestamp with time zone '2022-10-01 12:00:00Z' AT TIME ZONE zone formatted_sql: SELECT TIMESTAMP WITH TIME ZONE '2022-10-01 12:00:00Z' AT TIME ZONE zone formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(AtTimeZone { timestamp: TypedString { data_type: Timestamp(true), value: "2022-10-01 12:00:00Z" }, time_zone: Identifier(Ident { value: "zone", quote_style: None }) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' -# https://www.postgresql.org/message-id/CADT4RqBPdbsZW7HS1jJP319TMRHs1hzUiP=iRJYR6UqgHCrgNQ@mail.gmail.com -- input: SELECT now() + INTERVAL '14 days' AT TIME ZONE 'UTC'; +- input: SELECT now() + INTERVAL '14 days' AT TIME ZONE 'UTC'; -- https://www.postgresql.org/message-id/CADT4RqBPdbsZW7HS1jJP319TMRHs1hzUiP=iRJYR6UqgHCrgNQ@mail.gmail.com formatted_sql: SELECT now() + INTERVAL '14 days' AT TIME ZONE 'UTC' formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Function(Function { name: ObjectName([Ident { value: "now", quote_style: None }]), args: [], variadic: false, over: None, distinct: false, order_by: [], filter: None, within_group: None }), op: Plus, right: AtTimeZone { timestamp: Value(Interval { value: "14 days", leading_field: None, leading_precision: None, last_field: None, fractional_seconds_precision: None }), time_zone: Value(SingleQuotedString("UTC")) } })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' -# https://github.com/sqlparser-rs/sqlparser-rs/issues/1266 -- input: SELECT c FROM t WHERE c >= '2019-03-27T22:00:00.000Z'::timestamp AT TIME ZONE 'Europe/Brussels'; +- input: SELECT c FROM t WHERE c >= '2019-03-27T22:00:00.000Z'::timestamp AT TIME ZONE 'Europe/Brussels'; -- https://github.com/sqlparser-rs/sqlparser-rs/issues/1266 formatted_sql: SELECT c FROM t WHERE c >= CAST('2019-03-27T22:00:00.000Z' AS TIMESTAMP) AT TIME ZONE 'Europe/Brussels' formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Identifier(Ident { value: "c", quote_style: None }))], from: [TableWithJoins { relation: Table { name: ObjectName([Ident { value: "t", quote_style: None }]), alias: None, as_of: None }, joins: [] }], lateral_views: [], selection: Some(BinaryOp { left: Identifier(Ident { value: "c", quote_style: None }), op: GtEq, right: AtTimeZone { timestamp: Cast { expr: Value(SingleQuotedString("2019-03-27T22:00:00.000Z")), data_type: Timestamp(false) }, time_zone: Value(SingleQuotedString("Europe/Brussels")) } }), group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' - input: SELECT 0c6 @@ -222,3 +220,14 @@ sql parser error: expected statement, found: selet LINE 1: selet 1; ^ +- input: select date t::date; -- https://github.com/risingwavelabs/risingwave/issues/17461 + error_msg: |- + sql parser error: expected end of statement, found: :: + LINE 1: select date t::date; -- https://github.com/risingwavelabs/risingwave/issues/17461 + ^ +- input: select date 't'::date; -- TypedString higher precedence than Cast + formatted_sql: SELECT CAST(DATE 't' AS DATE) + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Cast { expr: TypedString { data_type: Date, value: "t" }, data_type: Date })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' +- input: select date t; -- A column "date" aliased to "t" + formatted_sql: SELECT date AS t + formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [ExprWithAlias { expr: Identifier(Ident { value: "date", quote_style: None }), alias: Ident { value: "t", quote_style: None } }], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })' diff --git a/src/sqlparser/tests/testdata/show.yaml b/src/sqlparser/tests/testdata/show.yaml index a3df33bff2cbc..5edca6422bbbb 100644 --- a/src/sqlparser/tests/testdata/show.yaml +++ b/src/sqlparser/tests/testdata/show.yaml @@ -11,7 +11,7 @@ - input: SHOW TABLES FROM t formatted_sql: SHOW TABLES FROM t formatted_ast: 'ShowObjects { object: Table { schema: Some(Ident { value: "t", quote_style: None }) }, filter: None }' -- input: SHOW TABLES FROM t LIKE "t%" +- input: SHOW TABLES FROM t LIKE 't%' formatted_sql: SHOW TABLES FROM t LIKE 't%' formatted_ast: 'ShowObjects { object: Table { schema: Some(Ident { value: "t", quote_style: None }) }, filter: Some(Like("t%")) }' - input: SHOW VIEWS @@ -29,7 +29,7 @@ - input: SHOW INTERNAL TABLES FROM t formatted_sql: SHOW INTERNAL TABLES FROM t formatted_ast: 'ShowObjects { object: InternalTable { schema: Some(Ident { value: "t", quote_style: None }) }, filter: None }' -- input: SHOW INTERNAL TABLES LIKE "%mv1%" +- input: SHOW INTERNAL TABLES LIKE '%mv1%' formatted_sql: SHOW INTERNAL TABLES LIKE '%mv1%' formatted_ast: 'ShowObjects { object: InternalTable { schema: None }, filter: Some(Like("%mv1%")) }' - input: SHOW MATERIALIZED VIEWS FROM t