1use super::ToSql;
2use super::traits::SqlGenerator;
3use crate::ast::*;
4
5#[derive(Debug, Default)]
7pub struct ParamContext {
8 pub index: usize,
9 pub params: Vec<Value>,
11 pub named_params: Vec<String>,
13}
14
15impl ParamContext {
16 pub fn new() -> Self {
17 Self {
18 index: 0,
19 params: Vec::new(),
20 named_params: Vec::new(),
21 }
22 }
23
24 pub fn add_param(&mut self, value: Value, generator: &dyn SqlGenerator) -> String {
26 self.index += 1;
27 self.params.push(value);
28 generator.placeholder(self.index)
29 }
30
31 pub fn add_named_param(&mut self, name: String, generator: &dyn SqlGenerator) -> String {
33 self.index += 1;
34 self.named_params.push(name);
35 generator.placeholder(self.index)
36 }
37}
38
39fn resolve_col_syntax(col: &str, cmd: &Qail, generator: &dyn SqlGenerator) -> String {
46 if col.starts_with('{') && col.ends_with('}') {
47 return col[1..col.len() - 1].to_string();
48 }
49
50 let parts: Vec<&str> = col.split('.').collect();
51 if parts.len() <= 1 {
52 return generator.quote_identifier(col);
53 }
54
55 let first = parts[0];
56
57 if first == cmd.table {
58 return format!(
60 "{}.{}",
61 generator.quote_identifier(first),
62 generator.quote_identifier(parts[1])
63 );
64 }
65
66 for join in &cmd.joins {
67 if first == join.table {
68 return format!(
70 "{}.{}",
71 generator.quote_identifier(first),
72 generator.quote_identifier(parts[1])
73 );
74 }
75 }
76
77 let col_name = parts[0];
79 let path = &parts[1..];
80 generator.json_access(col_name, path)
81}
82
83#[allow(clippy::borrowed_box)]
84pub trait ConditionToSql {
85 fn to_sql(&self, generator: &Box<dyn SqlGenerator>, context: Option<&Qail>) -> String;
86 fn to_value_sql(&self, generator: &Box<dyn SqlGenerator>) -> String;
87
88 fn to_sql_parameterized(
90 &self,
91 generator: &Box<dyn SqlGenerator>,
92 context: Option<&Qail>,
93 params: &mut ParamContext,
94 ) -> String;
95}
96
97impl ConditionToSql for Condition {
98 fn to_sql(&self, generator: &Box<dyn SqlGenerator>, context: Option<&Qail>) -> String {
100 let col = match &self.left {
101 Expr::Named(name) => {
102 if name.starts_with('{') && name.ends_with('}') {
103 name[1..name.len() - 1].to_string()
104 } else if let Some(cmd) = context {
105 resolve_col_syntax(name, cmd, generator.as_ref())
106 } else {
107 generator.quote_identifier(name)
108 }
109 }
110 Expr::JsonAccess {
111 column,
112 path_segments,
113 ..
114 } => {
115 let mut result = generator.quote_identifier(column);
116 for (path, as_text) in path_segments {
117 let op = if *as_text { "->>" } else { "->" };
118 if path.parse::<i64>().is_ok() {
119 result.push_str(&format!("{}{}", op, path));
120 } else {
121 result.push_str(&format!("{}'{}'", op, path));
122 }
123 }
124 result
125 }
126 expr => expr.to_string(),
127 };
128
129 if self.is_array_unnest {
130 let inner_condition = match self.op {
131 Operator::Eq => format!("_el = {}", self.to_value_sql(generator)),
132 Operator::Ne => format!("_el != {}", self.to_value_sql(generator)),
133 Operator::Gt => format!("_el > {}", self.to_value_sql(generator)),
134 Operator::Gte => format!("_el >= {}", self.to_value_sql(generator)),
135 Operator::Lt => format!("_el < {}", self.to_value_sql(generator)),
136 Operator::Lte => format!("_el <= {}", self.to_value_sql(generator)),
137 Operator::Fuzzy => {
138 let val = match &self.value {
139 Value::String(s) => format!("'%{}%'", s),
140 Value::Param(n) => {
141 let p = generator.placeholder(*n);
142 generator.string_concat(&["'%'", &p, "'%'"])
143 }
144 v => format!("'%{}%'", v),
145 };
146 format!("_el {} {}", generator.fuzzy_operator(), val)
147 }
148 _ => format!("_el = {}", self.to_value_sql(generator)),
149 };
150 return format!(
151 "EXISTS (SELECT 1 FROM unnest({}) _el WHERE {})",
152 col, inner_condition
153 );
154 }
155
156 if self.op.is_simple_binary() {
159 return format!(
160 "{} {} {}",
161 col,
162 self.op.sql_symbol(),
163 self.to_value_sql(generator)
164 );
165 }
166
167 match self.op {
169 Operator::Fuzzy => {
170 let val = match &self.value {
171 Value::String(s) => format!("'%{}%'", s),
172 Value::Param(n) => {
173 let p = generator.placeholder(*n);
174 generator.string_concat(&["'%'", &p, "'%'"])
175 }
176 v => format!("'%{}%'", v),
177 };
178 format!("{} {} {}", col, generator.fuzzy_operator(), val)
179 }
180 Operator::In => generator.in_array(&col, &format!("{}", self.value)),
181 Operator::NotIn => generator.not_in_array(&col, &format!("{}", self.value)),
182 Operator::IsNull => format!("{} IS NULL", col),
183 Operator::IsNotNull => format!("{} IS NOT NULL", col),
184 Operator::Contains => generator.json_contains(&col, &self.to_value_sql(generator)),
185 Operator::KeyExists => generator.json_key_exists(&col, &self.to_value_sql(generator)),
186 Operator::JsonExists => {
188 let path = self.to_value_sql(generator);
189 generator.json_exists(&col, path.trim_matches('\''))
190 }
191 Operator::JsonQuery => {
192 let path = self.to_value_sql(generator);
193 generator.json_query(&col, path.trim_matches('\''))
194 }
195 Operator::JsonValue => {
196 let path = self.to_value_sql(generator);
197 format!(
198 "{} = {}",
199 generator.json_value(&col, path.trim_matches('\'')),
200 self.to_value_sql(generator)
201 )
202 }
203 Operator::Between => {
204 if let Value::Array(vals) = &self.value
206 && vals.len() >= 2
207 {
208 return format!("{} BETWEEN {} AND {}", col, vals[0], vals[1]);
209 }
210 format!("{} BETWEEN {}", col, self.value)
211 }
212 Operator::NotBetween => {
213 if let Value::Array(vals) = &self.value
214 && vals.len() >= 2
215 {
216 return format!("{} NOT BETWEEN {} AND {}", col, vals[0], vals[1]);
217 }
218 format!("{} NOT BETWEEN {}", col, self.value)
219 }
220 Operator::Exists => {
221 if let Value::Subquery(cmd) = &self.value {
223 let subquery_sql = cmd.to_sql();
224 format!("EXISTS ({})", subquery_sql)
225 } else {
226 format!("EXISTS ({})", self.value)
227 }
228 }
229 Operator::NotExists => {
230 if let Value::Subquery(cmd) = &self.value {
231 let subquery_sql = cmd.to_sql();
232 format!("NOT EXISTS ({})", subquery_sql)
233 } else {
234 format!("NOT EXISTS ({})", self.value)
235 }
236 }
237 _ => format!(
239 "{} {} {}",
240 col,
241 self.op.sql_symbol(),
242 self.to_value_sql(generator)
243 ),
244 }
245 }
246
247 fn to_value_sql(&self, generator: &Box<dyn SqlGenerator>) -> String {
248 match &self.value {
249 Value::Param(n) => generator.placeholder(*n),
250 Value::String(s) => format!("'{}'", s.replace('\'', "''")),
251 Value::Bool(b) => generator.bool_literal(*b),
252 Value::Subquery(cmd) => {
253 use crate::transpiler::ToSql;
255 format!("({})", cmd.to_sql())
256 }
257 Value::Column(col) => {
258 if col.contains('.') {
268 let parts: Vec<&str> = col.split('.').collect();
269 parts
270 .iter()
271 .map(|p| generator.quote_identifier(p))
272 .collect::<Vec<String>>()
273 .join(".")
274 } else {
275 generator.quote_identifier(col)
276 }
277 }
278 v => v.to_string(),
279 }
280 }
281
282 fn to_sql_parameterized(
283 &self,
284 generator: &Box<dyn SqlGenerator>,
285 context: Option<&Qail>,
286 params: &mut ParamContext,
287 ) -> String {
288 let col = match &self.left {
289 Expr::Named(name) => {
290 if name.starts_with('{') && name.ends_with('}') {
291 name[1..name.len() - 1].to_string()
292 } else if let Some(cmd) = context {
293 resolve_col_syntax(name, cmd, generator.as_ref())
294 } else {
295 generator.quote_identifier(name)
296 }
297 }
298 Expr::JsonAccess {
299 column,
300 path_segments,
301 ..
302 } => {
303 let mut result = generator.quote_identifier(column);
304 for (path, as_text) in path_segments {
305 let op = if *as_text { "->>" } else { "->" };
306 if path.parse::<i64>().is_ok() {
307 result.push_str(&format!("{}{}", op, path));
308 } else {
309 result.push_str(&format!("{}'{}'", op, path));
310 }
311 }
312 result
313 }
314 expr => expr.to_string(),
315 };
316
317 let value_placeholder = |v: &Value, p: &mut ParamContext| -> String {
319 match v {
320 Value::Param(n) => generator.placeholder(*n), Value::NamedParam(name) => p.add_named_param(name.clone(), generator.as_ref()),
322 Value::Null => "NULL".to_string(),
323 other => p.add_param(other.clone(), generator.as_ref()),
324 }
325 };
326
327 match self.op {
328 Operator::Eq => {
329 if matches!(self.value, Value::Null)
331 && let Expr::Named(name) = &self.left
332 && name.starts_with('{')
333 && name.ends_with('}')
334 {
335 return col; }
337 format!("{} = {}", col, value_placeholder(&self.value, params))
338 }
339 Operator::Fuzzy => {
340 let placeholder = value_placeholder(&self.value, params);
342 format!("{} {} {}", col, generator.fuzzy_operator(), placeholder)
343 }
344 Operator::IsNull => format!("{} IS NULL", col),
345 Operator::IsNotNull => format!("{} IS NOT NULL", col),
346 Operator::In => generator.in_array(&col, &value_placeholder(&self.value, params)),
347 Operator::NotIn => {
348 generator.not_in_array(&col, &value_placeholder(&self.value, params))
349 }
350 Operator::Contains => {
351 generator.json_contains(&col, &value_placeholder(&self.value, params))
352 }
353 Operator::KeyExists => {
354 generator.json_key_exists(&col, &value_placeholder(&self.value, params))
355 }
356 Operator::JsonExists => {
357 let path = value_placeholder(&self.value, params);
358 generator.json_exists(&col, &path)
359 }
360 Operator::JsonQuery => {
361 let path = value_placeholder(&self.value, params);
362 generator.json_query(&col, &path)
363 }
364 Operator::JsonValue => {
365 let path = value_placeholder(&self.value, params);
366 format!(
367 "{} = {}",
368 generator.json_value(&col, &path),
369 value_placeholder(&self.value, params)
370 )
371 }
372 Operator::Between => {
373 if let Value::Array(vals) = &self.value
374 && vals.len() >= 2
375 {
376 return format!("{} BETWEEN {} AND {}", col, vals[0], vals[1]);
377 }
378 format!("{} BETWEEN {}", col, self.value)
379 }
380 Operator::NotBetween => {
381 if let Value::Array(vals) = &self.value
382 && vals.len() >= 2
383 {
384 return format!("{} NOT BETWEEN {} AND {}", col, vals[0], vals[1]);
385 }
386 format!("{} NOT BETWEEN {}", col, self.value)
387 }
388 Operator::Exists => {
389 if let Value::Subquery(cmd) = &self.value {
390 let subquery_sql = cmd.to_sql();
391 format!("EXISTS ({})", subquery_sql)
392 } else {
393 format!("EXISTS ({})", self.value)
394 }
395 }
396 Operator::NotExists => {
397 if let Value::Subquery(cmd) = &self.value {
398 let subquery_sql = cmd.to_sql();
399 format!("NOT EXISTS ({})", subquery_sql)
400 } else {
401 format!("NOT EXISTS ({})", self.value)
402 }
403 }
404 _ => format!(
406 "{} {} {}",
407 col,
408 self.op.sql_symbol(),
409 value_placeholder(&self.value, params)
410 ),
411 }
412 }
413}