sql_cli/sql/parser/expressions/
comparison.rs1use crate::sql::parser::ast::SqlExpression;
5use crate::sql::parser::lexer::Token;
6use tracing::debug;
7
8use super::{log_parse_decision, trace_parse_entry, trace_parse_exit};
9
10pub fn parse_comparison<P>(parser: &mut P) -> Result<SqlExpression, String>
13where
14 P: ParseComparison + ?Sized,
15{
16 trace_parse_entry("parse_comparison", parser.current_token());
17
18 let mut left = parser.parse_additive()?;
19
20 if matches!(parser.current_token(), Token::Between) {
22 debug!("BETWEEN operator detected");
23 log_parse_decision(
24 "parse_comparison",
25 parser.current_token(),
26 "BETWEEN operator - parsing range bounds",
27 );
28
29 parser.advance(); let lower = parser.parse_additive()?;
31 parser.consume(Token::And)?; let upper = parser.parse_additive()?;
33
34 let result = Ok(SqlExpression::Between {
35 expr: Box::new(left),
36 lower: Box::new(lower),
37 upper: Box::new(upper),
38 });
39 trace_parse_exit("parse_comparison", &result);
40 return result;
41 }
42
43 if matches!(parser.current_token(), Token::Not) {
45 parser.advance(); if matches!(parser.current_token(), Token::In) {
48 debug!("NOT IN operator detected");
49 log_parse_decision(
50 "parse_comparison",
51 parser.current_token(),
52 "NOT IN operator - parsing value list",
53 );
54
55 parser.advance(); parser.consume(Token::LeftParen)?;
57
58 if matches!(parser.current_token(), Token::Select | Token::With) {
61 debug!("Detected NOT IN subquery");
62 let subquery = parser.parse_subquery()?;
63 parser.consume(Token::RightParen)?;
64
65 let result = Ok(SqlExpression::NotInSubquery {
66 expr: Box::new(left),
67 subquery: Box::new(subquery),
68 });
69 trace_parse_exit("parse_comparison", &result);
70 return result;
71 } else {
72 let values = parser.parse_expression_list()?;
74 parser.consume(Token::RightParen)?;
75
76 let result = Ok(SqlExpression::NotInList {
77 expr: Box::new(left),
78 values,
79 });
80 trace_parse_exit("parse_comparison", &result);
81 return result;
82 }
83 } else {
84 return Err("Expected IN after NOT".to_string());
85 }
86 }
87
88 if matches!(parser.current_token(), Token::In) {
96 let result = parse_in_operator(parser, left);
97 trace_parse_exit("parse_comparison", &result);
98 return result;
99 }
100
101 if matches!(parser.current_token(), Token::Is) {
103 parser.advance(); if matches!(parser.current_token(), Token::Not) {
106 parser.advance(); if matches!(parser.current_token(), Token::Null) {
108 debug!("IS NOT NULL operator detected");
109 log_parse_decision(
110 "parse_comparison",
111 parser.current_token(),
112 "IS NOT NULL operator",
113 );
114
115 parser.advance(); left = SqlExpression::BinaryOp {
117 left: Box::new(left),
118 op: "IS NOT NULL".to_string(),
119 right: Box::new(SqlExpression::Null),
120 };
121 } else {
122 return Err("Expected NULL after IS NOT".to_string());
123 }
124 } else if matches!(parser.current_token(), Token::Null) {
125 debug!("IS NULL operator detected");
126 log_parse_decision(
127 "parse_comparison",
128 parser.current_token(),
129 "IS NULL operator",
130 );
131
132 parser.advance(); left = SqlExpression::BinaryOp {
134 left: Box::new(left),
135 op: "IS NULL".to_string(),
136 right: Box::new(SqlExpression::Null),
137 };
138 } else {
139 return Err("Expected NULL or NOT after IS".to_string());
140 }
141 }
142 else if let Some(op) = get_comparison_op(parser.current_token()) {
144 log_parse_decision(
145 "parse_comparison",
146 parser.current_token(),
147 &format!("Comparison operator '{}' found", op),
148 );
149
150 debug!(operator = %op, "Processing comparison operator");
151
152 parser.advance();
153 let right = parser.parse_additive()?;
154 left = SqlExpression::BinaryOp {
155 left: Box::new(left),
156 op,
157 right: Box::new(right),
158 };
159 }
160
161 let result = Ok(left);
162 trace_parse_exit("parse_comparison", &result);
163 result
164}
165
166pub fn parse_in_operator<P>(parser: &mut P, expr: SqlExpression) -> Result<SqlExpression, String>
169where
170 P: ParseComparison + ?Sized,
171{
172 trace_parse_entry("parse_in_operator", parser.current_token());
173
174 if matches!(parser.current_token(), Token::In) {
175 debug!("IN operator detected");
176 log_parse_decision(
177 "parse_in_operator",
178 parser.current_token(),
179 "IN operator - parsing value list",
180 );
181
182 parser.advance(); parser.consume(Token::LeftParen)?;
184
185 if matches!(parser.current_token(), Token::Select | Token::With) {
188 debug!("Detected IN subquery");
189 let subquery = parser.parse_subquery()?;
190 parser.consume(Token::RightParen)?;
191
192 let result = Ok(SqlExpression::InSubquery {
193 expr: Box::new(expr),
194 subquery: Box::new(subquery),
195 });
196 trace_parse_exit("parse_in_operator", &result);
197 return result;
198 } else {
199 let values = parser.parse_expression_list()?;
201 parser.consume(Token::RightParen)?;
202
203 let result = Ok(SqlExpression::InList {
204 expr: Box::new(expr),
205 values,
206 });
207 trace_parse_exit("parse_in_operator", &result);
208 return result;
209 }
210 } else {
211 Ok(expr)
212 }
213}
214
215fn get_comparison_op(token: &Token) -> Option<String> {
217 match token {
218 Token::Equal => Some("=".to_string()),
219 Token::NotEqual => Some("!=".to_string()),
220 Token::LessThan => Some("<".to_string()),
221 Token::GreaterThan => Some(">".to_string()),
222 Token::LessThanOrEqual => Some("<=".to_string()),
223 Token::GreaterThanOrEqual => Some(">=".to_string()),
224 Token::Like => Some("LIKE".to_string()),
225 Token::ILike => Some("ILIKE".to_string()),
226 _ => None,
227 }
228}
229
230pub trait ParseComparison {
232 fn current_token(&self) -> &Token;
233 fn advance(&mut self);
234 fn consume(&mut self, expected: Token) -> Result<(), String>;
235
236 fn parse_primary(&mut self) -> Result<SqlExpression, String>;
238 fn parse_additive(&mut self) -> Result<SqlExpression, String>;
239 fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
240
241 fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
243}