diff --git a/src/ast/query.rs b/src/ast/query.rs
index c52d01105..282f50d05 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -23,7 +23,7 @@ use crate::ast::*;
/// The most complete variant of a `SELECT` query expression, optionally
/// including `WITH`, `UNION` / other set operations, and `ORDER BY`.
-#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
#[cfg_attr(feature = "visitor", visit(with = "visit_query"))]
@@ -150,6 +150,13 @@ pub enum SetExpr {
Table(Box
),
}
+impl Default for SetExpr {
+ fn default() -> Self {
+ SetExpr::Select(Box::new(Select::default()))
+ }
+
+}
+
impl SetExpr {
/// If this `SetExpr` is a `SELECT`, returns the [`Select`].
pub fn as_select(&self) -> Option<&Select> {
@@ -267,7 +274,7 @@ impl fmt::Display for Table {
/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
/// appear either as the only body item of a `Query`, or as an operand
/// to a set operation like `UNION`.
-#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Select {
@@ -2106,7 +2113,13 @@ pub enum GroupByExpr {
Expressions(Vec, Vec),
}
-impl fmt::Display for GroupByExpr {
+impl Default for GroupByExpr {
+ fn default() -> Self {
+ GroupByExpr::Expressions(vec![], vec![])
+ }
+}
+
+impl Display for GroupByExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
GroupByExpr::All(modifiers) => {
diff --git a/src/dialect/duckdb.rs b/src/dialect/duckdb.rs
index 1fc211685..9a6fb73eb 100644
--- a/src/dialect/duckdb.rs
+++ b/src/dialect/duckdb.rs
@@ -55,4 +55,11 @@ impl Dialect for DuckDbDialect {
fn support_map_literal_syntax(&self) -> bool {
true
}
+
+ /// DuckDB allows this.
+ ///
+ /// https://duckdb.org/2023/08/23/even-friendlier-sql.html#from-first-in-select-statements
+ fn allow_from_first(&self) -> bool {
+ true
+ }
}
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index c8f1c00d9..bceb7f3c4 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -90,4 +90,8 @@ impl Dialect for GenericDialect {
fn supports_create_index_with_clause(&self) -> bool {
true
}
+
+ fn allow_from_first(&self) -> bool {
+ true
+ }
}
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 6b80243ff..d0ae33810 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -536,6 +536,12 @@ pub trait Dialect: Debug + Any {
fn require_interval_qualifier(&self) -> bool {
false
}
+
+ /// Whether to allow `SELECT` statements that start with a `FROM` clause,
+ /// e.g. `FROM x SELECT foo` see for rationale.
+ fn allow_from_first(&self) -> bool {
+ false
+ }
}
/// This represents the operators for which precedence must be defined
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 8d920b2ce..110562288 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -479,6 +479,9 @@ impl<'a> Parser<'a> {
self.prev_token();
self.parse_boxed_query().map(Statement::Query)
}
+ Keyword::FROM if self.dialect.allow_from_first() => {
+ Ok(Statement::Query(self.parse_from_first_query(None).map(Box::new)?))
+ }
Keyword::TRUNCATE => self.parse_truncate(),
Keyword::ATTACH => {
if dialect_of!(self is DuckDbDialect) {
@@ -8535,91 +8538,131 @@ impl<'a> Parser<'a> {
settings: None,
format_clause: None,
})
+ } else if self.parse_keyword(Keyword::FROM) && self.dialect.allow_from_first() {
+ self.parse_from_first_query(with)
} else {
let body = self.parse_boxed_query_body(self.dialect.prec_unknown())?;
+ self.parse_query_extra(body, with)
+ }
+ }
- let order_by = self.parse_optional_order_by()?;
-
- let mut limit = None;
- let mut offset = None;
+ /// Parse `SELECT` statements that start with a `FROM` clause,
+ /// e.g. `FROM x SELECT foo` see for rationale.
+ fn parse_from_first_query(&mut self, with: Option) -> Result {
+ let from = self.parse_comma_separated(Parser::parse_table_and_joins)?;
- for _x in 0..2 {
- if limit.is_none() && self.parse_keyword(Keyword::LIMIT) {
- limit = self.parse_limit()?
+ if matches!(self.peek_token().token, Token::EOF | Token::SemiColon) {
+ // no select part e.g. `FROM x`, this is equivalent to `FROM x SELECT *`
+ return Ok(Query {
+ with,
+ body: Box::new(SetExpr::Select(Box::new(Select {
+ projection: vec![
+ SelectItem::Wildcard(WildcardAdditionalOptions::default()),
+ ],
+ from,
+ ..Default::default()
+ }))),
+ ..Default::default()
+ });
+ } else {
+ let body = self.parse_boxed_query_body(self.dialect.prec_unknown())?;
+ let mut query = self.parse_query_extra(body, with)?;
+ if let SetExpr::Select(ref mut select) = *query.body {
+ if select.from.is_empty() {
+ select.from = from;
+ Ok(query)
+ } else {
+ Err(ParserError::ParserError("FROM clause can only be used once".to_string()))
}
+ } else {
+ Err(ParserError::ParserError("leading FROM clause can only be used with SELECT".to_string()))
+ }
+ }
+ }
- if offset.is_none() && self.parse_keyword(Keyword::OFFSET) {
- offset = Some(self.parse_offset()?)
- }
+ /// Parse everything beyond the body of a query, i.e. everything after the `SELECT` clause
+ fn parse_query_extra(&mut self, body: Box, with: Option) -> Result {
+ let order_by = self.parse_optional_order_by()?;
- if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect)
- && limit.is_some()
- && offset.is_none()
- && self.consume_token(&Token::Comma)
- {
- // MySQL style LIMIT x,y => LIMIT y OFFSET x.
- // Check for more details.
- offset = Some(Offset {
- value: limit.unwrap(),
- rows: OffsetRows::None,
- });
- limit = Some(self.parse_expr()?);
- }
+ let mut limit = None;
+ let mut offset = None;
+
+ for _x in 0..2 {
+ if limit.is_none() && self.parse_keyword(Keyword::LIMIT) {
+ limit = self.parse_limit()?
+ }
+
+ if offset.is_none() && self.parse_keyword(Keyword::OFFSET) {
+ offset = Some(self.parse_offset()?)
}
- let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
- && self.parse_keyword(Keyword::BY)
+ if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect)
+ && limit.is_some()
+ && offset.is_none()
+ && self.consume_token(&Token::Comma)
{
- self.parse_comma_separated(Parser::parse_expr)?
- } else {
- vec![]
- };
+ // MySQL style LIMIT x,y => LIMIT y OFFSET x.
+ // Check for more details.
+ offset = Some(Offset {
+ value: limit.unwrap(),
+ rows: OffsetRows::None,
+ });
+ limit = Some(self.parse_expr()?);
+ }
+ }
- let settings = self.parse_settings()?;
+ let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
+ && self.parse_keyword(Keyword::BY)
+ {
+ self.parse_comma_separated(Parser::parse_expr)?
+ } else {
+ vec![]
+ };
- let fetch = if self.parse_keyword(Keyword::FETCH) {
- Some(self.parse_fetch()?)
- } else {
- None
- };
+ let settings = self.parse_settings()?;
- let mut for_clause = None;
- let mut locks = Vec::new();
- while self.parse_keyword(Keyword::FOR) {
- if let Some(parsed_for_clause) = self.parse_for_clause()? {
- for_clause = Some(parsed_for_clause);
- break;
- } else {
- locks.push(self.parse_lock()?);
- }
+ let fetch = if self.parse_keyword(Keyword::FETCH) {
+ Some(self.parse_fetch()?)
+ } else {
+ None
+ };
+
+ let mut for_clause = None;
+ let mut locks = Vec::new();
+ while self.parse_keyword(Keyword::FOR) {
+ if let Some(parsed_for_clause) = self.parse_for_clause()? {
+ for_clause = Some(parsed_for_clause);
+ break;
+ } else {
+ locks.push(self.parse_lock()?);
}
- let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
- && self.parse_keyword(Keyword::FORMAT)
- {
- if self.parse_keyword(Keyword::NULL) {
- Some(FormatClause::Null)
- } else {
- let ident = self.parse_identifier(false)?;
- Some(FormatClause::Identifier(ident))
- }
+ }
+ let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
+ && self.parse_keyword(Keyword::FORMAT)
+ {
+ if self.parse_keyword(Keyword::NULL) {
+ Some(FormatClause::Null)
} else {
- None
- };
+ let ident = self.parse_identifier(false)?;
+ Some(FormatClause::Identifier(ident))
+ }
+ } else {
+ None
+ };
- Ok(Query {
- with,
- body,
- order_by,
- limit,
- limit_by,
- offset,
- fetch,
- locks,
- for_clause,
- settings,
- format_clause,
- })
- }
+ Ok(Query {
+ with,
+ body,
+ order_by,
+ limit,
+ limit_by,
+ offset,
+ fetch,
+ locks,
+ for_clause,
+ settings,
+ format_clause,
+ })
}
fn parse_settings(&mut self) -> Result