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::Is) {
90 parser.advance(); if matches!(parser.current_token(), Token::Not) {
93 parser.advance(); if matches!(parser.current_token(), Token::Null) {
95 debug!("IS NOT NULL operator detected");
96 log_parse_decision(
97 "parse_comparison",
98 parser.current_token(),
99 "IS NOT NULL operator",
100 );
101
102 parser.advance(); left = SqlExpression::BinaryOp {
104 left: Box::new(left),
105 op: "IS NOT NULL".to_string(),
106 right: Box::new(SqlExpression::Null),
107 };
108 } else {
109 return Err("Expected NULL after IS NOT".to_string());
110 }
111 } else if matches!(parser.current_token(), Token::Null) {
112 debug!("IS NULL operator detected");
113 log_parse_decision(
114 "parse_comparison",
115 parser.current_token(),
116 "IS NULL operator",
117 );
118
119 parser.advance(); left = SqlExpression::BinaryOp {
121 left: Box::new(left),
122 op: "IS NULL".to_string(),
123 right: Box::new(SqlExpression::Null),
124 };
125 } else {
126 return Err("Expected NULL or NOT after IS".to_string());
127 }
128 }
129 else if let Some(op) = get_comparison_op(parser.current_token()) {
131 log_parse_decision(
132 "parse_comparison",
133 parser.current_token(),
134 &format!("Comparison operator '{}' found", op),
135 );
136
137 debug!(operator = %op, "Processing comparison operator");
138
139 parser.advance();
140 let right = parser.parse_additive()?;
141 left = SqlExpression::BinaryOp {
142 left: Box::new(left),
143 op,
144 right: Box::new(right),
145 };
146 }
147
148 let result = Ok(left);
149 trace_parse_exit("parse_comparison", &result);
150 result
151}
152
153pub fn parse_in_operator<P>(parser: &mut P, expr: SqlExpression) -> Result<SqlExpression, String>
156where
157 P: ParseComparison + ?Sized,
158{
159 trace_parse_entry("parse_in_operator", parser.current_token());
160
161 if matches!(parser.current_token(), Token::In) {
162 debug!("IN operator detected");
163 log_parse_decision(
164 "parse_in_operator",
165 parser.current_token(),
166 "IN operator - parsing value list",
167 );
168
169 parser.advance(); parser.consume(Token::LeftParen)?;
171
172 if matches!(parser.current_token(), Token::Select | Token::With) {
175 debug!("Detected IN subquery");
176 let subquery = parser.parse_subquery()?;
177 parser.consume(Token::RightParen)?;
178
179 let result = Ok(SqlExpression::InSubquery {
180 expr: Box::new(expr),
181 subquery: Box::new(subquery),
182 });
183 trace_parse_exit("parse_in_operator", &result);
184 return result;
185 } else {
186 let values = parser.parse_expression_list()?;
188 parser.consume(Token::RightParen)?;
189
190 let result = Ok(SqlExpression::InList {
191 expr: Box::new(expr),
192 values,
193 });
194 trace_parse_exit("parse_in_operator", &result);
195 return result;
196 }
197 } else {
198 Ok(expr)
199 }
200}
201
202fn get_comparison_op(token: &Token) -> Option<String> {
204 match token {
205 Token::Equal => Some("=".to_string()),
206 Token::NotEqual => Some("!=".to_string()),
207 Token::LessThan => Some("<".to_string()),
208 Token::GreaterThan => Some(">".to_string()),
209 Token::LessThanOrEqual => Some("<=".to_string()),
210 Token::GreaterThanOrEqual => Some(">=".to_string()),
211 Token::Like => Some("LIKE".to_string()),
212 Token::ILike => Some("ILIKE".to_string()),
213 _ => None,
214 }
215}
216
217pub trait ParseComparison {
219 fn current_token(&self) -> &Token;
220 fn advance(&mut self);
221 fn consume(&mut self, expected: Token) -> Result<(), String>;
222
223 fn parse_primary(&mut self) -> Result<SqlExpression, String>;
225 fn parse_additive(&mut self) -> Result<SqlExpression, String>;
226 fn parse_expression_list(&mut self) -> Result<Vec<SqlExpression>, String>;
227
228 fn parse_subquery(&mut self) -> Result<crate::sql::parser::ast::SelectStatement, String>;
230}