Skip to content

Commit 52c7ba1

Browse files
committed
sql-parser: select with CTE
1 parent fffb833 commit 52c7ba1

File tree

5 files changed

+209
-8
lines changed

5 files changed

+209
-8
lines changed

src/ast/cte.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct WithCteStatement {
99

1010
pub cte_expressions: Vec<CteExpression>,
1111

12-
pub statement: Statement,
12+
pub statement: Box<Statement>,
1313
}
1414

1515
/// An AST for a single CTE expression.
@@ -19,7 +19,13 @@ pub struct CteExpression {
1919

2020
pub column_names: Vec<Identifier>,
2121

22-
pub materialized: Option<bool>,
22+
pub materialized: Option<MaterializationType>,
2323

24-
pub select: Vec<SelectStatement>,
24+
pub select: SelectStatement,
25+
}
26+
27+
#[derive(Debug, PartialEq)]
28+
pub enum MaterializationType {
29+
Materialized,
30+
NotMaterialized,
2531
}

src/ast/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub use create::{
2020
CreateIndexStatement, CreateTableStatement, CreateTriggerStatement, CreateViewStatement,
2121
CreateVirtualTableStatement,
2222
};
23+
pub use cte::{CteExpression, MaterializationType, WithCteStatement};
2324
pub use delete::DeleteStatement;
2425
pub use drop::{DropIndexStatement, DropTableStatement, DropTriggerStatement, DropViewStatement};
2526
pub use explain::ExplainStatement;
@@ -94,6 +95,9 @@ pub enum Statement {
9495
/// SQLite Administrative statements, see [ReindexStatement]
9596
Reindex(ReindexStatement),
9697

98+
/// WITH statement, see [WithCteStatement]
99+
WithCte(WithCteStatement),
100+
97101
/// EXPLAIN statement (can wrap another statement), see [ExplainStatement]
98102
Explain(ExplainStatement),
99103
}

src/parser/cte.rs

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use crate::{
2+
expression::IdentifierParser, CteExpression, Identifier, Keyword, MaterializationType,
3+
TokenType, WithCteStatement,
4+
};
5+
6+
use super::{select::SelectStatementParser, Parser, ParsingError};
7+
8+
pub trait CteStatementParser {
9+
fn parse_cte_statement(&mut self) -> Result<WithCteStatement, ParsingError>;
10+
11+
fn parse_cte_expression(&mut self) -> Result<CteExpression, ParsingError>;
12+
13+
fn parse_cte_column_names(&mut self) -> Result<Vec<Identifier>, ParsingError>;
14+
15+
fn parse_cte_materialized(&mut self) -> Result<Option<MaterializationType>, ParsingError>;
16+
}
17+
18+
impl<'a> CteStatementParser for Parser<'a> {
19+
fn parse_cte_statement(&mut self) -> Result<WithCteStatement, ParsingError> {
20+
self.consume_as_keyword(Keyword::With)?;
21+
22+
let recursive = self.consume_as_keyword(Keyword::Recursive).is_ok();
23+
24+
let mut cte_expressions = Vec::new();
25+
loop {
26+
let cte_expression = self.parse_cte_expression()?;
27+
cte_expressions.push(cte_expression);
28+
29+
if self.consume_as(TokenType::Comma).is_err() {
30+
break;
31+
}
32+
}
33+
34+
let statement = self.parse_statement()?;
35+
36+
Ok(WithCteStatement {
37+
recursive,
38+
cte_expressions,
39+
statement: Box::new(statement),
40+
})
41+
}
42+
43+
fn parse_cte_expression(&mut self) -> Result<CteExpression, ParsingError> {
44+
let name = self.parse_identifier()?;
45+
let column_names = self.parse_cte_column_names()?;
46+
47+
self.consume_as_keyword(Keyword::As)?;
48+
49+
let materialized = self.parse_cte_materialized()?;
50+
51+
self.consume_as(TokenType::LeftParen)?;
52+
let select = self.parse_select_statement()?;
53+
self.consume_as(TokenType::RightParen)?;
54+
55+
Ok(CteExpression {
56+
name,
57+
column_names,
58+
materialized,
59+
select,
60+
})
61+
}
62+
63+
fn parse_cte_column_names(&mut self) -> Result<Vec<Identifier>, ParsingError> {
64+
let mut column_names = Vec::new();
65+
66+
if self.consume_as(TokenType::LeftParen).is_ok() {
67+
loop {
68+
let column_name = self.parse_identifier()?;
69+
column_names.push(column_name);
70+
71+
if self.consume_as(TokenType::Comma).is_err() {
72+
break;
73+
}
74+
}
75+
self.consume_as(TokenType::RightParen)?;
76+
}
77+
78+
Ok(column_names)
79+
}
80+
81+
fn parse_cte_materialized(&mut self) -> Result<Option<MaterializationType>, ParsingError> {
82+
if self.consume_as_keyword(Keyword::Materialized).is_ok() {
83+
Ok(Some(MaterializationType::Materialized))
84+
} else if self.consume_as_keyword(Keyword::Not).is_ok() {
85+
self.consume_as_keyword(Keyword::Materialized)?;
86+
Ok(Some(MaterializationType::NotMaterialized))
87+
} else {
88+
Ok(None)
89+
}
90+
}
91+
}
92+
93+
#[cfg(test)]
94+
pub mod test_utils {
95+
use crate::{CteExpression, Identifier, MaterializationType, SelectStatement};
96+
97+
pub fn cte_expression(
98+
name: Identifier,
99+
column_names: Vec<Identifier>,
100+
materialized: Option<MaterializationType>,
101+
select: SelectStatement,
102+
) -> CteExpression {
103+
CteExpression {
104+
name,
105+
column_names,
106+
materialized,
107+
select,
108+
}
109+
}
110+
}

