sql_cli/data/
where_clause_converter.rs

1use crate::sql::recursive_parser::{Condition, LogicalOp, SqlExpression, WhereClause};
2use crate::sql::where_ast::{WhereExpr, WhereValue};
3use anyhow::Result;
4
5/// Converts recursive_parser's WhereClause to where_ast's WhereExpr
6pub struct WhereClauseConverter;
7
8impl WhereClauseConverter {
9    /// Convert a WhereClause from recursive_parser to WhereExpr for where_evaluator
10    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        // Convert all conditions
16        let mut expr = Self::convert_condition(&where_clause.conditions[0])?;
17
18        // Chain conditions with AND/OR
19        for i in 1..where_clause.conditions.len() {
20            let next_expr = Self::convert_condition(&where_clause.conditions[i])?;
21
22            // Use the connector from the previous condition
23            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        // For debugging - let's see what we're getting
82        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                // Handle IS NULL
103                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                // Handle IS NOT NULL
111                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        // Handle string methods like column.Contains("value")
127        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                // These typically chain with an equals, e.g., column.ToLower() == "value"
151                // For now, return an error - would need more context
152                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                // Try to parse as number
179                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                // Column references in values not supported yet
187                Err(anyhow::anyhow!(
188                    "Column references in WHERE values not yet supported"
189                ))
190            }
191            _ => Ok(WhereValue::Null),
192        }
193    }
194}