Skip to content

Commit 4246932

Browse files
committed
sql-parser: parse with CTEs
1 parent 52c7ba1 commit 4246932

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

src/ast/cte.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::fmt::Display;
2+
13
use super::{Identifier, SelectStatement, Statement};
24

35
/// An AST for [WITH](https://www.sqlite.org/lang_with.html) SQL statement.
@@ -24,8 +26,17 @@ pub struct CteExpression {
2426
pub select: SelectStatement,
2527
}
2628

27-
#[derive(Debug, PartialEq)]
29+
#[derive(Debug, PartialEq, Clone)]
2830
pub enum MaterializationType {
2931
Materialized,
3032
NotMaterialized,
3133
}
34+
35+
impl Display for MaterializationType {
36+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37+
match self {
38+
MaterializationType::Materialized => write!(f, "MATERIALIZED"),
39+
MaterializationType::NotMaterialized => write!(f, "NOT MATERIALIZED"),
40+
}
41+
}
42+
}

src/ast/expression.rs

+9
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ pub enum Identifier {
100100
NameWithWildcard(String),
101101
}
102102

103+
impl From<&str> for Identifier {
104+
fn from(s: &str) -> Self {
105+
if s == "*" {
106+
return Identifier::Wildcard;
107+
}
108+
Identifier::Single(s.to_string())
109+
}
110+
}
111+
103112
/// A binary operation
104113
#[derive(Debug, PartialEq, Clone)]
105114
pub enum BinaryOp {

src/parser/cte.rs

+112
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,115 @@ pub mod test_utils {
108108
}
109109
}
110110
}
111+
112+
#[cfg(test)]
113+
pub mod test_cte_statement_parser {
114+
use super::super::select::test_utils::select_star_from;
115+
use super::test_utils::cte_expression;
116+
use crate::{
117+
parser::test_utils::run_sunny_day_test, Identifier, MaterializationType, Statement,
118+
WithCteStatement,
119+
};
120+
121+
#[test]
122+
fn test_cte_statement_with_recursive() {
123+
let expected_statement = Statement::WithCte(WithCteStatement {
124+
recursive: true,
125+
cte_expressions: vec![cte_expression(
126+
Identifier::from("cte_1"),
127+
vec![],
128+
None,
129+
select_star_from(Identifier::from("cte_table")),
130+
)],
131+
statement: Box::new(Statement::Select(select_star_from(Identifier::from(
132+
"table_1",
133+
)))),
134+
});
135+
136+
run_sunny_day_test(
137+
"WITH RECURSIVE cte_1 AS (SELECT * FROM cte_table) SELECT * FROM table_1",
138+
expected_statement,
139+
);
140+
}
141+
142+
#[test]
143+
fn test_cte_statement_with_multiple_ctes() {
144+
let expected_statement = Statement::WithCte(WithCteStatement {
145+
recursive: false,
146+
cte_expressions: vec![
147+
cte_expression(
148+
Identifier::from("cte_1"),
149+
vec![],
150+
None,
151+
select_star_from(Identifier::from("cte_table1")),
152+
),
153+
cte_expression(
154+
Identifier::from("cte_2"),
155+
vec![],
156+
None,
157+
select_star_from(Identifier::from("cte_1")),
158+
),
159+
],
160+
statement: Box::new(Statement::Select(select_star_from(Identifier::from(
161+
"cte_2",
162+
)))),
163+
});
164+
165+
run_sunny_day_test(
166+
"WITH cte_1 AS (SELECT * FROM cte_table1), cte_2 AS (SELECT * FROM cte_1) SELECT * FROM cte_2",
167+
expected_statement,
168+
);
169+
}
170+
171+
#[test]
172+
fn test_cte_statement_with_materialized() {
173+
let materialization_types = vec![
174+
MaterializationType::Materialized,
175+
MaterializationType::NotMaterialized,
176+
];
177+
178+
for materialization_type in materialization_types {
179+
let expected_statement = Statement::WithCte(WithCteStatement {
180+
recursive: false,
181+
cte_expressions: vec![cte_expression(
182+
Identifier::from("cte_1"),
183+
vec![],
184+
Some(materialization_type.clone()),
185+
select_star_from(Identifier::from("cte_table")),
186+
)],
187+
statement: Box::new(Statement::Select(select_star_from(Identifier::from(
188+
"cte_1",
189+
)))),
190+
});
191+
192+
run_sunny_day_test(
193+
&format!(
194+
"WITH cte_1 AS {} (SELECT * FROM cte_table) SELECT * FROM cte_1",
195+
materialization_type
196+
),
197+
expected_statement,
198+
);
199+
}
200+
}
201+
202+
#[test]
203+
fn test_cte_statement_with_column_names() {
204+
let expected_statement = Statement::WithCte(WithCteStatement {
205+
recursive: false,
206+
cte_expressions: vec![cte_expression(
207+
Identifier::from("cte_1"),
208+
vec![Identifier::from("col1"), Identifier::from("col2")],
209+
None,
210+
select_star_from(Identifier::from("cte_table")),
211+
)],
212+
statement: Box::new(Statement::Select(select_star_from(Identifier::from(
213+
"cte_1",
214+
)))),
215+
});
216+
217+
run_sunny_day_test(
218+
"WITH cte_1 (col1, col2) AS (SELECT * FROM cte_table) SELECT * FROM cte_1",
219+
expected_statement,
220+
);
221+
}
222+
}

src/parser/select/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ impl<'a> SelectStatementParser for Parser<'a> {
474474
}
475475

476476
#[cfg(test)]
477-
mod test_utils {
477+
pub mod test_utils {
478478
use crate::{
479479
CteExpression, DistinctType, Expression, Identifier, LimitClause, NamedWindowDefinition,
480480
OrderingTerm, QualifiedTableName, Select, SelectFrom, SelectItem, SelectStatement,
@@ -503,6 +503,10 @@ mod test_utils {
503503
})
504504
}
505505

506+
pub fn select_star_from(table_name: Identifier) -> SelectStatement {
507+
select_statement_with_from(SelectFrom::Table(QualifiedTableName::from(table_name)))
508+
}
509+
506510
pub fn select_statement_with_where_clause(where_clause: Expression) -> SelectStatement {
507511
SelectStatement::Select(Select {
508512
distinct_type: DistinctType::None,

0 commit comments

Comments
 (0)