1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! CTE (Common Table Expression) parsing
use super::super::ast::{CteDefinition, QueryExpr, QueryWithCte, WithClause};
use super::super::lexer::Token;
use super::error::{ParseError, SafeTokenDisplay};
use super::Parser;
impl<'a> Parser<'a> {
/// Parse a complete query with optional WITH clause
pub fn parse_with_cte(&mut self) -> Result<QueryWithCte, ParseError> {
// Check for WITH clause
if self.check(&Token::With) && !self.is_with_filter_context() {
let with_clause = self.parse_with_clause()?;
let query = self.parse_query_expr()?;
// Expect end of input
if !self.check(&Token::Eof) {
return Err(ParseError::new(
// F-05: route the offending token through
// `SafeTokenDisplay` so user-controlled `Ident` /
// `String` / `JsonLiteral` payloads are escaped before
// the message reaches downstream serialization sinks.
format!(
"Unexpected token after query: {}",
SafeTokenDisplay(&self.current.token)
),
self.position(),
));
}
Ok(QueryWithCte::with_ctes(with_clause, query))
} else {
let query = self.parse_query_expr()?;
// Expect end of input
if !self.check(&Token::Eof) {
return Err(ParseError::new(
// F-05: same rationale as the CTE arm above.
format!(
"Unexpected token after query: {}",
SafeTokenDisplay(&self.current.token)
),
self.position(),
));
}
Ok(QueryWithCte::simple(query))
}
}
/// Check if WITH is part of a filter (STARTS WITH, ENDS WITH)
fn is_with_filter_context(&self) -> bool {
// WITH at start of query is CTE, not filter
false
}
/// Parse WITH clause containing one or more CTEs
///
/// Syntax:
/// ```text
/// WITH [RECURSIVE] cte_name [(columns)] AS (query) [, ...]
/// ```
fn parse_with_clause(&mut self) -> Result<WithClause, ParseError> {
self.expect(Token::With)?;
// Check for RECURSIVE keyword
let is_recursive = self.consume(&Token::Recursive)?;
let mut with_clause = WithClause::new();
// Parse CTEs (comma-separated)
loop {
let cte = self.parse_cte_definition(is_recursive)?;
with_clause = with_clause.add(cte);
// Check for more CTEs
if !self.consume(&Token::Comma)? {
break;
}
}
Ok(with_clause)
}
/// Parse a single CTE definition
///
/// Syntax:
/// ```text
/// cte_name [(column1, column2, ...)] AS (query)
/// ```
fn parse_cte_definition(&mut self, is_recursive: bool) -> Result<CteDefinition, ParseError> {
// CTE name
let name = self.expect_ident()?;
// Optional column list
let columns = if self.consume(&Token::LParen)? {
let mut cols = Vec::new();
loop {
cols.push(self.expect_ident()?);
if !self.consume(&Token::Comma)? {
break;
}
}
self.expect(Token::RParen)?;
cols
} else {
Vec::new()
};
// AS keyword
self.expect(Token::As)?;
// CTE query in parentheses
self.expect(Token::LParen)?;
let query = self.parse_cte_query()?;
self.expect(Token::RParen)?;
let mut cte = if is_recursive {
CteDefinition::recursive(&name, query)
} else {
CteDefinition::new(&name, query)
};
if !columns.is_empty() {
cte = cte.with_columns(columns);
}
Ok(cte)
}
/// Parse a query within a CTE (supports UNION ALL for recursive CTEs)
fn parse_cte_query(&mut self) -> Result<QueryExpr, ParseError> {
// Parse the base query
let query = self.parse_query_expr()?;
// For recursive CTEs, we might have UNION ALL
// For now, we just return the base query
// Full UNION ALL support would require a UnionQuery variant
if self.check(&Token::Union) {
// Skip UNION ALL - the recursive part references the CTE by name
// Full implementation would parse both sides
self.advance()?;
if self.consume(&Token::All)? {
// Parse the recursive part
let _recursive_query = self.parse_query_expr()?;
// For now, return just the base - execution handles recursion
}
}
Ok(query)
}
}