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        // Handle LEFT and RIGHT as function names when followed by parentheses
169        Token::Left | Token::Right => {
170            let func_name = match parser.current_token() {
171                Token::Left => "LEFT".to_string(),
172                Token::Right => "RIGHT".to_string(),
173                _ => unreachable!(),
174            };
175
176            parser.advance();
177
178            // Check if this is a function call
179            if matches!(parser.current_token(), Token::LeftParen) {
180                debug!(function = %func_name, "Parsing LEFT/RIGHT function call");
181                parser.advance(); // consume (
182                let (args, _has_distinct) = parser.parse_function_args()?;
183                parser.consume(Token::RightParen)?;
184
185                Ok(SqlExpression::FunctionCall {
186                    name: func_name,
187                    args,
188                    distinct: false,
189                })
190            } else {
191                // If not followed by parenthesis, this is likely a JOIN keyword - error
192                Err(format!(
193                    "{} keyword unexpected in expression context",
194                    func_name
195                ))
196            }
197        }
198
199        Token::LeftParen => {
200            debug!("Parsing parenthesized expression or subquery");
201            parser.advance(); // consume (
202
203            // Check if this is a subquery (starts with SELECT)
204            if matches!(parser.current_token(), Token::Select) {
205                debug!("Detected subquery - parsing SELECT statement");
206                let subquery = parser.parse_subquery()?;
207                parser.consume(Token::RightParen)?;
208                Ok(SqlExpression::ScalarSubquery {
209                    query: Box::new(subquery),
210                })
211            } else {
212                // Regular parenthesized expression
213                debug!("Regular parenthesized expression");
214                let expr = parser.parse_logical_or()?;
215                parser.consume(Token::RightParen)?;
216                Ok(expr)
217            }
218        }
219
220        Token::Not => {
221            debug!("Parsing NOT expression");
222            parse_not_expression(parser)
223        }
224
225        Token::Star => {
226            // Handle * as a literal (like in COUNT(*))
227            trace!("Star token as literal");
228            parser.advance();
229            Ok(SqlExpression::StringLiteral("*".to_string()))
230        }
231
232        _ => {
233            let err = format!(
234                "Unexpected token in primary expression: {:?}",
235                parser.current_token()
236            );
237            debug!(error = %err);
238            Err(err)
239        }
240    };
241
242    trace_parse_exit("parse_primary", &result);
243    result
244}
245
246/// Parse DateTime constructor
247fn parse_datetime_constructor<P>(parser: &mut P) -> Result<SqlExpression, String>
248where
249    P: ParsePrimary + ?Sized,
250{
251    parser.advance(); // consume DateTime
252    parser.consume(Token::LeftParen)?;
253
254    // Check if empty parentheses for DateTime() - today's date
255    if matches!(parser.current_token(), Token::RightParen) {
256        parser.advance(); // consume )
257        debug!("DateTime() - today's date");
258        return Ok(SqlExpression::DateTimeToday {
259            hour: None,
260            minute: None,
261            second: None,
262        });
263    }
264
265    // Parse year
266    let year = if let Token::NumberLiteral(n) = parser.current_token() {
267        n.parse::<i32>().map_err(|_| "Invalid year")?
268    } else {
269        return Err("Expected year in DateTime constructor".to_string());
270    };
271    parser.advance();
272    parser.consume(Token::Comma)?;
273
274    // Parse month
275    let month = if let Token::NumberLiteral(n) = parser.current_token() {
276        n.parse::<u32>().map_err(|_| "Invalid month")?
277    } else {
278        return Err("Expected month in DateTime constructor".to_string());
279    };
280    parser.advance();
281    parser.consume(Token::Comma)?;
282
283    // Parse day
284    let day = if let Token::NumberLiteral(n) = parser.current_token() {
285        n.parse::<u32>().map_err(|_| "Invalid day")?
286    } else {
287        return Err("Expected day in DateTime constructor".to_string());
288    };
289    parser.advance();
290
291    // Check for optional time components
292    let mut hour = None;
293    let mut minute = None;
294    let mut second = None;
295
296    if matches!(parser.current_token(), Token::Comma) {
297        parser.advance(); // consume comma
298
299        // Parse hour
300        if let Token::NumberLiteral(n) = parser.current_token() {
301            hour = Some(n.parse::<u32>().map_err(|_| "Invalid hour")?);
302            parser.advance();
303
304            // Check for minute
305            if matches!(parser.current_token(), Token::Comma) {
306                parser.advance(); // consume comma
307
308                if let Token::NumberLiteral(n) = parser.current_token() {
309                    minute = Some(n.parse::<u32>().map_err(|_| "Invalid minute")?);
310                    parser.advance();
311
312                    // Check for second
313                    if matches!(parser.current_token(), Token::Comma) {
314                        parser.advance(); // consume comma
315
316                        if let Token::NumberLiteral(n) = parser.current_token() {
317                            second = Some(n.parse::<u32>().map_err(|_| "Invalid second")?);
318                            parser.advance();
319                        }
320                    }
321                }
322            }
323        }
324    }
325
326    parser.consume(Token::RightParen)?;
327
328    debug!(year = year, month = month, day = day, hour = ?hour, minute = ?minute, second = ?second, "DateTime constructor parsed");
329
330    Ok(SqlExpression::DateTimeConstructor {
331        year,
332        month,
333        day,
334        hour,
335        minute,
336        second,
337    })
338}
339
340/// Parse NOT expression
341fn parse_not_expression<P>(parser: &mut P) -> Result<SqlExpression, String>
342where
343    P: ParsePrimary + ?Sized,
344{
345    parser.advance(); // consume NOT
346
347    // Check if this is a NOT IN expression
348    if let Ok(inner_expr) = parser.parse_comparison() {
349        // After parsing the inner expression, check if we're followed by IN
350        if matches!(parser.current_token(), Token::In) {
351            debug!("NOT IN expression detected");
352            parser.advance(); // consume IN
353            parser.consume(Token::LeftParen)?;
354            let values = parser.parse_expression_list()?;
355            parser.consume(Token::RightParen)?;
356
357            Ok(SqlExpression::NotInList {
358                expr: Box::new(inner_expr),
359                values,
360            })
361        } else {
362            // Regular NOT expression
363            debug!("Regular NOT expression");
364            Ok(SqlExpression::Not {
365                expr: Box::new(inner_expr),
366            })
367        }
368    } else {
369        Err("Expected expression after NOT".to_string())
370    }
371}
372
373/// Trait that parsers must implement to use primary expression parsing
374pub trait ParsePrimary {
375    fn current_token(&self) -> &Token;
376    fn advance(&mut self);
377    fn consume(&mut self, expected: Token) -> Result<(), String>;
378
379    // These methods are called from parse_primary
380    fn parse_case_expression(&mut self) -> Result<SqlExpression, String>;
381    fn parse_function_args(&mut self) -> Result<(Vec<SqlExpression>, bool), String>;
382    fn parse_window_spec(&mut self) -> Result<WindowSpec, String>;
383    fn parse_logical_or(&mut self) -> Result<SqlExpression, String>;
384    fn parse_comparison(&mut self) -> Result<SqlExpression, String>;
385    fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
386
387    // For subquery parsing (without parenthesis balance validation)
388    fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
389}