use crate::sql::parser::ast::{ColumnRef, SqlExpression, WindowSpec};
use crate::sql::parser::lexer::Token;
use tracing::{debug, trace};
use super::{log_parse_decision, trace_parse_entry, trace_parse_exit, ExpressionParser};
pub struct PrimaryExpressionContext<'a> {
pub columns: &'a [String],
pub in_method_args: bool,
}
impl<'a> Default for PrimaryExpressionContext<'a> {
fn default() -> Self {
Self {
columns: &[],
in_method_args: false,
}
}
}
pub fn parse_primary<P>(
parser: &mut P,
ctx: &PrimaryExpressionContext,
) -> Result<SqlExpression, String>
where
P: ParsePrimary + ExpressionParser + ?Sized,
{
trace_parse_entry("parse_primary", ExpressionParser::current_token(parser));
if let Token::NumberLiteral(num_str) = ExpressionParser::current_token(parser) {
if ctx.columns.iter().any(|col| col == num_str) {
log_parse_decision(
"parse_primary",
ExpressionParser::current_token(parser),
"Number literal matches column name, treating as column",
);
let expr = SqlExpression::Column(ColumnRef::unquoted(num_str.clone()));
ExpressionParser::advance(parser);
let result = Ok(expr);
trace_parse_exit("parse_primary", &result);
return result;
}
}
let result = match ExpressionParser::current_token(parser) {
Token::Case => {
debug!("Parsing CASE expression");
parser.parse_case_expression()
}
Token::DateTime => {
debug!("Parsing DateTime constructor");
parse_datetime_constructor(parser)
}
Token::Unnest => {
debug!("Parsing UNNEST expression");
parse_unnest(parser)
}
Token::Identifier(id) => {
let id_upper = id.to_uppercase();
let id_clone = id.clone();
if id_upper == "TRUE" {
log_parse_decision(
"parse_primary",
ExpressionParser::current_token(parser),
"Boolean literal TRUE",
);
ExpressionParser::advance(parser);
Ok(SqlExpression::BooleanLiteral(true))
} else if id_upper == "FALSE" {
log_parse_decision(
"parse_primary",
ExpressionParser::current_token(parser),
"Boolean literal FALSE",
);
ExpressionParser::advance(parser);
Ok(SqlExpression::BooleanLiteral(false))
} else {
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::Dot) {
ExpressionParser::advance(parser);
if let Token::Identifier(next_id) = ExpressionParser::current_token(parser) {
let next_id = next_id.clone();
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
debug!(object = %id_clone, method = %next_id, "Parsing method call");
ExpressionParser::advance(parser);
let args = if matches!(
ExpressionParser::current_token(parser),
Token::RightParen
) {
Vec::new()
} else {
parser.parse_expression_list()?
};
ExpressionParser::consume(parser, Token::RightParen)?;
log_parse_decision(
"parse_primary",
&Token::Identifier(next_id.clone()),
"Method call",
);
Ok(SqlExpression::MethodCall {
object: id_clone,
method: next_id,
args,
})
} else {
let col_ref = ColumnRef::qualified(id_clone, next_id.clone());
log_parse_decision(
"parse_primary",
&Token::Identifier(next_id),
"Qualified column reference",
);
Ok(SqlExpression::Column(col_ref))
}
} else {
Err("Expected identifier after '.'".to_string())
}
} else if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
debug!(function = %id_upper, "Parsing function call");
ExpressionParser::advance(parser); let (args, has_distinct) = parser.parse_function_args()?;
ExpressionParser::consume(parser, Token::RightParen)?;
if matches!(ExpressionParser::current_token(parser), Token::Over) {
debug!(function = %id_upper, "Window function detected");
ExpressionParser::advance(parser); ExpressionParser::consume(parser, Token::LeftParen)?;
let window_spec = parser.parse_window_spec()?;
ExpressionParser::consume(parser, Token::RightParen)?;
Ok(SqlExpression::WindowFunction {
name: id_upper,
args,
window_spec,
})
} else {
Ok(SqlExpression::FunctionCall {
name: id_upper,
args,
distinct: has_distinct,
})
}
} else {
log_parse_decision(
"parse_primary",
&Token::Identifier(id_clone.clone()),
"Column reference",
);
Ok(SqlExpression::Column(ColumnRef::unquoted(id_clone)))
}
}
}
Token::QuotedIdentifier(id) => {
let expr = if ctx.in_method_args {
log_parse_decision(
"parse_primary",
ExpressionParser::current_token(parser),
"Quoted identifier in method args - treating as string",
);
SqlExpression::StringLiteral(id.clone())
} else {
log_parse_decision(
"parse_primary",
ExpressionParser::current_token(parser),
"Quoted identifier as column name",
);
SqlExpression::Column(ColumnRef::quoted(id.clone()))
};
ExpressionParser::advance(parser);
Ok(expr)
}
Token::StringLiteral(s) => {
trace!("String literal: {}", s);
let expr = SqlExpression::StringLiteral(s.clone());
ExpressionParser::advance(parser);
Ok(expr)
}
Token::NumberLiteral(n) => {
trace!("Number literal: {}", n);
let expr = SqlExpression::NumberLiteral(n.clone());
ExpressionParser::advance(parser);
Ok(expr)
}
Token::Null => {
trace!("NULL literal");
ExpressionParser::advance(parser);
Ok(SqlExpression::Null)
}
Token::Left | Token::Right => {
let func_name = match ExpressionParser::current_token(parser) {
Token::Left => "LEFT".to_string(),
Token::Right => "RIGHT".to_string(),
_ => unreachable!(),
};
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::LeftParen) {
debug!(function = %func_name, "Parsing LEFT/RIGHT function call");
ExpressionParser::advance(parser); let (args, _has_distinct) = parser.parse_function_args()?;
ExpressionParser::consume(parser, Token::RightParen)?;
Ok(SqlExpression::FunctionCall {
name: func_name,
args,
distinct: false,
})
} else {
Err(format!(
"{} keyword unexpected in expression context",
func_name
))
}
}
Token::LeftParen => {
debug!("Parsing parenthesized expression or subquery");
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::Select) {
debug!("Detected subquery - parsing SELECT statement");
let subquery = parser.parse_subquery()?;
ExpressionParser::consume(parser, Token::RightParen)?;
Ok(SqlExpression::ScalarSubquery {
query: Box::new(subquery),
})
} else {
debug!("Regular parenthesized expression");
let expr = parser.parse_logical_or()?;
ExpressionParser::consume(parser, Token::RightParen)?;
Ok(expr)
}
}
Token::Not => {
debug!("Parsing NOT expression");
parse_not_expression(parser)
}
Token::Star => {
trace!("Star token as literal");
ExpressionParser::advance(parser);
Ok(SqlExpression::StringLiteral("*".to_string()))
}
Token::Row => {
trace!("ROW token treated as identifier in expression context");
ExpressionParser::advance(parser);
Ok(SqlExpression::Column(ColumnRef::unquoted(
"row".to_string(),
)))
}
Token::Rows => {
trace!("ROWS token treated as identifier in expression context");
ExpressionParser::advance(parser);
Ok(SqlExpression::Column(ColumnRef::unquoted(
"rows".to_string(),
)))
}
Token::Range => {
trace!("RANGE token treated as identifier in expression context");
ExpressionParser::advance(parser);
Ok(SqlExpression::Column(ColumnRef::unquoted(
"range".to_string(),
)))
}
_ => {
let err = format!(
"Unexpected token in primary expression: {:?}",
ExpressionParser::current_token(parser)
);
debug!(error = %err);
Err(err)
}
};
trace_parse_exit("parse_primary", &result);
result
}
fn parse_datetime_constructor<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParsePrimary + ExpressionParser + ?Sized,
{
ExpressionParser::advance(parser); ExpressionParser::consume(parser, Token::LeftParen)?;
if matches!(ExpressionParser::current_token(parser), Token::RightParen) {
ExpressionParser::advance(parser); debug!("DateTime() - today's date");
return Ok(SqlExpression::DateTimeToday {
hour: None,
minute: None,
second: None,
});
}
let year = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
n.parse::<i32>().map_err(|_| "Invalid year")?
} else {
return Err("Expected year in DateTime constructor".to_string());
};
ExpressionParser::advance(parser);
ExpressionParser::consume(parser, Token::Comma)?;
let month = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
n.parse::<u32>().map_err(|_| "Invalid month")?
} else {
return Err("Expected month in DateTime constructor".to_string());
};
ExpressionParser::advance(parser);
ExpressionParser::consume(parser, Token::Comma)?;
let day = if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
n.parse::<u32>().map_err(|_| "Invalid day")?
} else {
return Err("Expected day in DateTime constructor".to_string());
};
ExpressionParser::advance(parser);
let mut hour = None;
let mut minute = None;
let mut second = None;
if matches!(ExpressionParser::current_token(parser), Token::Comma) {
ExpressionParser::advance(parser);
if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
hour = Some(n.parse::<u32>().map_err(|_| "Invalid hour")?);
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::Comma) {
ExpressionParser::advance(parser);
if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
minute = Some(n.parse::<u32>().map_err(|_| "Invalid minute")?);
ExpressionParser::advance(parser);
if matches!(ExpressionParser::current_token(parser), Token::Comma) {
ExpressionParser::advance(parser);
if let Token::NumberLiteral(n) = ExpressionParser::current_token(parser) {
second = Some(n.parse::<u32>().map_err(|_| "Invalid second")?);
ExpressionParser::advance(parser);
}
}
}
}
}
}
ExpressionParser::consume(parser, Token::RightParen)?;
debug!(year = year, month = month, day = day, hour = ?hour, minute = ?minute, second = ?second, "DateTime constructor parsed");
Ok(SqlExpression::DateTimeConstructor {
year,
month,
day,
hour,
minute,
second,
})
}
fn parse_not_expression<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParsePrimary + ExpressionParser + ?Sized,
{
ExpressionParser::advance(parser);
if let Ok(inner_expr) = parser.parse_comparison() {
if matches!(ExpressionParser::current_token(parser), Token::In) {
debug!("NOT IN expression detected");
ExpressionParser::advance(parser); ExpressionParser::consume(parser, Token::LeftParen)?;
let values = parser.parse_expression_list()?;
ExpressionParser::consume(parser, Token::RightParen)?;
Ok(SqlExpression::NotInList {
expr: Box::new(inner_expr),
values,
})
} else {
debug!("Regular NOT expression");
Ok(SqlExpression::Not {
expr: Box::new(inner_expr),
})
}
} else {
Err("Expected expression after NOT".to_string())
}
}
fn parse_unnest<P>(parser: &mut P) -> Result<SqlExpression, String>
where
P: ParsePrimary + ExpressionParser + ?Sized,
{
debug!("parse_unnest: starting");
ExpressionParser::advance(parser); ExpressionParser::consume(parser, Token::LeftParen)?;
let column = parser.parse_logical_or()?;
debug!("parse_unnest: parsed column expression");
ExpressionParser::consume(parser, Token::Comma)?;
let delimiter = match ExpressionParser::current_token(parser) {
Token::StringLiteral(s) => {
let delim = s.clone();
ExpressionParser::advance(parser);
delim
}
_ => {
return Err("UNNEST delimiter must be a string literal".to_string());
}
};
debug!(delimiter = %delimiter, "parse_unnest: parsed delimiter");
ExpressionParser::consume(parser, Token::RightParen)?;
debug!("parse_unnest: complete");
Ok(SqlExpression::Unnest {
column: Box::new(column),
delimiter,
})
}
pub trait ParsePrimary {
fn current_token(&self) -> &Token;
fn advance(&mut self);
fn consume(&mut self, expected: Token) -> Result<(), String>;
fn parse_case_expression(&mut self) -> Result<SqlExpression, String>;
fn parse_function_args(&mut self) -> Result<(Vec<SqlExpression>, bool), String>;
fn parse_window_spec(&mut self) -> Result<WindowSpec, String>;
fn parse_logical_or(&mut self) -> Result<SqlExpression, String>;
fn parse_comparison(&mut self) -> Result<SqlExpression, String>;
fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
}