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>, ParserError> {