From bcdc790b0135dc64ec4d40eaa837316530a5f5f3 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Fri, 20 Dec 2024 12:08:19 +1100 Subject: [PATCH 1/4] Handle empty projection for pg --- src/ast/query.rs | 4 +++- src/parser/mod.rs | 8 +++++++- tests/sqlparser_postgres.rs | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 948febd26..69b7ea1c1 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -350,7 +350,9 @@ impl fmt::Display for Select { } } - write!(f, " {}", display_comma_separated(&self.projection))?; + if !self.projection.is_empty() { + write!(f, " {}", display_comma_separated(&self.projection))?; + } if let Some(ref into) = self.into { write!(f, " {into}")?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e809ffba5..13083d7c1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9604,7 +9604,13 @@ impl<'a> Parser<'a> { top = Some(self.parse_top()?); } - let projection = self.parse_projection()?; + let projection = if dialect_of!(self is PostgreSqlDialect | GenericDialect) + && self.peek_keyword(Keyword::FROM) + { + vec![] + } else { + self.parse_projection()? + }; let into = if self.parse_keyword(Keyword::INTO) { let temporary = self diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index aaf4e65db..13556c837 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5195,3 +5195,8 @@ fn parse_bitstring_literal() { ))] ); } + +#[test] +fn parse_select_without_projection() { + pg_and_generic().verified_stmt("SELECT FROM users"); +} From d74afb24a134d64ebaab91b4ecef536ef4c83a62 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Sun, 22 Dec 2024 11:45:24 +1100 Subject: [PATCH 2/4] Add supports_empty_projections fn to dialect --- src/dialect/generic.rs | 4 ++++ src/dialect/mod.rs | 10 ++++++++++ src/dialect/postgresql.rs | 10 ++++++++++ 3 files changed, 24 insertions(+) diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index 61e5070fb..f852152a0 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -127,4 +127,8 @@ impl Dialect for GenericDialect { fn supports_struct_literal(&self) -> bool { true } + + fn supports_empty_projections(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index c32b763a4..7a71d662f 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -410,6 +410,16 @@ pub trait Dialect: Debug + Any { false } + /// Return true if the dialect supports empty projections in SELECT statements + /// + /// Example + /// ```sql + /// SELECT from table_name + /// ``` + fn supports_empty_projections(&self) -> bool { + false + } + /// Dialect-specific infix parser override /// /// This method is called to parse the next infix expression. diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index dcdcc88c1..80c14c8da 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -231,6 +231,16 @@ impl Dialect for PostgreSqlDialect { fn supports_named_fn_args_with_expr_name(&self) -> bool { true } + + /// Return true if the dialect supports empty projections in SELECT statements + /// + /// Example + /// ```sql + /// SELECT from table_name + /// ``` + fn supports_empty_projections(&self) -> bool { + true + } } pub fn parse_create(parser: &mut Parser) -> Option> { From 0053bf76a5de947643d5ff7571d4ef98b94f4dca Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Sun, 22 Dec 2024 11:46:02 +1100 Subject: [PATCH 3/4] Use dialect method for empty projection feature flag --- src/parser/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 13083d7c1..6ab9318b0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9604,13 +9604,12 @@ impl<'a> Parser<'a> { top = Some(self.parse_top()?); } - let projection = if dialect_of!(self is PostgreSqlDialect | GenericDialect) - && self.peek_keyword(Keyword::FROM) - { - vec![] - } else { - self.parse_projection()? - }; + let projection = + if self.dialect.supports_empty_projections() && self.peek_keyword(Keyword::FROM) { + vec![] + } else { + self.parse_projection()? + }; let into = if self.parse_keyword(Keyword::INTO) { let temporary = self From fd840142b4f4e94a46bb9af22c220036725a1261 Mon Sep 17 00:00:00 2001 From: Toby Hede Date: Sun, 22 Dec 2024 11:46:15 +1100 Subject: [PATCH 4/4] Move empty projection test to common --- tests/sqlparser_common.rs | 6 ++++++ tests/sqlparser_postgres.rs | 5 ----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e7e2e3bc2..ca217f1d9 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -12446,3 +12446,9 @@ fn overflow() { let statement = statements.pop().unwrap(); assert_eq!(statement.to_string(), sql); } + +#[test] +fn parse_select_without_projection() { + let dialects = all_dialects_where(|d| d.supports_empty_projections()); + dialects.verified_stmt("SELECT FROM users"); +} diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 13556c837..aaf4e65db 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5195,8 +5195,3 @@ fn parse_bitstring_literal() { ))] ); } - -#[test] -fn parse_select_without_projection() { - pg_and_generic().verified_stmt("SELECT FROM users"); -}