sql_cli/sql/parser/expressions/
primary.rs

1// Primary expression parsing
2// Handles literals, identifiers, function calls, and parenthesized expressions
3
4use crate::sql::parser::ast::{SqlExpression, WindowSpec};
5use crate::sql::parser::lexer::Token;
6use tracing::{debug, trace};
7
8use super::{log_parse_decision, trace_parse_entry, trace_parse_exit};
9
10/// Parser context for primary expressions
11pub struct PrimaryExpressionContext<'a> {
12    pub columns: &'a [String],
13    pub in_method_args: bool,
14}
15
16impl<'a> Default for PrimaryExpressionContext<'a> {
17    fn default() -> Self {
18        Self {
19            columns: &[],
20            in_method_args: false,
21        }
22    }
23}
24
25/// Parse a primary expression (literals, identifiers, functions, parentheses)
26/// This is the bottom of the expression hierarchy
27pub fn parse_primary<P>(
28    parser: &mut P,
29    ctx: &PrimaryExpressionContext,
30) -> Result<SqlExpression, String>
31where
32    P: ParsePrimary + ?Sized,
33{
34    trace_parse_entry("parse_primary", parser.current_token());
35
36    // Special case: check if a number literal could actually be a column name
37    // This handles cases where columns are named with pure numbers like "202204"
38    if let Token::NumberLiteral(num_str) = parser.current_token() {
39        if ctx.columns.iter().any(|col| col == num_str) {
40            log_parse_decision(
41                "parse_primary",
42                parser.current_token(),
43                "Number literal matches column name, treating as column",
44            );
45            let expr = SqlExpression::Column(num_str.clone());
46            parser.advance();
47            let result = Ok(expr);
48            trace_parse_exit("parse_primary", &result);
49            return result;
50        }
51    }
52
53    let result = match parser.current_token() {
54        Token::Case => {
55            debug!("Parsing CASE expression");
56            parser.parse_case_expression()
57        }
58
59        Token::DateTime => {
60            debug!("Parsing DateTime constructor");
61            parse_datetime_constructor(parser)
62        }
63
64        Token::Identifier(id) => {
65            let id_upper = id.to_uppercase();
66            let id_clone = id.clone();
67
68            // Check for boolean literals first
69            if id_upper == "TRUE" {
70                log_parse_decision(
71                    "parse_primary",
72                    parser.current_token(),
73                    "Boolean literal TRUE",
74                );
75                parser.advance();
76                Ok(SqlExpression::BooleanLiteral(true))
77            } else if id_upper == "FALSE" {
78                log_parse_decision(
79                    "parse_primary",
80                    parser.current_token(),
81                    "Boolean literal FALSE",
82                );
83                parser.advance();
84                Ok(SqlExpression::BooleanLiteral(false))
85            } else {
86                parser.advance();
87
88                // Check if this is a function call
89                if matches!(parser.current_token(), Token::LeftParen) {
90                    debug!(function = %id_upper, "Parsing function call");
91                    parser.advance(); // consume (
92                    let (args, has_distinct) = parser.parse_function_args()?;
93                    parser.consume(Token::RightParen)?;
94
95                    // Check for OVER clause for window functions
96                    if matches!(parser.current_token(), Token::Over) {
97                        debug!(function = %id_upper, "Window function detected");
98                        parser.advance(); // consume OVER
99                        parser.consume(Token::LeftParen)?;
100                        let window_spec = parser.parse_window_spec()?;
101                        parser.consume(Token::RightParen)?;
102                        Ok(SqlExpression::WindowFunction {
103                            name: id_upper,
104                            args,
105                            window_spec,
106                        })
107                    } else {
108                        Ok(SqlExpression::FunctionCall {
109                            name: id_upper,
110                            args,
111                            distinct: has_distinct,
112                        })
113                    }
114                } else {
115                    // Otherwise treat as column
116                    log_parse_decision(
117                        "parse_primary",
118                        &Token::Identifier(id_clone.clone()),
119                        "Column reference",
120                    );
121                    Ok(SqlExpression::Column(id_clone))
122                }
123            }
124        }
125
126        Token::QuotedIdentifier(id) => {
127            let expr = if ctx.in_method_args {
128                // In method arguments, treat quoted identifiers as string literals
129                log_parse_decision(
130                    "parse_primary",
131                    parser.current_token(),
132                    "Quoted identifier in method args - treating as string",
133                );
134                SqlExpression::StringLiteral(id.clone())
135            } else {
136                // Otherwise it's a column name like "Customer Id"
137                log_parse_decision(
138                    "parse_primary",
139                    parser.current_token(),
140                    "Quoted identifier as column name",
141                );
142                SqlExpression::Column(id.clone())
143            };
144            parser.advance();
145            Ok(expr)
146        }
147
148        Token::StringLiteral(s) => {
149            trace!("String literal: {}", s);
150            let expr = SqlExpression::StringLiteral(s.clone());
151            parser.advance();
152            Ok(expr)
153        }
154
155        Token::NumberLiteral(n) => {
156            trace!("Number literal: {}", n);
157            let expr = SqlExpression::NumberLiteral(n.clone());
158            parser.advance();
159            Ok(expr)
160        }
161
162        Token::Null => {
163            trace!("NULL literal");
164            parser.advance();
165            Ok(SqlExpression::Null)
166        }
167
168        Token::LeftParen => {
169            debug!("Parsing parenthesized expression or subquery");
170            parser.advance(); // consume (
171
172            // Check if this is a subquery (starts with SELECT)
173            if matches!(parser.current_token(), Token::Select) {
174                debug!("Detected subquery - parsing SELECT statement");
175                let subquery = parser.parse_subquery()?;
176                parser.consume(Token::RightParen)?;
177                Ok(SqlExpression::ScalarSubquery {
178                    query: Box::new(subquery),
179                })
180            } else {
181                // Regular parenthesized expression
182                debug!("Regular parenthesized expression");
183                let expr = parser.parse_logical_or()?;
184                parser.consume(Token::RightParen)?;
185                Ok(expr)
186            }
187        }
188
189        Token::Not => {
190            debug!("Parsing NOT expression");
191            parse_not_expression(parser)
192        }
193
194        Token::Star => {
195            // Handle * as a literal (like in COUNT(*))
196            trace!("Star token as literal");
197            parser.advance();
198            Ok(SqlExpression::StringLiteral("*".to_string()))
199        }
200
201        _ => {
202            let err = format!(
203                "Unexpected token in primary expression: {:?}",
204                parser.current_token()
205            );
206            debug!(error = %err);
207            Err(err)
208        }
209    };
210
211    trace_parse_exit("parse_primary", &result);
212    result
213}
214
215/// Parse DateTime constructor
216fn parse_datetime_constructor<P>(parser: &mut P) -> Result<SqlExpression, String>
217where
218    P: ParsePrimary + ?Sized,
219{
220    parser.advance(); // consume DateTime
221    parser.consume(Token::LeftParen)?;
222
223    // Check if empty parentheses for DateTime() - today's date
224    if matches!(parser.current_token(), Token::RightParen) {
225        parser.advance(); // consume )
226        debug!("DateTime() - today's date");
227        return Ok(SqlExpression::DateTimeToday {
228            hour: None,
229            minute: None,
230            second: None,
231        });
232    }
233
234    // Parse year
235    let year = if let Token::NumberLiteral(n) = parser.current_token() {
236        n.parse::<i32>().map_err(|_| "Invalid year")?
237    } else {
238        return Err("Expected year in DateTime constructor".to_string());
239    };
240    parser.advance();
241    parser.consume(Token::Comma)?;
242
243    // Parse month
244    let month = if let Token::NumberLiteral(n) = parser.current_token() {
245        n.parse::<u32>().map_err(|_| "Invalid month")?
246    } else {
247        return Err("Expected month in DateTime constructor".to_string());
248    };
249    parser.advance();
250    parser.consume(Token::Comma)?;
251
252    // Parse day
253    let day = if let Token::NumberLiteral(n) = parser.current_token() {
254        n.parse::<u32>().map_err(|_| "Invalid day")?
255    } else {
256        return Err("Expected day in DateTime constructor".to_string());
257    };
258    parser.advance();
259
260    // Check for optional time components
261    let mut hour = None;
262    let mut minute = None;
263    let mut second = None;
264
265    if matches!(parser.current_token(), Token::Comma) {
266        parser.advance(); // consume comma
267
268        // Parse hour
269        if let Token::NumberLiteral(n) = parser.current_token() {
270            hour = Some(n.parse::<u32>().map_err(|_| "Invalid hour")?);
271            parser.advance();
272
273            // Check for minute
274            if matches!(parser.current_token(), Token::Comma) {
275                parser.advance(); // consume comma
276
277                if let Token::NumberLiteral(n) = parser.current_token() {
278                    minute = Some(n.parse::<u32>().map_err(|_| "Invalid minute")?);
279                    parser.advance();
280
281                    // Check for second
282                    if matches!(parser.current_token(), Token::Comma) {
283                        parser.advance(); // consume comma
284
285                        if let Token::NumberLiteral(n) = parser.current_token() {
286                            second = Some(n.parse::<u32>().map_err(|_| "Invalid second")?);
287                            parser.advance();
288                        }
289                    }
290                }
291            }
292        }
293    }
294
295    parser.consume(Token::RightParen)?;
296
297    debug!(year = year, month = month, day = day, hour = ?hour, minute = ?minute, second = ?second, "DateTime constructor parsed");
298
299    Ok(SqlExpression::DateTimeConstructor {
300        year,
301        month,
302        day,
303        hour,
304        minute,
305        second,
306    })
307}
308
309/// Parse NOT expression
310fn parse_not_expression<P>(parser: &mut P) -> Result<SqlExpression, String>
311where
312    P: ParsePrimary + ?Sized,
313{
314    parser.advance(); // consume NOT
315
316    // Check if this is a NOT IN expression
317    if let Ok(inner_expr) = parser.parse_comparison() {
318        // After parsing the inner expression, check if we're followed by IN
319        if matches!(parser.current_token(), Token::In) {
320            debug!("NOT IN expression detected");
321            parser.advance(); // consume IN
322            parser.consume(Token::LeftParen)?;
323            let values = parser.parse_expression_list()?;
324            parser.consume(Token::RightParen)?;
325
326            Ok(SqlExpression::NotInList {
327                expr: Box::new(inner_expr),
328                values,
329            })
330        } else {
331            // Regular NOT expression
332            debug!("Regular NOT expression");
333            Ok(SqlExpression::Not {
334                expr: Box::new(inner_expr),
335            })
336        }
337    } else {
338        Err("Expected expression after NOT".to_string())
339    }
340}
341
342/// Trait that parsers must implement to use primary expression parsing
343pub trait ParsePrimary {
344    fn current_token(&self) -> &Token;
345    fn advance(&mut self);
346    fn consume(&mut self, expected: Token) -> Result<(), String>;
347
348    // These methods are called from parse_primary
349    fn parse_case_expression(&mut self) -> Result<SqlExpression, String>;
350    fn parse_function_args(&mut self) -> Result<(Vec<SqlExpression>, bool), String>;
351    fn parse_window_spec(&mut self) -> Result<WindowSpec, String>;
352    fn parse_logical_or(&mut self) -> Result<SqlExpression, String>;
353    fn parse_comparison(&mut self) -> Result<SqlExpression, String>;
354    fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
355
356    // For subquery parsing (without parenthesis balance validation)
357    fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
358}