odatav4_parser/renderers/
filter.rs

1use crate::ast::{ArithmeticOp, ComparisonOp, FilterExpression, LambdaOp, LogicalOp};
2
3/// Helper trait for rendering filter expressions with dialect-specific quoting
4pub trait FilterRenderer {
5    /// Quote an identifier (field name) for this SQL dialect
6    fn quote_identifier(&mut self, ident: &str) -> String;
7    
8    /// Quote a string literal for this SQL dialect
9    fn quote_string(&mut self, s: &str) -> String {
10        format!("'{}'", s.replace('\'', "''"))
11    }
12    
13    /// Render a filter expression to SQL WHERE clause
14    fn render_filter(&mut self, expr: &FilterExpression) -> String {
15        println!("Trait default render_filter: {:?}", expr);
16        match expr {
17            FilterExpression::Comparison { left, op, right } => {
18                let left_sql = self.render_filter(left);
19                let right_sql = self.render_filter(right);
20                let op_sql = match op {
21                    ComparisonOp::Eq => "=",
22                    ComparisonOp::Ne => "<>",
23                    ComparisonOp::Gt => ">",
24                    ComparisonOp::Ge => ">=",
25                    ComparisonOp::Lt => "<",
26                    ComparisonOp::Le => "<=",
27                    ComparisonOp::Has => {
28                        // Special handling for 'has': (left & right) = right
29                        return format!("({} & {}) = {}", left_sql, right_sql, right_sql);
30                    }
31                };
32                format!("{} {} {}", left_sql, op_sql, right_sql)
33            }
34            FilterExpression::Logical { left, op, right } => {
35                let left_sql = self.render_filter(left);
36                let right_sql = self.render_filter(right);
37                let op_sql = match op {
38                    LogicalOp::And => "AND",
39                    LogicalOp::Or => "OR",
40                };
41                format!("({} {} {})", left_sql, op_sql, right_sql)
42            }
43            FilterExpression::Not(inner) => {
44                format!("NOT ({})", self.render_filter(inner))
45            }
46            FilterExpression::Arithmetic { left, op, right } => {
47                let left_sql = self.render_filter(left);
48                let right_sql = self.render_filter(right);
49                let op_sql = match op {
50                    ArithmeticOp::Add => "+",
51                    ArithmeticOp::Sub => "-",
52                    ArithmeticOp::Mul => "*",
53                    ArithmeticOp::Div => "/",
54                    ArithmeticOp::Mod => "%",
55                };
56                format!("({} {} {})", left_sql, op_sql, right_sql)
57            }
58            FilterExpression::UnaryMinus(inner) => {
59                format!("-({})", self.render_filter(inner))
60            }
61            FilterExpression::FunctionCall { name, args } => {
62                self.render_function(name, args)
63            }
64            FilterExpression::In { field, values } => {
65                let field_sql = self.render_filter(field);
66                let values_sql = values
67                    .iter()
68                    .map(|v| self.render_filter(v))
69                    .collect::<Vec<_>>()
70                    .join(", ");
71                format!("{} IN ({})", field_sql, values_sql)
72            }
73            FilterExpression::Lambda { collection, operator, variable, predicate } => {
74                self.render_lambda(collection, operator, variable, predicate)
75            }
76            FilterExpression::Field(name) => self.quote_identifier(name),
77            FilterExpression::StringLiteral(s) => self.quote_string(s),
78            FilterExpression::NumberLiteral(n) => self.render_number_literal(*n),
79            FilterExpression::BooleanLiteral(b) => self.render_boolean_literal(*b),
80            FilterExpression::Null => "NULL".to_string(),
81            FilterExpression::GuidLiteral(guid) => self.quote_string(guid),
82            FilterExpression::DateLiteral(date) => self.quote_string(date),
83        }
84    }
85
86    /// Render a number literal
87    fn render_number_literal(&mut self, n: f64) -> String {
88        if n.fract() == 0.0 {
89            format!("{:.0}", n)
90        } else {
91            n.to_string()
92        }
93    }
94
95    /// Render a boolean literal
96    fn render_boolean_literal(&mut self, b: bool) -> String {
97        if b { "TRUE" } else { "FALSE" }.to_string()
98    }
99
100    /// Render a function call - dialect-specific implementation
101    fn render_function(&mut self, name: &str, args: &[FilterExpression]) -> String {
102        match name.to_lowercase().as_str() {
103            // String functions
104            "contains" => self.render_contains(args),
105            "startswith" => self.render_startswith(args),
106            "endswith" => self.render_endswith(args),
107            "length" => self.render_length(args),
108            "indexof" => self.render_indexof(args),
109            "substring" => self.render_substring(args),
110            "tolower" => self.render_tolower(args),
111            "toupper" => self.render_toupper(args),
112            "trim" => self.render_trim(args),
113            "concat" => self.render_concat(args),
114            
115            // Date/time functions
116            "year" => self.render_year(args),
117            "month" => self.render_month(args),
118            "day" => self.render_day(args),
119            "hour" => self.render_hour(args),
120            "minute" => self.render_minute(args),
121            "second" => self.render_second(args),
122            "now" => self.render_now(args),
123            
124            // Math functions
125            "round" => self.render_round(args),
126            "floor" => self.render_floor(args),
127            "ceiling" => self.render_ceiling(args),
128            
129            _ => format!("{}({})", name.to_uppercase(), 
130                args.iter()
131                    .map(|a| self.render_filter(a))
132                    .collect::<Vec<_>>()
133                    .join(", "))
134        }
135    }
136
137    /// Default implementations for string functions (ANSI SQL)
138    fn render_contains(&mut self, args: &[FilterExpression]) -> String {
139        if args.len() != 2 {
140            return "1=0".to_string(); // Invalid
141        }
142        let field = self.render_filter(&args[0]);
143        let value = self.render_filter(&args[1]);
144        format!("{} LIKE CONCAT('%', {}, '%')", field, value.trim_matches('\''))
145    }
146
147    fn render_startswith(&mut self, args: &[FilterExpression]) -> String {
148        if args.len() != 2 {
149            return "1=0".to_string();
150        }
151        let field = self.render_filter(&args[0]);
152        let value = self.render_filter(&args[1]);
153        format!("{} LIKE CONCAT({}, '%')", field, value.trim_matches('\''))
154    }
155
156    fn render_endswith(&mut self, args: &[FilterExpression]) -> String {
157        if args.len() != 2 {
158            return "1=0".to_string();
159        }
160        let field = self.render_filter(&args[0]);
161        let value = self.render_filter(&args[1]);
162        format!("{} LIKE CONCAT('%', {})", field, value.trim_matches('\''))
163    }
164
165    fn render_length(&mut self, args: &[FilterExpression]) -> String {
166        if args.is_empty() {
167            return "0".to_string();
168        }
169        format!("LENGTH({})", self.render_filter(&args[0]))
170    }
171
172    fn render_indexof(&mut self, args: &[FilterExpression]) -> String {
173        if args.len() < 2 {
174            return "0".to_string();
175        }
176        // Need to evaluate args in order to allow mutable borrow
177        let substr = self.render_filter(&args[1]);
178        let str_source = self.render_filter(&args[0]);
179        format!("POSITION({} IN {})", substr, str_source)
180    }
181
182    fn render_substring(&mut self, args: &[FilterExpression]) -> String {
183        if args.len() < 2 {
184            return "''".to_string();
185        }
186        let field = self.render_filter(&args[0]);
187        let start = self.render_filter(&args[1]);
188        if args.len() >= 3 {
189            let length = self.render_filter(&args[2]);
190            format!("SUBSTRING({}, {} + 1, {})", field, start, length)
191        } else {
192            format!("SUBSTRING({}, {} + 1)", field, start)
193        }
194    }
195
196    fn render_tolower(&mut self, args: &[FilterExpression]) -> String {
197        if args.is_empty() {
198            return "''".to_string();
199        }
200        format!("LOWER({})", self.render_filter(&args[0]))
201    }
202
203    fn render_toupper(&mut self, args: &[FilterExpression]) -> String {
204        if args.is_empty() {
205            return "''".to_string();
206        }
207        format!("UPPER({})", self.render_filter(&args[0]))
208    }
209
210    fn render_trim(&mut self, args: &[FilterExpression]) -> String {
211        if args.is_empty() {
212            return "''".to_string();
213        }
214        format!("TRIM({})", self.render_filter(&args[0]))
215    }
216
217    fn render_concat(&mut self, args: &[FilterExpression]) -> String {
218        if args.len() < 2 {
219            return "''".to_string();
220        }
221        let mut parts = Vec::new();
222        for a in args {
223            parts.push(self.render_filter(a));
224        }
225        format!("CONCAT({})", parts.join(", "))
226    }
227
228    /// Date/time functions
229    fn render_year(&mut self, args: &[FilterExpression]) -> String {
230        if args.is_empty() {
231            return "0".to_string();
232        }
233        format!("YEAR({})", self.render_filter(&args[0]))
234    }
235
236    fn render_month(&mut self, args: &[FilterExpression]) -> String {
237        if args.is_empty() {
238            return "0".to_string();
239        }
240        format!("MONTH({})", self.render_filter(&args[0]))
241    }
242
243    fn render_day(&mut self, args: &[FilterExpression]) -> String {
244        if args.is_empty() {
245            return "0".to_string();
246        }
247        format!("DAY({})", self.render_filter(&args[0]))
248    }
249
250    fn render_hour(&mut self, args: &[FilterExpression]) -> String {
251        if args.is_empty() {
252            return "0".to_string();
253        }
254        format!("HOUR({})", self.render_filter(&args[0]))
255    }
256
257    fn render_minute(&mut self, args: &[FilterExpression]) -> String {
258        if args.is_empty() {
259            return "0".to_string();
260        }
261        format!("MINUTE({})", self.render_filter(&args[0]))
262    }
263
264    fn render_second(&mut self, args: &[FilterExpression]) -> String {
265        if args.is_empty() {
266            return "0".to_string();
267        }
268        format!("SECOND({})", self.render_filter(&args[0]))
269    }
270
271    fn render_now(&mut self, _args: &[FilterExpression]) -> String {
272        "NOW()".to_string()
273    }
274
275    /// Math functions
276    fn render_round(&mut self, args: &[FilterExpression]) -> String {
277        if args.is_empty() {
278            return "0".to_string();
279        }
280        format!("ROUND({}, 0)", self.render_filter(&args[0]))
281    }
282
283    fn render_floor(&mut self, args: &[FilterExpression]) -> String {
284        if args.is_empty() {
285            return "0".to_string();
286        }
287        format!("FLOOR({})", self.render_filter(&args[0]))
288    }
289
290    fn render_ceiling(&mut self, args: &[FilterExpression]) -> String {
291        if args.is_empty() {
292            return "0".to_string();
293        }
294        format!("CEILING({})", self.render_filter(&args[0]))
295    }
296
297    /// Lambda operators - default implementation (may need override per dialect)
298    fn render_lambda(&mut self, collection: &str, operator: &LambdaOp, variable: &str, _predicate: &FilterExpression) -> String {
299        let op_str = match operator {
300            LambdaOp::Any => "ANY",
301            LambdaOp::All => "ALL",
302        };
303        format!("/* TODO: {} lambda on {} with {} */", op_str, collection, variable)
304    }
305}