src/parser/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::iter::Peekable;
22

33
mod alter;
44
mod column_definition;
5+
mod cte;
56
mod drop;
67
mod errors;
78
pub(crate) mod expression;
@@ -15,12 +16,12 @@ mod test_utils;
1516

1617
use crate::{Keyword, SelectStatement, Statement, Token, TokenType, Tokenizer};
1718
use alter::AlterTableStatementParser;
19+
use cte::CteStatementParser;
1820
use drop::DropStatementParser;
1921
pub use errors::*;
2022
use select::{SelectStatementParser, ValuesStatementParser};
2123
use sqlite::SQLite3StatementParser;
2224
use trx::TransactionStatementParser;
23-
2425
/// A parser for SQLite SQL statements
2526
pub struct Parser<'a> {
2627
tokenizer: Peekable<Tokenizer<'a>>,
@@ -240,7 +241,7 @@ impl<'a> Parser<'a> {
240241
}
241242
Keyword::Values => ValuesStatementParser::parse_values_statement(self)
242243
.map(|stmt| Statement::Select(SelectStatement::Values(stmt))),
243-
244+
Keyword::With => CteStatementParser::parse_cte_statement(self).map(Statement::WithCte),
244245
Keyword::Alter => AlterTableStatementParser::parse_alter_table_statement(self)
245246
.map(Statement::AlterTable),
246247
keyword => Err(ParsingError::UnexpectedKeyword(keyword)),

src/parser/select/mod.rs

+83-3
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,9 @@ impl<'a> SelectStatementParser for Parser<'a> {
476476
#[cfg(test)]
477477
mod test_utils {
478478
use crate::{
479-
DistinctType, Expression, Identifier, LimitClause, NamedWindowDefinition, OrderingTerm,
480-
QualifiedTableName, Select, SelectFrom, SelectItem, SelectStatement, UnionStatement,
481-
UnionStatementType,
479+
CteExpression, DistinctType, Expression, Identifier, LimitClause, NamedWindowDefinition,
480+
OrderingTerm, QualifiedTableName, Select, SelectFrom, SelectItem, SelectStatement,
481+
Statement, UnionStatement, UnionStatementType, WithCteStatement,
482482
};
483483

484484
pub fn select_statement_with_columns(
@@ -613,6 +613,28 @@ mod test_utils {
613613
..Default::default()
614614
})
615615
}
616+
617+
pub fn select_statement_with_cte_clause(
618+
recursive: bool,
619+
expressions: Vec<CteExpression>,
620+
) -> Statement {
621+
Statement::WithCte(WithCteStatement {
622+
recursive,
623+
cte_expressions: expressions,
624+
statement: Box::new(Statement::Select(SelectStatement::Select(Select {
625+
distinct_type: DistinctType::None,
626+
columns: vec![SelectItem::Expression(Expression::Identifier(
627+
Identifier::Wildcard,
628+
))],
629+
from: Some(SelectFrom::Table(QualifiedTableName {
630+
table_id: Identifier::Single("table_1".to_string()),
631+
alias: None,
632+
indexed_type: None,
633+
})),
634+
..Default::default()
635+
}))),
636+
})
637+
}
616638
}
617639

618640
#[cfg(test)]
@@ -1826,3 +1848,61 @@ mod test_select_limit_clause {
18261848
);
18271849
}
18281850
}
1851+
1852+
#[cfg(test)]
1853+
mod test_select_cte_clause {
1854+
use super::super::cte::test_utils::cte_expression;
1855+
use super::test_utils::{select_statement_with_cte_clause, select_statement_with_from};
1856+
use crate::parser::test_utils::*;
1857+
use crate::{Identifier, QualifiedTableName, SelectFrom};
1858+
1859+
#[test]
1860+
fn test_select_cte_clause() {
1861+
let expected_statement = select_statement_with_cte_clause(
1862+
true,
1863+
vec![cte_expression(
1864+
Identifier::Single("cte_1".to_string()),
1865+
vec![],
1866+
None,
1867+
select_statement_with_from(SelectFrom::Table(QualifiedTableName::from(
1868+
Identifier::Single("cte_table".to_string()),
1869+
))),
1870+
)],
1871+
);
1872+
1873+
run_sunny_day_test(
1874+
"WITH RECURSIVE cte_1 AS (SELECT * FROM cte_table) SELECT * FROM table_1",
1875+
expected_statement,
1876+
);
1877+
}
1878+
1879+
#[test]
1880+
fn test_select_with_multiple_ctes() {
1881+
let expected_statement = select_statement_with_cte_clause(
1882+
true,
1883+
vec![
1884+
cte_expression(
1885+
Identifier::Single("cte_1".to_string()),
1886+
vec![],
1887+
None,
1888+
select_statement_with_from(SelectFrom::Table(QualifiedTableName::from(
1889+
Identifier::Single("cte_table1".to_string()),
1890+
))),
1891+
),
1892+
cte_expression(
1893+
Identifier::Single("cte_2".to_string()),
1894+
vec![],
1895+
None,
1896+
select_statement_with_from(SelectFrom::Table(QualifiedTableName::from(
1897+
Identifier::Single("cte_table2".to_string()),
1898+
))),
1899+
),
1900+
],
1901+
);
1902+
1903+
run_sunny_day_test(
1904+
"WITH RECURSIVE cte_1 AS (SELECT * FROM cte_table1), cte_2 AS (SELECT * FROM cte_table2) SELECT * FROM table_1",
1905+
expected_statement,
1906+
);
1907+
}
1908+
}

0 commit comments

Comments
 (0)