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        _ => {
280            let err = format!(
281                "Unexpected token in primary expression: {:?}",
282                ExpressionParser::current_token(parser)
283            );
284            debug!(error = %err);
285            Err(err)
286        }
287    };
288
289    trace_parse_exit("parse_primary", &result);
290    result
291}
292
293/// Parse DateTime constructor
294fn parse_datetime_constructor<P>(parser: &mut P) -> Result<SqlExpression, String>
295where
296    P: ParsePrimary + ExpressionParser + ?Sized,
297{
298    ExpressionParser::advance(parser); // consume DateTime
299    ExpressionParser::consume(parser, Token::LeftParen)?;
300
301    // Check if empty parentheses for DateTime() - today's date
302    if matches!(ExpressionParser::current_token(parser), Token::RightParen) {
303        ExpressionParser::advance(parser); // consume )
304        debug!("DateTime() - today's date");
305        return Ok(SqlExpression::DateTimeToday {
306            hour: None,
307            minute: None,
308            second: None,
309        });
310    }
311
312    // Parse year
313    let year = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
314        n.parse::<i32>().map_err(|_| "Invalid year")?
315    } else {
316        return Err("Expected year in DateTime constructor".to_string());
317    };
318    ExpressionParser::advance(parser);
319    ExpressionParser::consume(parser, Token::Comma)?;
320
321    // Parse month
322    let month = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
323        n.parse::<u32>().map_err(|_| "Invalid month")?
324    } else {
325        return Err("Expected month in DateTime constructor".to_string());
326    };
327    ExpressionParser::advance(parser);
328    ExpressionParser::consume(parser, Token::Comma)?;
329
330    // Parse day
331    let day = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
332        n.parse::<u32>().map_err(|_| "Invalid day")?
333    } else {
334        return Err("Expected day in DateTime constructor".to_string());
335    };
336    ExpressionParser::advance(parser);
337
338    // Check for optional time components
339    let mut hour = None;
340    let mut minute = None;
341    let mut second = None;
342
343    if matches!(ExpressionParser::current_token(parser), Token::Comma) {
344        ExpressionParser::advance(parser); // consume comma
345
346        // Parse hour
347        if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
348            hour = Some(n.parse::<u32>().map_err(|_| "Invalid hour")?);
349            ExpressionParser::advance(parser);
350
351            // Check for minute
352            if matches!(ExpressionParser::current_token(parser), Token::Comma) {
353                ExpressionParser::advance(parser); // consume comma
354
355                if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
356                    minute = Some(n.parse::<u32>().map_err(|_| "Invalid minute")?);
357                    ExpressionParser::advance(parser);
358
359                    // Check for second
360                    if matches!(ExpressionParser::current_token(parser), Token::Comma) {
361                        ExpressionParser::advance(parser); // consume comma
362
363                        if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
364                            second = Some(n.parse::<u32>().map_err(|_| "Invalid second")?);
365                            ExpressionParser::advance(parser);
366                        }
367                    }
368                }
369            }
370        }
371    }
372
373    ExpressionParser::consume(parser, Token::RightParen)?;
374
375    debug!(year = year, month = month, day = day, hour = ?hour, minute = ?minute, second = ?second, "DateTime constructor parsed");
376
377    Ok(SqlExpression::DateTimeConstructor {
378        year,
379        month,
380        day,
381        hour,
382        minute,
383        second,
384    })
385}
386
387/// Parse NOT expression
388fn parse_not_expression<P>(parser: &mut P) -> Result<SqlExpression, String>
389where
390    P: ParsePrimary + ExpressionParser + ?Sized,
391{
392    ExpressionParser::advance(parser); // consume NOT
393
394    // Check if this is a NOT IN expression
395    if let Ok(inner_expr) = parser.parse_comparison() {
396        // After parsing the inner expression, check if we're followed by IN
397        if matches!(ExpressionParser::current_token(parser), Token::In) {
398            debug!("NOT IN expression detected");
399            ExpressionParser::advance(parser); // consume IN
400            ExpressionParser::consume(parser, Token::LeftParen)?;
401            let values = parser.parse_expression_list()?;
402            ExpressionParser::consume(parser, Token::RightParen)?;
403
404            Ok(SqlExpression::NotInList {
405                expr: Box::new(inner_expr),
406                values,
407            })
408        } else {
409            // Regular NOT expression
410            debug!("Regular NOT expression");
411            Ok(SqlExpression::Not {
412                expr: Box::new(inner_expr),
413            })
414        }
415    } else {
416        Err("Expected expression after NOT".to_string())
417    }
418}
419
420/// Trait that parsers must implement to use primary expression parsing
421pub trait ParsePrimary {
422    fn current_token(&self) -> &Token;
423    fn advance(&mut self);
424    fn consume(&mut self, expected: Token) -> Result<(), String>;
425
426    // These methods are called from parse_primary
427    fn parse_case_expression(&mut self) -> Result<SqlExpression, String>;
428    fn parse_function_args(&mut self) -> Result<(Vec<SqlExpression>, bool), String>;
429    fn parse_window_spec(&mut self) -> Result<WindowSpec, String>;
430    fn parse_logical_or(&mut self) -> Result<SqlExpression, String>;
431    fn parse_comparison(&mut self) -> Result<SqlExpression, String>;
432    fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
433
434    // For subquery parsing (without parenthesis balance validation)
435    fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
436}