Skip to content

Commit 325744f

Browse files
committed
Resolve merge conflicts with upstream/main
Integrate supports_cte_without_as and other upstream changes into the support-multi-column-alias branch.
2 parents 7708a40 + 827978d commit 325744f

File tree

11 files changed

+195
-44
lines changed

11 files changed

+195
-44
lines changed

src/ast/dml.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ pub struct Update {
387387
pub output: Option<OutputClause>,
388388
/// SQLite-specific conflict resolution clause
389389
pub or: Option<SqliteOnConflict>,
390+
/// ORDER BY (MySQL extension for single-table UPDATE)
391+
/// See <https://dev.mysql.com/doc/refman/8.4/en/update.html>
392+
pub order_by: Vec<OrderByExpr>,
390393
/// LIMIT
391394
pub limit: Option<Expr>,
392395
}
@@ -434,6 +437,11 @@ impl Display for Update {
434437
f.write_str("RETURNING")?;
435438
indented_list(f, returning)?;
436439
}
440+
if !self.order_by.is_empty() {
441+
SpaceOrNewline.fmt(f)?;
442+
f.write_str("ORDER BY")?;
443+
indented_list(f, &self.order_by)?;
444+
}
437445
if let Some(limit) = &self.limit {
438446
SpaceOrNewline.fmt(f)?;
439447
write!(f, "LIMIT {limit}")?;

src/ast/spans.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,7 @@ impl Spanned for Update {
952952
returning,
953953
output,
954954
or: _,
955+
order_by,
955956
limit,
956957
} = self;
957958

@@ -963,6 +964,7 @@ impl Spanned for Update {
963964
.chain(selection.iter().map(|i| i.span()))
964965
.chain(returning.iter().flat_map(|i| i.iter().map(|k| k.span())))
965966
.chain(output.iter().map(|i| i.span()))
967+
.chain(order_by.iter().map(|i| i.span()))
966968
.chain(limit.iter().map(|i| i.span())),
967969
)
968970
}

src/dialect/databricks.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ impl Dialect for DatabricksDialect {
100100
true
101101
}
102102

103+
/// See <https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-cte>
104+
fn supports_cte_without_as(&self) -> bool {
105+
true
106+
}
107+
103108
fn supports_select_item_multi_column_alias(&self) -> bool {
104109
true
105110
}

src/dialect/generic.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ impl Dialect for GenericDialect {
133133
true
134134
}
135135

136+
fn supports_update_order_by(&self) -> bool {
137+
true
138+
}
139+
136140
fn supports_from_first_select(&self) -> bool {
137141
true
138142
}
@@ -289,6 +293,10 @@ impl Dialect for GenericDialect {
289293
true
290294
}
291295

296+
fn supports_cte_without_as(&self) -> bool {
297+
true
298+
}
299+
292300
fn supports_select_item_multi_column_alias(&self) -> bool {
293301
true
294302
}

src/dialect/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,16 @@ pub trait Dialect: Debug + Any {
520520
false
521521
}
522522

523+
/// Returns true if the dialect supports `ORDER BY` in `UPDATE` statements.
524+
///
525+
/// ```sql
526+
/// UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC;
527+
/// ```
528+
/// See <https://dev.mysql.com/doc/refman/8.4/en/update.html>
529+
fn supports_update_order_by(&self) -> bool {
530+
false
531+
}
532+
523533
/// Returns true if the dialect supports an `EXCEPT` clause following a
524534
/// wildcard in a select list.
525535
///
@@ -1671,6 +1681,17 @@ pub trait Dialect: Debug + Any {
16711681
false
16721682
}
16731683

1684+
/// Returns true if the dialect supports the `AS` keyword being
1685+
/// optional in a CTE definition. For example:
1686+
/// ```sql
1687+
/// WITH cte_name (SELECT ...)
1688+
/// ```
1689+
///
1690+
/// [Databricks](https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-cte)
1691+
fn supports_cte_without_as(&self) -> bool {
1692+
false
1693+
}
1694+
16741695
/// Returns true if the dialect supports parenthesized multi-column
16751696
/// aliases in SELECT items. For example:
16761697
/// ```sql

src/dialect/mysql.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ impl Dialect for MySqlDialect {
179179
true
180180
}
181181

182+
/// See: <https://dev.mysql.com/doc/refman/8.4/en/update.html>
183+
fn supports_update_order_by(&self) -> bool {
184+
true
185+
}
186+
182187
fn supports_data_type_signed_suffix(&self) -> bool {
183188
true
184189
}

src/parser/mod.rs

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14060,64 +14060,74 @@ impl<'a> Parser<'a> {
1406014060
})
1406114061
}
1406214062

