Skip to content

Support from first in select #1429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
19 changes: 16 additions & 3 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"))]
Expand Down Expand Up @@ -150,6 +150,13 @@ pub enum SetExpr {
Table(Box<Table>),
}

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> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -2106,7 +2113,13 @@ pub enum GroupByExpr {
Expressions(Vec<Expr>, Vec<GroupByWithModifier>),
}

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) => {
Expand Down
7 changes: 7 additions & 0 deletions src/dialect/duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
4 changes: 4 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,8 @@ impl Dialect for GenericDialect {
fn supports_create_index_with_clause(&self) -> bool {
true
}

fn allow_from_first(&self) -> bool {
true
}
}
6 changes: 6 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/sqlparser-rs/sqlparser-rs/issues/1400> for rationale.
fn allow_from_first(&self) -> bool {
false
}
}

/// This represents the operators for which precedence must be defined
Expand Down
181 changes: 112 additions & 69 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 <https://github.com/sqlparser-rs/sqlparser-rs/issues/1400> for rationale.
fn parse_from_first_query(&mut self, with: Option<With>) -> Result<Query, ParserError> {
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<SetExpr>, with: Option<With>) -> Result<Query, ParserError> {
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 <https://dev.mysql.com/doc/refman/8.0/en/select.html> 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 <https://dev.mysql.com/doc/refman/8.0/en/select.html> 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<Option<Vec<Setting>>, ParserError> {
Expand Down
Loading