sql_cli/data/
where_clause_converter.rs1use crate::sql::recursive_parser::{Condition, LogicalOp, SqlExpression, WhereClause};
2use crate::sql::where_ast::{WhereExpr, WhereValue};
3use anyhow::Result;
4
5pub struct WhereClauseConverter;
7
8impl WhereClauseConverter {
9 pub fn convert(where_clause: &WhereClause) -> Result<WhereExpr> {
11 if where_clause.conditions.is_empty() {
12 return Err(anyhow::anyhow!("Empty WHERE clause"));
13 }
14
15 let mut expr = Self::convert_condition(&where_clause.conditions[0])?;
17
18 for i in 1..where_clause.conditions.len() {
20 let next_expr = Self::convert_condition(&where_clause.conditions[i])?;
21
22 if let Some(connector) = &where_clause.conditions[i - 1].connector {
24 expr = match connector {
25 LogicalOp::And => WhereExpr::And(Box::new(expr), Box::new(next_expr)),
26 LogicalOp::Or => WhereExpr::Or(Box::new(expr), Box::new(next_expr)),
27 };
28 }
29 }
30
31 Ok(expr)
32 }
33
34 fn convert_condition(condition: &Condition) -> Result<WhereExpr> {
35 Self::convert_expression(&condition.expr)
36 }
37
38 fn convert_expression(expr: &SqlExpression) -> Result<WhereExpr> {
39 match expr {
40 SqlExpression::BinaryOp { left, op, right } => Self::convert_binary_op(left, op, right),
41 SqlExpression::InList { expr, values } => {
42 let column = Self::extract_column_name(expr)?;
43 let where_values = values
44 .iter()
45 .map(Self::convert_to_where_value)
46 .collect::<Result<Vec<_>>>()?;
47 Ok(WhereExpr::In(column, where_values))
48 }
49 SqlExpression::NotInList { expr, values } => {
50 let column = Self::extract_column_name(expr)?;
51 let where_values = values
52 .iter()
53 .map(Self::convert_to_where_value)
54 .collect::<Result<Vec<_>>>()?;
55 Ok(WhereExpr::NotIn(column, where_values))
56 }
57 SqlExpression::Between { expr, lower, upper } => {
58 let column = Self::extract_column_name(expr)?;
59 let lower_value = Self::convert_to_where_value(lower)?;
60 let upper_value = Self::convert_to_where_value(upper)?;
61 Ok(WhereExpr::Between(column, lower_value, upper_value))
62 }
63 SqlExpression::Not { expr } => {
64 let inner = Self::convert_expression(expr)?;
65 Ok(WhereExpr::Not(Box::new(inner)))
66 }
67 SqlExpression::MethodCall {
68 object,
69 method,
70 args,
71 } => Self::convert_method_call(object, method, args),
72 _ => Err(anyhow::anyhow!("Unsupported expression type: {:?}", expr)),
73 }
74 }
75
76 fn convert_binary_op(
77 left: &SqlExpression,
78 op: &str,
79 right: &SqlExpression,
80 ) -> Result<WhereExpr> {
81 eprintln!("Converting binary op: {:?} {} {:?}", left, op, right);
83
84 let column = Self::extract_column_name(left)?;
85 let value = Self::convert_to_where_value(right)?;
86
87 match op.to_uppercase().as_str() {
88 "=" | "==" => Ok(WhereExpr::Equal(column, value)),
89 "!=" | "<>" => Ok(WhereExpr::NotEqual(column, value)),
90 ">" => Ok(WhereExpr::GreaterThan(column, value)),
91 ">=" => Ok(WhereExpr::GreaterThanOrEqual(column, value)),
92 "<" => Ok(WhereExpr::LessThan(column, value)),
93 "<=" => Ok(WhereExpr::LessThanOrEqual(column, value)),
94 "LIKE" => {
95 if let WhereValue::String(pattern) = value {
96 Ok(WhereExpr::Like(column, pattern))
97 } else {
98 Err(anyhow::anyhow!("LIKE requires string pattern"))
99 }
100 }
101 "IS" => {
102 if matches!(value, WhereValue::Null) {
104 Ok(WhereExpr::IsNull(column))
105 } else {
106 Err(anyhow::anyhow!("IS only supports NULL"))
107 }
108 }
109 "IS NOT" => {
110 if matches!(value, WhereValue::Null) {
112 Ok(WhereExpr::IsNotNull(column))
113 } else {
114 Err(anyhow::anyhow!("IS NOT only supports NULL"))
115 }
116 }
117 _ => Err(anyhow::anyhow!("Unsupported operator: {}", op)),
118 }
119 }
120
121 fn convert_method_call(
122 object: &str,
123 method: &str,
124 args: &[SqlExpression],
125 ) -> Result<WhereExpr> {
126 match method.to_lowercase().as_str() {
128 "contains" => {
129 if args.len() != 1 {
130 return Err(anyhow::anyhow!("Contains requires exactly 1 argument"));
131 }
132 let value = Self::extract_string_value(&args[0])?;
133 Ok(WhereExpr::Contains(object.to_string(), value))
134 }
135 "startswith" => {
136 if args.len() != 1 {
137 return Err(anyhow::anyhow!("StartsWith requires exactly 1 argument"));
138 }
139 let value = Self::extract_string_value(&args[0])?;
140 Ok(WhereExpr::StartsWith(object.to_string(), value))
141 }
142 "endswith" => {
143 if args.len() != 1 {
144 return Err(anyhow::anyhow!("EndsWith requires exactly 1 argument"));
145 }
146 let value = Self::extract_string_value(&args[0])?;
147 Ok(WhereExpr::EndsWith(object.to_string(), value))
148 }
149 "tolower" | "toupper" => {
150 Err(anyhow::anyhow!(
153 "ToLower/ToUpper methods need comparison context"
154 ))
155 }
156 _ => Err(anyhow::anyhow!("Unsupported method: {}", method)),
157 }
158 }
159
160 fn extract_column_name(expr: &SqlExpression) -> Result<String> {
161 match expr {
162 SqlExpression::Column(name) => Ok(name.clone()),
163 _ => Err(anyhow::anyhow!("Expected column name, got: {:?}", expr)),
164 }
165 }
166
167 fn extract_string_value(expr: &SqlExpression) -> Result<String> {
168 match expr {
169 SqlExpression::StringLiteral(s) => Ok(s.clone()),
170 _ => Err(anyhow::anyhow!("Expected string literal, got: {:?}", expr)),
171 }
172 }
173
174 fn convert_to_where_value(expr: &SqlExpression) -> Result<WhereValue> {
175 match expr {
176 SqlExpression::StringLiteral(s) => Ok(WhereValue::String(s.clone())),
177 SqlExpression::NumberLiteral(n) => {
178 if let Ok(num) = n.parse::<f64>() {
180 Ok(WhereValue::Number(num))
181 } else {
182 Ok(WhereValue::String(n.clone()))
183 }
184 }
185 SqlExpression::Column(_) => {
186 Err(anyhow::anyhow!(
188 "Column references in WHERE values not yet supported"
189 ))
190 }
191 _ => Ok(WhereValue::Null),
192 }
193 }
194}