Skip to content

Commit 0c5f6fb

Browse files
ReppCodesalamb
andauthored
ISSUE-1147: Add support for MATERIALIZED CTEs (#1148)
Co-authored-by: Andrew Repp <[email protected]> Co-authored-by: Andrew Lamb <[email protected]>
1 parent 57113a9 commit 0c5f6fb

File tree

5 files changed

+65
-9
lines changed

5 files changed

+65
-9
lines changed

src/ast/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ pub use self::ddl::{
3939
};
4040
pub use self::operator::{BinaryOperator, UnaryOperator};
4141
pub use self::query::{
42-
Cte, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause, ForJson, ForXml,
43-
GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator, JsonTableColumn,
44-
JsonTableColumnErrorHandling, LateralView, LockClause, LockType, NamedWindowDefinition,
45-
NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem, ReplaceSelectElement,
46-
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator, SetQuantifier, Table,
47-
TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity, ValueTableMode,
48-
Values, WildcardAdditionalOptions, With,
42+
Cte, CteAsMaterialized, Distinct, ExceptSelectItem, ExcludeSelectItem, Fetch, ForClause,
43+
ForJson, ForXml, GroupByExpr, IdentWithAlias, Join, JoinConstraint, JoinOperator,
44+
JsonTableColumn, JsonTableColumnErrorHandling, LateralView, LockClause, LockType,
45+
NamedWindowDefinition, NonBlock, Offset, OffsetRows, OrderByExpr, Query, RenameSelectItem,
46+
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SetOperator,
47+
SetQuantifier, Table, TableAlias, TableFactor, TableVersion, TableWithJoins, Top, TopQuantity,
48+
ValueTableMode, Values, WildcardAdditionalOptions, With,
4949
};
5050
pub use self::value::{
5151
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,31 @@ impl fmt::Display for With {
383383
}
384384
}
385385

386-
/// A single CTE (used after `WITH`): `alias [(col1, col2, ...)] AS ( query )`
386+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
387+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
388+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
389+
pub enum CteAsMaterialized {
390+
/// The `WITH` statement specifies `AS MATERIALIZED` behavior
391+
Materialized,
392+
/// The `WITH` statement specifies `AS NOT MATERIALIZED` behavior
393+
NotMaterialized,
394+
}
395+
396+
impl fmt::Display for CteAsMaterialized {
397+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
398+
match *self {
399+
CteAsMaterialized::Materialized => {
400+
write!(f, "MATERIALIZED")?;
401+
}
402+
CteAsMaterialized::NotMaterialized => {
403+
write!(f, "NOT MATERIALIZED")?;
404+
}
405+
};
406+
Ok(())
407+
}
408+
}
409+
410+
/// A single CTE (used after `WITH`): `<alias> [(col1, col2, ...)] AS <materialized> ( <query> )`
387411
/// The names in the column list before `AS`, when specified, replace the names
388412
/// of the columns returned by the query. The parser does not validate that the
389413
/// number of columns in the query matches the number of columns in the query.
@@ -394,11 +418,15 @@ pub struct Cte {
394418
pub alias: TableAlias,
395419
pub query: Box<Query>,
396420
pub from: Option<Ident>,
421+
pub materialized: Option<CteAsMaterialized>,
397422
}
398423

399424
impl fmt::Display for Cte {
400425
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401-
write!(f, "{} AS ({})", self.alias, self.query)?;
426+
match self.materialized.as_ref() {
427+
None => write!(f, "{} AS ({})", self.alias, self.query)?,
428+
Some(materialized) => write!(f, "{} AS {materialized} ({})", self.alias, self.query)?,
429+
};
402430
if let Some(ref fr) = self.from {
403431
write!(f, " FROM {fr}")?;
404432
}

src/parser/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6921,6 +6921,14 @@ impl<'a> Parser<'a> {
69216921
let name = self.parse_identifier(false)?;
69226922

69236923
let mut cte = if self.parse_keyword(Keyword::AS) {
6924+
let mut is_materialized = None;
6925+
if dialect_of!(self is PostgreSqlDialect) {
6926+
if self.parse_keyword(Keyword::MATERIALIZED) {
6927+
is_materialized = Some(CteAsMaterialized::Materialized);
6928+
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
6929+
is_materialized = Some(CteAsMaterialized::NotMaterialized);
6930+
}
6931+
}
69246932
self.expect_token(&Token::LParen)?;
69256933
let query = Box::new(self.parse_query()?);
69266934
self.expect_token(&Token::RParen)?;
@@ -6932,10 +6940,19 @@ impl<'a> Parser<'a> {
69326940
alias,
69336941
query,
69346942
from: None,
6943+
materialized: is_materialized,
69356944
}
69366945
} else {
69376946
let columns = self.parse_parenthesized_column_list(Optional, false)?;
69386947
self.expect_keyword(Keyword::AS)?;
6948+
let mut is_materialized = None;
6949+
if dialect_of!(self is PostgreSqlDialect) {
6950+
if self.parse_keyword(Keyword::MATERIALIZED) {
6951+
is_materialized = Some(CteAsMaterialized::Materialized);
6952+
} else if self.parse_keywords(&[Keyword::NOT, Keyword::MATERIALIZED]) {
6953+
is_materialized = Some(CteAsMaterialized::NotMaterialized);
6954+
}
6955+
}
69396956
self.expect_token(&Token::LParen)?;
69406957
let query = Box::new(self.parse_query()?);
69416958
self.expect_token(&Token::RParen)?;
@@ -6944,6 +6961,7 @@ impl<'a> Parser<'a> {
69446961
alias,
69456962
query,
69466963
from: None,
6964+
materialized: is_materialized,
69476965
}
69486966
};
69496967
if self.parse_keyword(Keyword::FROM) {

tests/sqlparser_common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5589,6 +5589,7 @@ fn parse_recursive_cte() {
55895589
},
55905590
query: Box::new(cte_query),
55915591
from: None,
5592+
materialized: None,
55925593
};
55935594
assert_eq!(with.cte_tables.first().unwrap(), &expected);
55945595
}

tests/sqlparser_postgres.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3837,3 +3837,12 @@ fn parse_array_agg() {
38373837
let sql4 = "SELECT ARRAY_AGG(my_schema.sections_tbl.*) AS sections FROM sections_tbl";
38383838
pg().verified_stmt(sql4);
38393839
}
3840+
3841+
#[test]
3842+
fn parse_mat_cte() {
3843+
let sql = r#"WITH cte AS MATERIALIZED (SELECT id FROM accounts) SELECT id FROM cte"#;
3844+
pg().verified_stmt(sql);
3845+
3846+
let sql2 = r#"WITH cte AS NOT MATERIALIZED (SELECT id FROM accounts) SELECT id FROM cte"#;
3847+
pg().verified_stmt(sql2);
3848+
}

0 commit comments

Comments
 (0)