Skip to content

Commit ff42726

Browse files
committed
Resolve merge conflicts with main
Integrate supports_cte_without_as from main into the support-multi-column-alias branch.
2 parents 7708a40 + f692d90 commit ff42726

File tree

5 files changed

+122
-25
lines changed

5 files changed

+122
-25
lines changed

src/dialect/databricks.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::dialect::Dialect;
1919

2020
/// A [`Dialect`] for [Databricks SQL](https://www.databricks.com/)
2121
///
22-
/// See <https://docs.databricks.com/en/sql/language-manual/index.html>.
22+
/// See <https://docs.databricks.com/en/sql/language-manual/index.html>.
2323
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
2424
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2525
pub struct DatabricksDialect;
@@ -100,6 +100,12 @@ 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+
108+
103109
fn supports_select_item_multi_column_alias(&self) -> bool {
104110
true
105111
}

src/dialect/generic.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ impl Dialect for GenericDialect {
289289
true
290290
}
291291

292+
fn supports_cte_without_as(&self) -> bool {
293+
true
294+
}
295+
296+
292297
fn supports_select_item_multi_column_alias(&self) -> bool {
293298
true
294299
}

src/dialect/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,6 +1671,17 @@ pub trait Dialect: Debug + Any {
16711671
false
16721672
}
16731673

1674+
/// Returns true if the dialect supports the `AS` keyword being
1675+
/// optional in a CTE definition. For example:
1676+
/// ```sql
1677+
/// WITH cte_name (SELECT ...)
1678+
/// ```
1679+
///
1680+
/// [Databricks](https://docs.databricks.com/aws/en/sql/language-manual/sql-ref-syntax-qry-select-cte)
1681+
fn supports_cte_without_as(&self) -> bool {
1682+
false
1683+
}
1684+
16741685
/// Returns true if the dialect supports parenthesized multi-column
16751686
/// aliases in SELECT items. For example:
16761687
/// ```sql

src/parser/mod.rs

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14060,7 +14060,7 @@ 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

@@ -14091,32 +14091,65 @@ impl<'a> Parser<'a> {
1409114091
closing_paren_token: closing_paren_token.into(),
1409214092
}
1409314093
} else {
14094-
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);
14094+
let as_optional = self.dialect.supports_cte_without_as();
14095+
let opt_query = if as_optional {
14096+
self.maybe_parse(|p| {
14097+
p.expect_token(&Token::LParen)?;
14098+
let query = p.parse_query()?;
14099+
let closing_paren_token = p.expect_token(&Token::RParen)?;
14100+
Ok((query, closing_paren_token))
14101+
})?
14102+
} else {
14103+
None
14104+
};
14105+
match opt_query {
14106+
Some((query, closing_paren_token)) => {
14107+
let alias = TableAlias {
14108+
explicit: false,
14109+
name,
14110+
columns: vec![],
14111+
};
14112+
Cte {
14113+
alias,
14114+
query,
14115+
from: None,
14116+
materialized: None,
14117+
closing_paren_token: closing_paren_token.into(),
14118+
}
1410214119
}
14103-
}
14104-
self.expect_token(&Token::LParen)?;
14120+
None => {
14121+
let columns = self.parse_table_alias_column_defs()?;
14122+
if as_optional {
14123+
let _ = self.parse_keyword(Keyword::AS);
14124+
} else {
14125+
self.expect_keyword_is(Keyword::AS)?;
14126+
}
14127+
let mut is_materialized = None;
14128+
if dialect_of!(self is PostgreSqlDialect) {
14129+
if self.parse_keyword(Keyword::MATERIALIZED) {
14130+
is_materialized = Some(CteAsMaterialized::Materialized);
14131+
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
14132+
is_materialized = Some(CteAsMaterialized::NotMaterialized);
14133+
}
14134+
}
14135+
self.expect_token(&Token::LParen)?;
1410514136

14106-
let query = self.parse_query()?;
14107-
let closing_paren_token = self.expect_token(&Token::RParen)?;
14137+
let query = self.parse_query()?;
14138+
let closing_paren_token = self.expect_token(&Token::RParen)?;
1410814139

14109-
let alias = TableAlias {
14110-
explicit: false,
14111-
name,
14112-
columns,
14113-
};
14114-
Cte {
14115-
alias,
14116-
query,
14117-
from: None,
14118-
materialized: is_materialized,
14119-
closing_paren_token: closing_paren_token.into(),
14140+
let alias = TableAlias {
14141+
explicit: false,
14142+
name,
14143+
columns,
14144+
};
14145+
Cte {
14146+
alias,
14147+
query,
14148+
from: None,
14149+
materialized: is_materialized,
14150+
closing_paren_token: closing_paren_token.into(),
14151+
}
14152+
}
1412014153
}
1412114154
};
1412214155
if self.parse_keyword(Keyword::FROM) {

tests/sqlparser_databricks.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,45 @@ 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+
681+
}
682+
683+
#[test]
684+
fn parse_select_item_multi_column_alias() {
685+
databricks_and_generic().verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2)");
686+
687+
databricks_and_generic()
688+
.verified_stmt("SELECT stack(2, 'a', 'b', 'c', 'd') AS (col1, col2) FROM t");
689+
690+
assert!(
691+
all_dialects_where(|d| !d.supports_select_item_multi_column_alias())
692+
.parse_sql_statements("SELECT stack(2, 'a', 'b') AS (col1, col2)")
693+
.is_err()
694+
);
695+
}

0 commit comments

Comments
 (0)