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::{ColumnRef, SqlExpression, WindowSpec};
5use crate::sql::parser::lexer::Token;
6use tracing::{debug, trace};
7
8use super::{log_parse_decision, trace_parse_entry, trace_parse_exit, ExpressionParser};
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 + ExpressionParser + ?Sized,
33{
34    trace_parse_entry("parse_primary", ExpressionParser::current_token(parser));
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) = ExpressionParser::current_token(parser) {
39        if ctx.columns.iter().any(|col| col == num_str) {
40            log_parse_decision(
41                "parse_primary",
42                ExpressionParser::current_token(parser),
43                "Number literal matches column name, treating as column",
44            );
45            let expr = SqlExpression::Column(ColumnRef::unquoted(num_str.clone()));
46            ExpressionParser::advance(parser);
47            let result = Ok(expr);
48            trace_parse_exit("parse_primary", &result);
49            return result;
50        }
51    }
52
53    let result = match ExpressionParser::current_token(parser) {
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                    ExpressionParser::current_token(parser),
73                    "Boolean literal TRUE",
74                );
75                ExpressionParser::advance(parser);
76                Ok(SqlExpression::BooleanLiteral(true))
77            } else if id_upper == "FALSE" {
78                log_parse_decision(
79                    "parse_primary",
80                    ExpressionParser::current_token(parser),
81                    "Boolean literal FALSE",
82                );
83                ExpressionParser::advance(parser);
84                Ok(SqlExpression::BooleanLiteral(false))
85            } else {
86                ExpressionParser::advance(parser);
87
88                // Check for table.column notation or method calls
89                if matches!(ExpressionParser::current_token(parser), Token::Dot) {
90                    ExpressionParser::advance(parser); // consume dot
91
92                    if let Token::Identifier(next_id) = ExpressionParser::current_token(parser) {
93                        let next_id = next_id.clone();
94                        ExpressionParser::advance(parser);
95
96                        // Check if this is a method call (followed by parentheses)
97                        if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
98                            debug!(object = %id_clone, method = %next_id, "Parsing method call");
99                            ExpressionParser::advance(parser); // consume (
100
101                            // Handle empty argument list
102                            let args = if matches!(
103                                ExpressionParser::current_token(parser),
104                                Token::RightParen
105                            ) {
106                                Vec::new()
107                            } else {
108                                parser.parse_expression_list()?
109                            };
110                            ExpressionParser::consume(parser, Token::RightParen)?;
111
112                            log_parse_decision(
113                                "parse_primary",
114                                &Token::Identifier(next_id.clone()),
115                                "Method call",
116                            );
117                            Ok(SqlExpression::MethodCall {
118                                object: id_clone,
119                                method: next_id,
120                                args,
121                            })
122                        } else {
123                            // It's a qualified column reference
124                            let col_ref = ColumnRef::qualified(id_clone, next_id.clone());
125                            log_parse_decision(
126                                "parse_primary",
127                                &Token::Identifier(next_id),
128                                "Qualified column reference",
129                            );
130                            Ok(SqlExpression::Column(col_ref))
131                        }
132                    } else {
133                        Err("Expected identifier after '.'".to_string())
134                    }
135                // Check if this is a function call
136                } else if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
137                    debug!(function = %id_upper, "Parsing function call");
138                    ExpressionParser::advance(parser); // consume (
139                    let (args, has_distinct) = parser.parse_function_args()?;
140                    ExpressionParser::consume(parser, Token::RightParen)?;
141
142                    // Check for OVER clause for window functions
143                    if matches!(ExpressionParser::current_token(parser), Token::Over) {
144                        debug!(function = %id_upper, "Window function detected");
145                        ExpressionParser::advance(parser); // consume OVER
146                        ExpressionParser::consume(parser, Token::LeftParen)?;
147                        let window_spec = parser.parse_window_spec()?;
148                        ExpressionParser::consume(parser, Token::RightParen)?;
149                        Ok(SqlExpression::WindowFunction {
150                            name: id_upper,
151                            args,
152                            window_spec,
153                        })
154                    } else {
155                        Ok(SqlExpression::FunctionCall {
156                            name: id_upper,
157                            args,
158                            distinct: has_distinct,
159                        })
160                    }
161                } else {
162                    // Otherwise treat as simple column
163                    log_parse_decision(
164                        "parse_primary",
165                        &Token::Identifier(id_clone.clone()),
166                        "Column reference",
167                    );
168                    Ok(SqlExpression::Column(ColumnRef::unquoted(id_clone)))
169                }
170            }
171        }
172
173        Token::QuotedIdentifier(id) => {
174            let expr = if ctx.in_method_args {
175                // In method arguments, treat quoted identifiers as string literals
176                log_parse_decision(
177                    "parse_primary",
178                    ExpressionParser::current_token(parser),
179                    "Quoted identifier in method args - treating as string",
180                );
181                SqlExpression::StringLiteral(id.clone())
182            } else {
183                // Otherwise it's a column name like "Customer Id"
184                log_parse_decision(
185                    "parse_primary",
186                    ExpressionParser::current_token(parser),
187                    "Quoted identifier as column name",
188                );
189                SqlExpression::Column(ColumnRef::quoted(id.clone()))
190            };
191            ExpressionParser::advance(parser);
192            Ok(expr)
193        }
194
195        Token::StringLiteral(s) => {
196            trace!("String literal: {}", s);
197            let expr = SqlExpression::StringLiteral(s.clone());
198            ExpressionParser::advance(parser);
199            Ok(expr)
200        }
201
202        Token::NumberLiteral(n) => {
203            trace!("Number literal: {}", n);
204            let expr = SqlExpression::NumberLiteral(n.clone());
205            ExpressionParser::advance(parser);
206            Ok(expr)
207        }
208
209        Token::Null => {
210            trace!("NULL literal");
211            ExpressionParser::advance(parser);
212            Ok(SqlExpression::Null)
213        }
214
215        // Handle LEFT and RIGHT as function names when followed by parentheses
216        Token::Left | Token::Right => {
217            let func_name = match ExpressionParser::current_token(parser) {
218                Token::Left => "LEFT".to_string(),
219                Token::Right => "RIGHT".to_string(),
220                _ => unreachable!(),
221            };
222
223            ExpressionParser::advance(parser);
224
225            // Check if this is a function call
226            if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
227                debug!(function = %func_name, "Parsing LEFT/RIGHT function call");
228                ExpressionParser::advance(parser); // consume (
229                let (args, _has_distinct) = parser.parse_function_args()?;
230                ExpressionParser::consume(parser, Token::RightParen)?;
231
232                Ok(SqlExpression::FunctionCall {
233                    name: func_name,
234                    args,
235                    distinct: false,
236                })
237            } else {
238                // If not followed by parenthesis, this is likely a JOIN keyword - error
239                Err(format!(
240                    "{} keyword unexpected in expression context",
241                    func_name
242                ))
243            }
244        }
245
246        Token::LeftParen => {
247            debug!("Parsing parenthesized expression or subquery");
248            ExpressionParser::advance(parser); // consume (
249
250            // Check if this is a subquery (starts with SELECT)
251            if matches!(ExpressionParser::current_token(parser), Token::Select) {
252                debug!("Detected subquery - parsing SELECT statement");
253                let subquery = parser.parse_subquery()?;
254                ExpressionParser::consume(parser, Token::RightParen)?;
255                Ok(SqlExpression::ScalarSubquery {
256                    query: Box::new(subquery),
257                })
258            } else {
259                // Regular parenthesized expression
260                debug!("Regular parenthesized expression");
261                let expr = parser.parse_logical_or()?;
262                ExpressionParser::consume(parser, Token::RightParen)?;
263                Ok(expr)
264            }
265        }
266
267        Token::Not => {
268            debug!("Parsing NOT expression");
269            parse_not_expression(parser)
270        }
271
272        Token::Star => {
273            // Handle * as a literal (like in COUNT(*))
274            trace!("Star token as literal");
275            ExpressionParser::advance(parser);
276            Ok(SqlExpression::StringLiteral("*".to_string()))
277        }
278
279        // Handle window-related keywords that can also be column names
280        Token::Row => {
281            trace!("ROW token treated as identifier in expression context");
282            ExpressionParser::advance(parser);
283            Ok(SqlExpression::Column(ColumnRef::unquoted(
284                "row".to_string(),
285            )))
286        }
287
288        Token::Rows => {
289            trace!("ROWS token treated as identifier in expression context");
290            ExpressionParser::advance(parser);
291            Ok(SqlExpression::Column(ColumnRef::unquoted(
292                "rows".to_string(),
293            )))
294        }
295
296        Token::Range => {
297            trace!("RANGE token treated as identifier in expression context");
298            ExpressionParser::advance(parser);
299            Ok(SqlExpression::Column(ColumnRef::unquoted(
300                "range".to_string(),
301            )))
302        }
303
304        _ => {
305            let err = format!(
306                "Unexpected token in primary expression: {:?}",
307                ExpressionParser::current_token(parser)
308            );
309            debug!(error = %err);
310            Err(err)
311        }
312    };
313
314    trace_parse_exit("parse_primary", &result);
315    result
316}
317
318/// Parse DateTime constructor
319fn parse_datetime_constructor<P>(parser: &mut P) -> Result<SqlExpression, String>
320where
321    P: ParsePrimary + ExpressionParser + ?Sized,
322{
323    ExpressionParser::advance(parser); // consume DateTime
324    ExpressionParser::consume(parser, Token::LeftParen)?;
325
326    // Check if empty parentheses for DateTime() - today's date
327    if matches!(ExpressionParser::current_token(parser), Token::RightParen) {
328        ExpressionParser::advance(parser); // consume )
329        debug!("DateTime() - today's date");
330        return Ok(SqlExpression::DateTimeToday {
331            hour: None,
332            minute: None,
333            second: None,
334        });
335    }
336
337    // Parse year
338    let year = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
339        n.parse::<i32>().map_err(|_| "Invalid year")?
340    } else {
341        return Err("Expected year in DateTime constructor".to_string());
342    };
343    ExpressionParser::advance(parser);
344    ExpressionParser::consume(parser, Token::Comma)?;
345
346    // Parse month
347    let month = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
348        n.parse::<u32>().map_err(|_| "Invalid month")?
349    } else {
350        return Err("Expected month in DateTime constructor".to_string());
351    };
352    ExpressionParser::advance(parser);
353    ExpressionParser::consume(parser, Token::Comma)?;
354
355    // Parse day
356    let day = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
357        n.parse::<u32>().map_err(|_| "Invalid day")?
358    } else {
359        return Err("Expected day in DateTime constructor".to_string());
360    };
361    ExpressionParser::advance(parser);
362
363    // Check for optional time components
364    let mut hour = None;
365    let mut minute = None;
366    let mut second = None;
367
368    if matches!(ExpressionParser::current_token(parser), Token::Comma) {
369        ExpressionParser::advance(parser); // consume comma
370
371        // Parse hour
372        if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
373            hour = Some(n.parse::<u32>().map_err(|_| "Invalid hour")?);
374            ExpressionParser::advance(parser);
375
376            // Check for minute
377            if matches!(ExpressionParser::current_token(parser), Token::Comma) {
378                ExpressionParser::advance(parser); // consume comma
379
380                if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
381                    minute = Some(n.parse::<u32>().map_err(|_| "Invalid minute")?);
382                    ExpressionParser::advance(parser);
383
384                    // Check for second
385                    if matches!(ExpressionParser::current_token(parser), Token::Comma) {
386                        ExpressionParser::advance(parser); // consume comma
387
388                        if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
389                            second = Some(n.parse::<u32>().map_err(|_| "Invalid second")?);
390                            ExpressionParser::advance(parser);
391                        }
392                    }
393                }
394            }
395        }
396    }
397
398    ExpressionParser::consume(parser, Token::RightParen)?;
399
400    debug!(year = year, month = month, day = day, hour = ?hour, minute = ?minute, second = ?second, "DateTime constructor parsed");
401
402    Ok(SqlExpression::DateTimeConstructor {
403        year,
404        month,
405        day,
406        hour,
407        minute,
408        second,
409    })
410}
411
412/// Parse NOT expression
413fn parse_not_expression<P>(parser: &mut P) -> Result<SqlExpression, String>
414where
415    P: ParsePrimary + ExpressionParser + ?Sized,
416{
417    ExpressionParser::advance(parser); // consume NOT
418
419    // Check if this is a NOT IN expression
420    if let Ok(inner_expr) = parser.parse_comparison() {
421        // After parsing the inner expression, check if we're followed by IN
422        if matches!(ExpressionParser::current_token(parser), Token::In) {
423            debug!("NOT IN expression detected");
424            ExpressionParser::advance(parser); // consume IN
425            ExpressionParser::consume(parser, Token::LeftParen)?;
426            let values = parser.parse_expression_list()?;
427            ExpressionParser::consume(parser, Token::RightParen)?;
428
429            Ok(SqlExpression::NotInList {
430                expr: Box::new(inner_expr),
431                values,
432            })
433        } else {
434            // Regular NOT expression
435            debug!("Regular NOT expression");
436            Ok(SqlExpression::Not {
437                expr: Box::new(inner_expr),
438            })
439        }
440    } else {
441        Err("Expected expression after NOT".to_string())
442    }
443}
444
445/// Trait that parsers must implement to use primary expression parsing
446pub trait ParsePrimary {
447    fn current_token(&self) -> &Token;
448    fn advance(&mut self);
449    fn consume(&mut self, expected: Token) -> Result<(), String>;
450
451    // These methods are called from parse_primary
452    fn parse_case_expression(&mut self) -> Result<SqlExpression, String>;
453    fn parse_function_args(&mut self) -> Result<(Vec<SqlExpression>, bool), String>;
454    fn parse_window_spec(&mut self) -> Result<WindowSpec, String>;
455    fn parse_logical_or(&mut self) -> Result<SqlExpression, String>;
456    fn parse_comparison(&mut self) -> Result<SqlExpression, String>;
457    fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
458
459    // For subquery parsing (without parenthesis balance validation)
460    fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
461}