Skip to content

Commit c3df261

Browse files
committed
Support from first in select fix #1400
1 parent b9f6784 commit c3df261

File tree

5 files changed

+145
-72
lines changed

5 files changed

+145
-72
lines changed

src/ast/query.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::ast::*;
2323

2424
/// The most complete variant of a `SELECT` query expression, optionally
2525
/// including `WITH`, `UNION` / other set operations, and `ORDER BY`.
26-
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
26+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
2727
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2828
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2929
#[cfg_attr(feature = "visitor", visit(with = "visit_query"))]
@@ -150,6 +150,13 @@ pub enum SetExpr {
150150
Table(Box<Table>),
151151
}
152152

153+
impl Default for SetExpr {
154+
fn default() -> Self {
155+
SetExpr::Select(Box::new(Select::default()))
156+
}
157+
158+
}
159+
153160
impl SetExpr {
154161
/// If this `SetExpr` is a `SELECT`, returns the [`Select`].
155162
pub fn as_select(&self) -> Option<&Select> {
@@ -267,7 +274,7 @@ impl fmt::Display for Table {
267274
/// A restricted variant of `SELECT` (without CTEs/`ORDER BY`), which may
268275
/// appear either as the only body item of a `Query`, or as an operand
269276
/// to a set operation like `UNION`.
270-
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
277+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)]
271278
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
272279
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
273280
pub struct Select {
@@ -2106,7 +2113,13 @@ pub enum GroupByExpr {
21062113
Expressions(Vec<Expr>, Vec<GroupByWithModifier>),
21072114
}
21082115

2109-
impl fmt::Display for GroupByExpr {
2116+
impl Default for GroupByExpr {
2117+
fn default() -> Self {
2118+
GroupByExpr::Expressions(vec![], vec![])
2119+
}
2120+
}
2121+
2122+
impl Display for GroupByExpr {
21102123
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21112124
match self {
21122125
GroupByExpr::All(modifiers) => {

src/dialect/duckdb.rs

+7
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,11 @@ impl Dialect for DuckDbDialect {
5555
fn support_map_literal_syntax(&self) -> bool {
5656
true
5757
}
58+
59+
/// DuckDB allows this.
60+
///
61+
/// https://duckdb.org/2023/08/23/even-friendlier-sql.html#from-first-in-select-statements
62+
fn allow_from_first(&self) -> bool {
63+
true
64+
}
5865
}

src/dialect/generic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,8 @@ impl Dialect for GenericDialect {
9090
fn supports_create_index_with_clause(&self) -> bool {
9191
true
9292
}
93+
94+
fn allow_from_first(&self) -> bool {
95+
true
96+
}
9397
}

src/dialect/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,12 @@ pub trait Dialect: Debug + Any {
536536
fn require_interval_qualifier(&self) -> bool {
537537
false
538538
}
539+
540+
/// Whether to allow `SELECT` statements that start with a `FROM` clause,
541+
/// e.g. `FROM x SELECT foo` see <https://github.com/sqlparser-rs/sqlparser-rs/issues/1400> for rationale.
542+
fn allow_from_first(&self) -> bool {
543+
false
544+
}
539545
}
540546

541547
/// This represents the operators for which precedence must be defined

src/parser/mod.rs

+112-69
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,9 @@ impl<'a> Parser<'a> {
479479
self.prev_token();
480480
self.parse_boxed_query().map(Statement::Query)
481481
}
482+
Keyword::FROM if self.dialect.allow_from_first() => {
483+
Ok(Statement::Query(self.parse_from_first_query(None).map(Box::new)?))
484+
}
482485
Keyword::TRUNCATE => self.parse_truncate(),
483486
Keyword::ATTACH => {
484487
if dialect_of!(self is DuckDbDialect) {
@@ -8535,91 +8538,131 @@ impl<'a> Parser<'a> {
85358538
settings: None,
85368539
format_clause: None,
85378540
})
8541+
} else if self.parse_keyword(Keyword::FROM) && self.dialect.allow_from_first() {
8542+
self.parse_from_first_query(with)
85388543
} else {
85398544
let body = self.parse_boxed_query_body(self.dialect.prec_unknown())?;
8545+
self.parse_query_extra(body, with)
8546+
}
8547+
}
85408548

8541-
let order_by = self.parse_optional_order_by()?;
8542-
8543-
let mut limit = None;
8544-
let mut offset = None;
8549+
/// Parse `SELECT` statements that start with a `FROM` clause,
8550+
/// e.g. `FROM x SELECT foo` see <https://github.com/sqlparser-rs/sqlparser-rs/issues/1400> for rationale.
8551+
fn parse_from_first_query(&mut self, with: Option<With>) -> Result<Query, ParserError> {
8552+
let from = self.parse_comma_separated(Parser::parse_table_and_joins)?;
85458553

8546-
for _x in 0..2 {
8547-
if limit.is_none() && self.parse_keyword(Keyword::LIMIT) {
8548-
limit = self.parse_limit()?
8554+
if matches!(self.peek_token().token, Token::EOF | Token::SemiColon) {
8555+
// no select part e.g. `FROM x`, this is equivalent to `FROM x SELECT *`
8556+
return Ok(Query {
8557+
with,
8558+
body: Box::new(SetExpr::Select(Box::new(Select {
8559+
projection: vec![
8560+
SelectItem::Wildcard(WildcardAdditionalOptions::default()),
8561+
],
8562+
from,
8563+
..Default::default()
8564+
}))),
8565+
..Default::default()
8566+
});
8567+
} else {
8568+
let body = self.parse_boxed_query_body(self.dialect.prec_unknown())?;
8569+
let mut query = self.parse_query_extra(body, with)?;
8570+
if let SetExpr::Select(ref mut select) = *query.body {
8571+
if select.from.is_empty() {
8572+
select.from = from;
8573+
Ok(query)
8574+
} else {
8575+
Err(ParserError::ParserError("FROM clause can only be used once".to_string()))
85498576
}
8577+
} else {
8578+
Err(ParserError::ParserError("leading FROM clause can only be used with SELECT".to_string()))
8579+
}
8580+
}
8581+
}
85508582

8551-
if offset.is_none() && self.parse_keyword(Keyword::OFFSET) {
8552-
offset = Some(self.parse_offset()?)
8553-
}
8583+
/// Parse everything beyond the body of a query, i.e. everything after the `SELECT` clause
8584+
fn parse_query_extra(&mut self, body: Box<SetExpr>, with: Option<With>) -> Result<Query, ParserError> {
8585+
let order_by = self.parse_optional_order_by()?;
85548586

8555-
if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect)
8556-
&& limit.is_some()
8557-
&& offset.is_none()
8558-
&& self.consume_token(&Token::Comma)
8559-
{
8560-
// MySQL style LIMIT x,y => LIMIT y OFFSET x.
8561-
// Check <https://dev.mysql.com/doc/refman/8.0/en/select.html> for more details.
8562-
offset = Some(Offset {
8563-
value: limit.unwrap(),
8564-
rows: OffsetRows::None,
8565-
});
8566-
limit = Some(self.parse_expr()?);
8567-
}
8587+
let mut limit = None;
8588+
let mut offset = None;
8589+
8590+
for _x in 0..2 {
8591+
if limit.is_none() && self.parse_keyword(Keyword::LIMIT) {
8592+
limit = self.parse_limit()?
8593+
}
8594+
8595+
if offset.is_none() && self.parse_keyword(Keyword::OFFSET) {
8596+
offset = Some(self.parse_offset()?)
85688597
}
85698598

8570-
let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
8571-
&& self.parse_keyword(Keyword::BY)
8599+
if dialect_of!(self is GenericDialect | MySqlDialect | ClickHouseDialect)
8600+
&& limit.is_some()
8601+
&& offset.is_none()
8602+
&& self.consume_token(&Token::Comma)
85728603
{
8573-
self.parse_comma_separated(Parser::parse_expr)?
8574-
} else {
8575-
vec![]
8576-
};
8604+
// MySQL style LIMIT x,y => LIMIT y OFFSET x.
8605+
// Check <https://dev.mysql.com/doc/refman/8.0/en/select.html> for more details.
8606+
offset = Some(Offset {
8607+
value: limit.unwrap(),
8608+
rows: OffsetRows::None,
8609+
});
8610+
limit = Some(self.parse_expr()?);
8611+
}
8612+
}
85778613

8578-
let settings = self.parse_settings()?;
8614+
let limit_by = if dialect_of!(self is ClickHouseDialect | GenericDialect)
8615+
&& self.parse_keyword(Keyword::BY)
8616+
{
8617+
self.parse_comma_separated(Parser::parse_expr)?
8618+
} else {
8619+
vec![]
8620+
};
85798621

8580-
let fetch = if self.parse_keyword(Keyword::FETCH) {
8581-
Some(self.parse_fetch()?)
8582-
} else {
8583-
None
8584-
};
8622+
let settings = self.parse_settings()?;
85858623

8586-
let mut for_clause = None;
8587-
let mut locks = Vec::new();
8588-
while self.parse_keyword(Keyword::FOR) {
8589-
if let Some(parsed_for_clause) = self.parse_for_clause()? {
8590-
for_clause = Some(parsed_for_clause);
8591-
break;
8592-
} else {
8593-
locks.push(self.parse_lock()?);
8594-
}
8624+
let fetch = if self.parse_keyword(Keyword::FETCH) {
8625+
Some(self.parse_fetch()?)
8626+
} else {
8627+
None
8628+
};
8629+
8630+
let mut for_clause = None;
8631+
let mut locks = Vec::new();
8632+
while self.parse_keyword(Keyword::FOR) {
8633+
if let Some(parsed_for_clause) = self.parse_for_clause()? {
8634+
for_clause = Some(parsed_for_clause);
8635+
break;
8636+
} else {
8637+
locks.push(self.parse_lock()?);
85958638
}
8596-
let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
8597-
&& self.parse_keyword(Keyword::FORMAT)
8598-
{
8599-
if self.parse_keyword(Keyword::NULL) {
8600-
Some(FormatClause::Null)
8601-
} else {
8602-
let ident = self.parse_identifier(false)?;
8603-
Some(FormatClause::Identifier(ident))
8604-
}
8639+
}
8640+
let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
8641+
&& self.parse_keyword(Keyword::FORMAT)
8642+
{
8643+
if self.parse_keyword(Keyword::NULL) {
8644+
Some(FormatClause::Null)
86058645
} else {
8606-
None
8607-
};
8646+
let ident = self.parse_identifier(false)?;
8647+
Some(FormatClause::Identifier(ident))
8648+
}
8649+
} else {
8650+
None
8651+
};
86088652

8609-
Ok(Query {
8610-
with,
8611-
body,
8612-
order_by,
8613-
limit,
8614-
limit_by,
8615-
offset,
8616-
fetch,
8617-
locks,
8618-
for_clause,
8619-
settings,
8620-
format_clause,
8621-
})
8622-
}
8653+
Ok(Query {
8654+
with,
8655+
body,
8656+
order_by,
8657+
limit,
8658+
limit_by,
8659+
offset,
8660+
fetch,
8661+
locks,
8662+
for_clause,
8663+
settings,
8664+
format_clause,
8665+
})
86238666
}
86248667

86258668
fn parse_settings(&mut self) -> Result<Option<Vec<Setting>>, ParserError> {

0 commit comments

Comments
 (0)