1use std::fmt::Display;
4use std::ops::Bound;
5
6use crate::expr::{Filter, Operator};
7use crate::{Expr, Literal, ScalarExpr};
8
9impl<'a, F> Expr<'a, F>
10where
11 F: Display + Copy,
12{
13 pub fn format_display(&self) -> String {
15 use Expr::*;
16
17 let mut traverse_stack = Vec::new();
21 let mut postorder = Vec::new();
22 traverse_stack.push(self);
23
24 while let Some(node) = traverse_stack.pop() {
25 postorder.push(node);
26 match node {
27 And(children) | Or(children) => {
28 for child in children {
29 traverse_stack.push(child);
30 }
31 }
32 Not(inner) => traverse_stack.push(inner),
33 Pred(_)
34 | Compare { .. }
35 | InList { .. }
36 | IsNull { .. }
37 | Literal(_)
38 | Exists(_) => {}
39 }
40 }
41
42 let mut result_stack: Vec<String> = Vec::new();
43 for node in postorder.into_iter().rev() {
44 match node {
45 And(children) => {
46 if children.is_empty() {
47 result_stack.push("TRUE".to_string());
48 } else {
49 let mut parts = Vec::with_capacity(children.len());
50 for _ in 0..children.len() {
51 parts.push(result_stack.pop().unwrap_or_default());
52 }
53 parts.reverse();
54 result_stack.push(parts.join(" AND "));
55 }
56 }
57 Or(children) => {
58 if children.is_empty() {
59 result_stack.push("FALSE".to_string());
60 } else {
61 let mut parts = Vec::with_capacity(children.len());
62 for _ in 0..children.len() {
63 parts.push(result_stack.pop().unwrap_or_default());
64 }
65 parts.reverse();
66 result_stack.push(parts.join(" OR "));
67 }
68 }
69 Not(_) => {
70 let inner = result_stack.pop().unwrap_or_default();
71 result_stack.push(format!("NOT ({inner})"));
72 }
73 Pred(filter) => {
74 result_stack.push(format_filter(filter));
75 }
76 Compare { left, op, right } => {
77 result_stack.push(format!(
78 "{} {} {}",
79 left.format_display(),
80 op.as_str(),
81 right.format_display()
82 ));
83 }
84 InList {
85 expr,
86 list,
87 negated,
88 } => {
89 let expr_str = expr.format_display();
90 let mut parts = Vec::with_capacity(list.len());
91 for value in list {
92 parts.push(value.format_display());
93 }
94 let keyword = if *negated { "NOT IN" } else { "IN" };
95 result_stack.push(format!("{} {} ({})", expr_str, keyword, parts.join(", ")));
96 }
97 IsNull { expr, negated } => {
98 let expr_str = expr.format_display();
99 let keyword = if *negated { "IS NOT NULL" } else { "IS NULL" };
100 result_stack.push(format!("{} {}", expr_str, keyword));
101 }
102 Literal(value) => {
103 result_stack.push(if *value {
104 "TRUE".to_string()
105 } else {
106 "FALSE".to_string()
107 });
108 }
109 Exists(_) => {
110 result_stack.push("EXISTS(...)".to_string());
111 }
112 }
113 }
114
115 result_stack.pop().unwrap_or_default()
116 }
117}
118
119impl<F> ScalarExpr<F>
120where
121 F: Display + Copy,
122{
123 pub fn format_display(&self) -> String {
125 use ScalarExpr::*;
126
127 let mut traverse_stack = Vec::new();
128 let mut postorder = Vec::new();
129 traverse_stack.push(self);
130
131 while let Some(node) = traverse_stack.pop() {
132 postorder.push(node);
133 match node {
134 Column(_) | Literal(_) => {}
135 Binary { left, right, .. } | Compare { left, right, .. } => {
136 traverse_stack.push(right);
137 traverse_stack.push(left);
138 }
139 Not(inner) => traverse_stack.push(inner),
140 IsNull { expr, .. } => traverse_stack.push(expr),
141 Aggregate(_) => {}
142 GetField { base, .. } => traverse_stack.push(base),
143 Cast { expr, .. } => traverse_stack.push(expr),
144 Case {
145 operand,
146 branches,
147 else_expr,
148 } => {
149 if let Some(op) = operand {
150 traverse_stack.push(op);
151 }
152 if let Some(else_expr) = else_expr {
153 traverse_stack.push(else_expr);
154 }
155 for (when_expr, then_expr) in branches {
156 traverse_stack.push(then_expr);
157 traverse_stack.push(when_expr);
158 }
159 }
160 Coalesce(items) => {
161 for item in items {
162 traverse_stack.push(item);
163 }
164 }
165 Random => {}
166 ScalarSubquery(_) => {}
167 }
168 }
169
170 let mut result_stack: Vec<String> = Vec::new();
171 for node in postorder.into_iter().rev() {
172 match node {
173 Column(fid) => result_stack.push(format!("col#{}", fid)),
174 Literal(lit) => result_stack.push(lit.format_display()),
175 Aggregate(_agg) => result_stack.push("AGG".to_string()),
176 GetField { field_name, .. } => {
177 let base = result_stack.pop().unwrap_or_default();
178 result_stack.push(format!("{base}.{field_name}"));
179 }
180 Cast { data_type, .. } => {
181 let value = result_stack.pop().unwrap_or_default();
182 result_stack.push(format!("CAST({value} AS {data_type:?})"));
183 }
184 Binary { op, .. } => {
185 let right = result_stack.pop().unwrap_or_default();
186 let left = result_stack.pop().unwrap_or_default();
187 result_stack.push(format!("({} {} {})", left, op.as_str(), right));
188 }
189 Compare { op, .. } => {
190 let right = result_stack.pop().unwrap_or_default();
191 let left = result_stack.pop().unwrap_or_default();
192 result_stack.push(format!("({} {} {})", left, op.as_str(), right));
193 }
194 Not(_) => {
195 let operand = result_stack.pop().unwrap_or_default();
196 result_stack.push(format!("(NOT {})", operand));
197 }
198 IsNull { negated, .. } => {
199 let operand = result_stack.pop().unwrap_or_default();
200 if *negated {
201 result_stack.push(format!("({operand} IS NOT NULL)"));
202 } else {
203 result_stack.push(format!("({operand} IS NULL)"));
204 }
205 }
206 Case {
207 branches,
208 else_expr,
209 ..
210 } => {
211 let mut parts = Vec::with_capacity(branches.len() + 2);
212 for _ in 0..branches.len() {
213 let then_str = result_stack.pop().unwrap_or_default();
214 let when_str = result_stack.pop().unwrap_or_default();
215 parts.push(format!("WHEN {when_str} THEN {then_str}"));
216 }
217 if let Some(_else_expr) = else_expr {
218 parts.push(format!("ELSE {}", result_stack.pop().unwrap_or_default()));
219 }
220
221 parts.push("END".to_string());
222 let mut output = parts.join(" ");
223 output.insert_str(0, "CASE ");
224 result_stack.push(output);
225 }
226 Coalesce(items) => {
227 let mut args = Vec::with_capacity(items.len());
228 for _ in 0..items.len() {
229 args.push(result_stack.pop().unwrap_or_default());
230 }
231 args.reverse();
232 result_stack.push(format!("COALESCE({})", args.join(", ")));
233 }
234 Random => {
235 result_stack.push("RANDOM()".to_string());
236 }
237 ScalarSubquery(sub) => {
238 result_stack.push(format!("(SCALAR_SUBQUERY#{})", sub.id.0));
239 }
240 }
241 }
242
243 result_stack.pop().unwrap_or_default()
244 }
245}
246
247fn format_filter<F: Display>(filter: &Filter<'_, F>) -> String {
248 format!("field#{} {}", filter.field_id, format_operator(&filter.op))
249}
250
251fn format_operator(op: &Operator<'_>) -> String {
252 match op {
253 Operator::Equals(lit) => format!("= {}", lit.format_display()),
254 Operator::Range { lower, upper } => format!(
255 "IN {} .. {}",
256 format_range_bound_lower(lower),
257 format_range_bound_upper(upper)
258 ),
259 Operator::GreaterThan(lit) => format!("> {}", lit.format_display()),
260 Operator::GreaterThanOrEquals(lit) => format!(">= {}", lit.format_display()),
261 Operator::LessThan(lit) => format!("< {}", lit.format_display()),
262 Operator::LessThanOrEquals(lit) => format!("<= {}", lit.format_display()),
263 Operator::In(values) => {
264 let rendered: Vec<String> = values.iter().map(|lit| lit.format_display()).collect();
265 format!("IN {{{}}}", rendered.join(", "))
266 }
267 Operator::StartsWith {
268 pattern,
269 case_sensitive,
270 } => format_pattern_op("STARTS WITH", pattern, *case_sensitive),
271 Operator::EndsWith {
272 pattern,
273 case_sensitive,
274 } => format_pattern_op("ENDS WITH", pattern, *case_sensitive),
275 Operator::Contains {
276 pattern,
277 case_sensitive,
278 } => format_pattern_op("CONTAINS", pattern, *case_sensitive),
279 Operator::IsNull => "IS NULL".to_string(),
280 Operator::IsNotNull => "IS NOT NULL".to_string(),
281 }
282}
283
284fn format_pattern_op(op_name: &str, pattern: &str, case_sensitive: bool) -> String {
285 let mut rendered = format!("{} \"{}\"", op_name, escape_string(pattern));
286 if !case_sensitive {
287 rendered.push_str(" (case-insensitive)");
288 }
289 rendered
290}
291
292fn format_range_bound_lower(bound: &Bound<Literal>) -> String {
293 match bound {
294 Bound::Unbounded => "-inf".to_string(),
295 Bound::Included(lit) => format!("[{}", lit.format_display()),
296 Bound::Excluded(lit) => format!("({}", lit.format_display()),
297 }
298}
299
300fn format_range_bound_upper(bound: &Bound<Literal>) -> String {
301 match bound {
302 Bound::Unbounded => "+inf".to_string(),
303 Bound::Included(lit) => format!("{}]", lit.format_display()),
304 Bound::Excluded(lit) => format!("{})", lit.format_display()),
305 }
306}
307
308fn escape_string(value: &str) -> String {
309 value.chars().flat_map(|c| c.escape_default()).collect()
310}