14063-
/// Parse a CTE (`alias [( col1, col2, ... )] AS (subquery)`)
14063+
/// Parse a CTE (`alias [( col1, col2, ... )] [AS] (subquery)`)
1406414064
pub fn parse_cte(&mut self) -> Result<Cte, ParserError> {
1406514065
let name = self.parse_identifier()?;
1406614066

14067-
let mut cte = if self.parse_keyword(Keyword::AS) {
14068-
let mut is_materialized = None;
14069-
if dialect_of!(self is PostgreSqlDialect) {
14070-
if self.parse_keyword(Keyword::MATERIALIZED) {
14071-
is_materialized = Some(CteAsMaterialized::Materialized);
14072-
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
14073-
is_materialized = Some(CteAsMaterialized::NotMaterialized);
14067+
let as_optional = self.dialect.supports_cte_without_as();
14068+
14069+
// If AS is optional, first try to parse `name (query)` directly
14070+
if as_optional && !self.peek_keyword(Keyword::AS) {
14071+
if let Some((query, closing_paren_token)) = self.maybe_parse(|p| {
14072+
p.expect_token(&Token::LParen)?;
14073+
let query = p.parse_query()?;
14074+
let closing_paren_token = p.expect_token(&Token::RParen)?;
14075+
Ok((query, closing_paren_token))
14076+
})? {
14077+
let mut cte = Cte {
14078+
alias: TableAlias {
14079+
explicit: false,
14080+
name,
14081+
columns: vec![],
14082+
},
14083+
query,
14084+
from: None,
14085+
materialized: None,
14086+
closing_paren_token: closing_paren_token.into(),
14087+
};
14088+
if self.parse_keyword(Keyword::FROM) {
14089+
cte.from = Some(self.parse_identifier()?);
1407414090
}
14091+
return Ok(cte);
1407514092
}
14076-
self.expect_token(&Token::LParen)?;
14077-
14078-
let query = self.parse_query()?;
14079-
let closing_paren_token = self.expect_token(&Token::RParen)?;
14093+
}
1408014094

14081-
let alias = TableAlias {
14082-
explicit: false,
14083-
name,
14084-
columns: vec![],
14085-
};
14086-
Cte {
14087-
alias,
14088-
query,
14089-
from: None,
14090-
materialized: is_materialized,
14091-
closing_paren_token: closing_paren_token.into(),
14092-
}
14095+
// Determine column definitions and consume AS
14096+
let columns = if self.parse_keyword(Keyword::AS) {
14097+
vec![]
1409314098
} else {
1409414099
let columns = self.parse_table_alias_column_defs()?;
14095-
self.expect_keyword_is(Keyword::AS)?;
14096-
let mut is_materialized = None;
14097-
if dialect_of!(self is PostgreSqlDialect) {
14098-
if self.parse_keyword(Keyword::MATERIALIZED) {
14099-
is_materialized = Some(CteAsMaterialized::Materialized);
14100-
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
14101-
is_materialized = Some(CteAsMaterialized::NotMaterialized);
14102-
}
14100+
if as_optional {
14101+
let _ = self.parse_keyword(Keyword::AS);
14102+
} else {
14103+
self.expect_keyword_is(Keyword::AS)?;
1410314104
}
14104-
self.expect_token(&Token::LParen)?;
14105+
columns
14106+
};
1410514107

14106-
let query = self.parse_query()?;
14107-
let closing_paren_token = self.expect_token(&Token::RParen)?;
14108+
let mut is_materialized = None;
14109+
if dialect_of!(self is PostgreSqlDialect) {
14110+
if self.parse_keyword(Keyword::MATERIALIZED) {
14111+
is_materialized = Some(CteAsMaterialized::Materialized);
14112+
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
14113+
is_materialized = Some(CteAsMaterialized::NotMaterialized);
14114+
}
14115+
}
1410814116

14109-
let alias = TableAlias {
14117+
self.expect_token(&Token::LParen)?;
14118+
let query = self.parse_query()?;
14119+
let closing_paren_token = self.expect_token(&Token::RParen)?;
14120+
14121+
let mut cte = Cte {
14122+
alias: TableAlias {
1411014123
explicit: false,
1411114124
name,
1411214125
columns,
14113-
};
14114-
Cte {
14115-
alias,
14116-
query,
14117-
from: None,
14118-
materialized: is_materialized,
14119-
closing_paren_token: closing_paren_token.into(),
14120-
}
14126+
},
14127+
query,
14128+
from: None,
14129+
materialized: is_materialized,
14130+
closing_paren_token: closing_paren_token.into(),
1412114131
};
1412214132
if self.parse_keyword(Keyword::FROM) {
1412314133
cte.from = Some(self.parse_identifier()?);
@@ -17745,6 +17755,13 @@ impl<'a> Parser<'a> {
1774517755
} else {
1774617756
None
1774717757
};
17758+
let order_by = if self.dialect.supports_update_order_by()
17759+
&& self.parse_keywords(&[Keyword::ORDER, Keyword::BY])
17760+
{
17761+
self.parse_comma_separated(Parser::parse_order_by_expr)?
17762+
} else {
17763+
vec![]
17764+
};
1774817765
let limit = if self.parse_keyword(Keyword::LIMIT) {
1774917766
Some(self.parse_expr()?)
1775017767
} else {
@@ -17760,6 +17777,7 @@ impl<'a> Parser<'a> {
1776017777
returning,
1776117778
output,
1776217779
or,
17780+
order_by,
1776317781
limit,
1776417782
}
1776517783
.into())

tests/sqlparser_common.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ fn parse_update_set_from() {
535535
returning: None,
536536
output: None,
537537
or: None,
538+
order_by: vec![],
538539
limit: None
539540
})
540541
);
@@ -554,6 +555,7 @@ fn parse_update_with_table_alias() {
554555
selection,
555556
returning,
556557
or: None,
558+
order_by: _,
557559
limit: None,
558560
optimizer_hints,
559561
update_token: _,

tests/sqlparser_databricks.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,30 @@ fn parse_numeric_prefix_identifier() {
651651

652652
databricks().verified_stmt("SELECT * FROM a.b.1c");
653653
}
654+
655+
#[test]
656+
fn parse_cte_without_as() {
657+
databricks_and_generic().one_statement_parses_to(
658+
"WITH cte (SELECT 1) SELECT * FROM cte",
659+
"WITH cte AS (SELECT 1) SELECT * FROM cte",
660+
);
661+
662+
databricks_and_generic().one_statement_parses_to(
663+
"WITH a AS (SELECT 1), b (SELECT 2) SELECT * FROM a, b",
664+
"WITH a AS (SELECT 1), b AS (SELECT 2) SELECT * FROM a, b",
665+
);
666+
667+
databricks_and_generic().one_statement_parses_to(
668+
"WITH cte (col1, col2) (SELECT 1, 2) SELECT * FROM cte",
669+
"WITH cte (col1, col2) AS (SELECT 1, 2) SELECT * FROM cte",
670+
);
671+
672+
databricks_and_generic().verified_query("WITH cte AS (SELECT 1) SELECT * FROM cte");
673+
674+
databricks_and_generic()
675+
.verified_query("WITH cte (col1, col2) AS (SELECT 1, 2) SELECT * FROM cte");
676+
677+
assert!(all_dialects_where(|d| !d.supports_cte_without_as())
678+
.parse_sql_statements("WITH cte (SELECT 1) SELECT * FROM cte")
679+
.is_err());
680+
}

tests/sqlparser_mysql.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,7 @@ fn parse_update_with_joins() {
27072707
selection,
27082708
returning,
27092709
or: None,
2710+
order_by: _,
27102711
limit: None,
27112712
optimizer_hints,
27122713
update_token: _,
@@ -2784,6 +2785,59 @@ fn parse_update_with_joins() {
27842785
}
27852786
}
27862787

2788+
#[test]
2789+
fn parse_update_with_order_by() {
2790+
let sql = "UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC";
2791+
match mysql_and_generic().verified_stmt(sql) {
2792+
Statement::Update(Update { order_by, .. }) => {
2793+
assert_eq!(
2794+
vec![OrderByExpr {
2795+
expr: Expr::Identifier(Ident {
2796+
value: "foo".to_owned(),
2797+
quote_style: None,
2798+
span: Span::empty(),
2799+
}),
2800+
options: OrderByOptions {
2801+
asc: Some(true),
2802+
nulls_first: None,
2803+
},
2804+
with_fill: None,
2805+
}],
2806+
order_by
2807+
);
2808+
}
2809+
_ => unreachable!(),
2810+
}
2811+
}
2812+
2813+
#[test]
2814+
fn parse_update_with_order_by_and_limit() {
2815+
let sql = "UPDATE foo SET bar = false WHERE foo = true ORDER BY foo ASC LIMIT 10";
2816+
match mysql_and_generic().verified_stmt(sql) {
2817+
Statement::Update(Update {
2818+
order_by, limit, ..
2819+
}) => {
2820+
assert_eq!(
2821+
vec![OrderByExpr {
2822+
expr: Expr::Identifier(Ident {
2823+
value: "foo".to_owned(),
2824+
quote_style: None,
2825+
span: Span::empty(),
2826+
}),
2827+
options: OrderByOptions {
2828+
asc: Some(true),
2829+
nulls_first: None,
2830+
},
2831+
with_fill: None,
2832+
}],
2833+
order_by
2834+
);
2835+
assert_eq!(Some(Expr::value(number("10"))), limit);
2836+
}
2837+
_ => unreachable!(),
2838+
}
2839+
}
2840+
27872841
#[test]
27882842
fn parse_delete_with_order_by() {
27892843
let sql = "DELETE FROM customers ORDER BY id DESC";

0 commit comments

Comments
 (0)