Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions e2e_test/ddl/show.slt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/src/handler/create_sql_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ pub async fn handle_create_sql_function(
let body = match &params.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(
Expand Down
4 changes: 4 additions & 0 deletions src/sqlparser/src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2885,13 +2885,15 @@ 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),
}

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}$$")?,
}
Expand All @@ -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,
}
Expand All @@ -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,
}
Expand Down
26 changes: 10 additions & 16 deletions src/sqlparser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -3550,28 +3552,20 @@ impl Parser<'_> {
}

pub fn parse_function_definition(&mut self) -> PResult<FunctionDefinition> {
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)),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I said (unquoted or double quoted) identifier I was thinking of .real_value() but now I realize .value has better backward compatibility:

When the SQL to parse is as camelCase, the old behavior gets camelCase but the identifier's real_value is camelcase. Although as "camelCase" is a case-sensitive identifier, I agree it is better to encourage as 'camelCase' if breaking change is inevitable.

fail.expect("function definition"),
))
.parse_next(self)
}

/// Parse a literal string
pub fn parse_literal_string(&mut self) -> PResult<String> {
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"),
}
Expand Down
13 changes: 13 additions & 0 deletions src/sqlparser/src/parser_v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,19 @@ where
.parse_next(input)
}

/// Consume an $$ dollar-quoted string $$.
pub fn dollar_quoted_string<S>(input: &mut S) -> PResult<String>
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.
Expand Down
17 changes: 13 additions & 4 deletions src/sqlparser/tests/testdata/select.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 })'
4 changes: 2 additions & 2 deletions src/sqlparser/tests/testdata/show.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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%'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking this fix is a breaking change. But it was wrong syntax and our documentation has been using single quotes correctly. The impact shall be negligible.

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
Expand All @@ -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
Expand Down