alopex_sql/parser/
dml.rs

1use crate::ast::span::Spanned;
2use crate::ast::{
3    Assignment, Delete, Insert, LITERAL_TABLE, OrderByExpr, Select, SelectItem, TableRef, Update,
4};
5use crate::error::{ParserError, Result};
6use crate::tokenizer::keyword::Keyword;
7use crate::tokenizer::token::{Token, Word};
8
9use super::Parser;
10
11impl<'a> Parser<'a> {
12    pub fn parse_select(&mut self) -> Result<Select> {
13        let start_span = self.expect_keyword("SELECT", Keyword::SELECT)?;
14        let distinct = self.consume_keyword(Keyword::DISTINCT);
15
16        let projection = self.parse_projection_list()?;
17        let mut end_span = projection
18            .last()
19            .map(|item| item.span())
20            .unwrap_or(start_span);
21
22        let from = if self.consume_keyword(Keyword::FROM) {
23            let from = self.parse_table_ref()?;
24            end_span = from.span;
25            from
26        } else {
27            validate_literal_projection(&projection)?;
28            TableRef {
29                name: LITERAL_TABLE.to_string(),
30                alias: None,
31                span: end_span,
32            }
33        };
34
35        let selection = if self.consume_keyword(Keyword::WHERE) {
36            let expr = self.parse_expr()?;
37            end_span = end_span.union(&expr.span());
38            Some(expr)
39        } else {
40            None
41        };
42
43        let order_by = if self.consume_keyword(Keyword::ORDER) {
44            self.expect_keyword("BY", Keyword::BY)?;
45            let items = self.parse_order_by()?;
46            if let Some(last) = items.last() {
47                end_span = end_span.union(&last.span);
48            }
49            items
50        } else {
51            Vec::new()
52        };
53
54        let mut limit = None;
55        let mut offset = None;
56        if self.consume_keyword(Keyword::LIMIT) {
57            let lim = self.parse_expr()?;
58            end_span = end_span.union(&lim.span());
59            limit = Some(lim);
60
61            if self.consume_keyword(Keyword::OFFSET) {
62                let off = self.parse_expr()?;
63                end_span = end_span.union(&off.span());
64                offset = Some(off);
65            }
66        }
67
68        let span = start_span.union(&end_span);
69        Ok(Select {
70            distinct,
71            projection,
72            from,
73            selection,
74            order_by,
75            limit,
76            offset,
77            span,
78        })
79    }
80
81    fn parse_projection_list(&mut self) -> Result<Vec<SelectItem>> {
82        let mut items = Vec::new();
83        loop {
84            items.push(self.parse_select_item()?);
85            if matches!(self.peek().token, Token::Comma) {
86                self.advance();
87                continue;
88            }
89            break;
90        }
91        Ok(items)
92    }
93
94    fn parse_select_item(&mut self) -> Result<SelectItem> {
95        let tok = self.peek().clone();
96        if matches!(tok.token, Token::Mul) {
97            self.advance();
98            return Ok(SelectItem::Wildcard { span: tok.span });
99        }
100
101        let expr = self.parse_expr()?;
102        let mut span = expr.span();
103        let mut alias = None;
104
105        if self.consume_keyword(Keyword::AS) {
106            let (name, alias_span) = self.parse_identifier()?;
107            span = span.union(&alias_span);
108            alias = Some(name);
109        } else if let Token::Word(Word {
110            keyword: Keyword::NoKeyword,
111            ..
112        }) = &self.peek().token
113        {
114            let alias_tok = self.advance();
115            if let Token::Word(Word { value, .. }) = alias_tok.token {
116                span = span.union(&alias_tok.span);
117                alias = Some(value);
118            }
119        }
120
121        Ok(SelectItem::Expr { expr, alias, span })
122    }
123
124    fn parse_table_ref(&mut self) -> Result<TableRef> {
125        let (name, name_span) = self.parse_identifier()?;
126        let mut alias = None;
127        let mut span = name_span;
128
129        if self.consume_keyword(Keyword::AS) {
130            let (a, alias_span) = self.parse_identifier()?;
131            alias = Some(a);
132            span = span.union(&alias_span);
133        } else if let Token::Word(Word {
134            keyword: Keyword::NoKeyword,
135            ..
136        }) = &self.peek().token
137        {
138            let alias_tok = self.advance();
139            if let Token::Word(Word { value, .. }) = alias_tok.token {
140                span = span.union(&alias_tok.span);
141                alias = Some(value);
142            }
143        }
144
145        Ok(TableRef { name, alias, span })
146    }
147
148    fn parse_order_by(&mut self) -> Result<Vec<OrderByExpr>> {
149        let mut items = Vec::new();
150        loop {
151            let expr = self.parse_expr()?;
152            let mut span = expr.span();
153            let mut asc = None;
154            let mut nulls_first = None;
155
156            if let Token::Word(Word { keyword, .. }) = &self.peek().token {
157                match keyword {
158                    Keyword::ASC => {
159                        let s = self.advance().span;
160                        span = span.union(&s);
161                        asc = Some(true);
162                    }
163                    Keyword::DESC => {
164                        let s = self.advance().span;
165                        span = span.union(&s);
166                        asc = Some(false);
167                    }
168                    _ => {}
169                }
170            }
171
172            if let Token::Word(Word {
173                keyword: Keyword::NULLS,
174                ..
175            }) = &self.peek().token
176            {
177                let nulls_tok = self.advance();
178                let dir_tok = self.expect_token("FIRST or LAST", |t| {
179                    matches!(
180                        t,
181                        Token::Word(Word {
182                            keyword: Keyword::FIRST | Keyword::LAST,
183                            ..
184                        })
185                    )
186                })?;
187                nulls_first = Some(matches!(
188                    dir_tok.token,
189                    Token::Word(Word {
190                        keyword: Keyword::FIRST,
191                        ..
192                    })
193                ));
194                span = span.union(&nulls_tok.span).union(&dir_tok.span);
195            }
196
197            items.push(OrderByExpr {
198                expr,
199                asc,
200                nulls_first,
201                span,
202            });
203
204            if matches!(self.peek().token, Token::Comma) {
205                self.advance();
206                continue;
207            }
208            break;
209        }
210
211        Ok(items)
212    }
213
214    pub fn parse_insert(&mut self) -> Result<Insert> {
215        let start_span = self.expect_keyword("INSERT", Keyword::INSERT)?;
216        self.expect_keyword("INTO", Keyword::INTO)?;
217        let (table, table_span) = self.parse_identifier()?;
218        let mut end_span = table_span;
219        let mut columns = None;
220
221        if matches!(self.peek().token, Token::LParen) {
222            self.advance();
223            let mut cols = Vec::new();
224            loop {
225                let (col, col_span) = self.parse_identifier()?;
226                end_span = end_span.union(&col_span);
227                cols.push(col);
228                if matches!(self.peek().token, Token::Comma) {
229                    self.advance();
230                    continue;
231                }
232                break;
233            }
234            let close = self
235                .expect_token("')'", |t| matches!(t, Token::RParen))?
236                .span;
237            end_span = end_span.union(&close);
238            columns = Some(cols);
239        }
240
241        self.expect_keyword("VALUES", Keyword::VALUES)?;
242        let mut values = Vec::new();
243        loop {
244            self.expect_token("'('", |t| matches!(t, Token::LParen))?;
245            let mut row = Vec::new();
246            row.push(self.parse_expr()?);
247            while matches!(self.peek().token, Token::Comma) {
248                self.advance();
249                row.push(self.parse_expr()?);
250            }
251            let row_end = self
252                .expect_token("')'", |t| matches!(t, Token::RParen))?
253                .span;
254            end_span = end_span.union(&row_end);
255            values.push(row);
256
257            if matches!(self.peek().token, Token::Comma) {
258                self.advance();
259                continue;
260            }
261            break;
262        }
263
264        let span = start_span.union(&end_span);
265        Ok(Insert {
266            table,
267            columns,
268            values,
269            span,
270        })
271    }
272
273    pub fn parse_update(&mut self) -> Result<Update> {
274        let start_span = self.expect_keyword("UPDATE", Keyword::UPDATE)?;
275        let (table, table_span) = self.parse_identifier()?;
276        self.expect_keyword("SET", Keyword::SET)?;
277
278        let mut assignments = Vec::new();
279        loop {
280            let (column, col_span) = self.parse_identifier()?;
281            self.expect_token("'='", |t| matches!(t, Token::Eq))?;
282            let value = self.parse_expr()?;
283            let span = col_span.union(&value.span());
284            assignments.push(Assignment {
285                column,
286                value,
287                span,
288            });
289
290            if matches!(self.peek().token, Token::Comma) {
291                self.advance();
292                continue;
293            }
294            break;
295        }
296
297        let mut end_span = assignments.last().map(|a| a.span).unwrap_or(table_span);
298
299        let selection = if self.consume_keyword(Keyword::WHERE) {
300            let expr = self.parse_expr()?;
301            end_span = end_span.union(&expr.span());
302            Some(expr)
303        } else {
304            None
305        };
306
307        let span = start_span.union(&end_span);
308        Ok(Update {
309            table,
310            assignments,
311            selection,
312            span,
313        })
314    }
315
316    pub fn parse_delete(&mut self) -> Result<Delete> {
317        let start_span = self.expect_keyword("DELETE", Keyword::DELETE)?;
318        self.expect_keyword("FROM", Keyword::FROM)?;
319        let (table, table_span) = self.parse_identifier()?;
320        let mut end_span = table_span;
321
322        let selection = if self.consume_keyword(Keyword::WHERE) {
323            let expr = self.parse_expr()?;
324            end_span = end_span.union(&expr.span());
325            Some(expr)
326        } else {
327            None
328        };
329
330        let span = start_span.union(&end_span);
331        Ok(Delete {
332            table,
333            selection,
334            span,
335        })
336    }
337}
338
339fn validate_literal_projection(items: &[SelectItem]) -> Result<()> {
340    for item in items {
341        match item {
342            SelectItem::Wildcard { span } => {
343                return Err(ParserError::UnexpectedToken {
344                    line: span.start.line,
345                    column: span.start.column,
346                    expected: "literal expression".to_string(),
347                    found: "*".to_string(),
348                });
349            }
350            SelectItem::Expr { expr, .. } => {
351                if expr_contains_column_ref(expr) {
352                    return Err(ParserError::UnexpectedToken {
353                        line: expr.span.start.line,
354                        column: expr.span.start.column,
355                        expected: "literal expression".to_string(),
356                        found: "column reference".to_string(),
357                    });
358                }
359            }
360        }
361    }
362    Ok(())
363}
364
365fn expr_contains_column_ref(expr: &crate::ast::Expr) -> bool {
366    use crate::ast::expr::ExprKind;
367    match &expr.kind {
368        ExprKind::ColumnRef { .. } => true,
369        ExprKind::BinaryOp { left, right, .. } => {
370            expr_contains_column_ref(left) || expr_contains_column_ref(right)
371        }
372        ExprKind::UnaryOp { operand, .. } => expr_contains_column_ref(operand),
373        ExprKind::FunctionCall { args, .. } => args.iter().any(expr_contains_column_ref),
374        ExprKind::Between {
375            expr, low, high, ..
376        } => {
377            expr_contains_column_ref(expr)
378                || expr_contains_column_ref(low)
379                || expr_contains_column_ref(high)
380        }
381        ExprKind::Like {
382            expr,
383            pattern,
384            escape,
385            ..
386        } => {
387            expr_contains_column_ref(expr)
388                || expr_contains_column_ref(pattern)
389                || escape
390                    .as_ref()
391                    .is_some_and(|expr| expr_contains_column_ref(expr))
392        }
393        ExprKind::InList { expr, list, .. } => {
394            expr_contains_column_ref(expr) || list.iter().any(expr_contains_column_ref)
395        }
396        ExprKind::IsNull { expr, .. } => expr_contains_column_ref(expr),
397        ExprKind::Literal(_) | ExprKind::VectorLiteral(_) => false,
398    }
399}