1use crate::ast::*;
2use super::traits::SqlGenerator;
3
4#[derive(Debug, Default)]
6pub struct ParamContext {
7 pub index: usize,
9 pub params: Vec<Value>,
11}
12
13impl ParamContext {
14 pub fn new() -> Self {
15 Self { index: 0, params: Vec::new() }
16 }
17
18 pub fn add_param(&mut self, value: Value, generator: &dyn SqlGenerator) -> String {
20 self.index += 1;
21 self.params.push(value);
22 generator.placeholder(self.index)
23 }
24}
25
26fn resolve_col_syntax(col: &str, cmd: &QailCmd, generator: &dyn SqlGenerator) -> String {
35 let parts: Vec<&str> = col.split('.').collect();
36 if parts.len() <= 1 {
37 return generator.quote_identifier(col);
38 }
39
40 let first = parts[0];
41
42 if first == cmd.table {
44 return format!("{}.{}", generator.quote_identifier(first), generator.quote_identifier(parts[1]));
46 }
47
48 for join in &cmd.joins {
50 if first == join.table {
51 return format!("{}.{}", generator.quote_identifier(first), generator.quote_identifier(parts[1]));
53 }
54 }
55
56 let col_name = parts[0];
58 let path = &parts[1..];
59 generator.json_access(col_name, path)
60}
61
62pub trait ConditionToSql {
63 fn to_sql(&self, generator: &Box<dyn SqlGenerator>, context: Option<&QailCmd>) -> String;
64 fn to_value_sql(&self, generator: &Box<dyn SqlGenerator>) -> String;
65
66 fn to_sql_parameterized(
69 &self,
70 generator: &Box<dyn SqlGenerator>,
71 context: Option<&QailCmd>,
72 params: &mut ParamContext
73 ) -> String;
74}
75
76impl ConditionToSql for Condition {
77 fn to_sql(&self, generator: &Box<dyn SqlGenerator>, context: Option<&QailCmd>) -> String {
79 let col = if let Some(cmd) = context {
80 resolve_col_syntax(&self.column, cmd, generator.as_ref())
81 } else {
82 generator.quote_identifier(&self.column)
83 };
84
85 if self.is_array_unnest {
87 let inner_condition = match self.op {
88 Operator::Eq => format!("_el = {}", self.to_value_sql(generator)),
89 Operator::Ne => format!("_el != {}", self.to_value_sql(generator)),
90 Operator::Gt => format!("_el > {}", self.to_value_sql(generator)),
91 Operator::Gte => format!("_el >= {}", self.to_value_sql(generator)),
92 Operator::Lt => format!("_el < {}", self.to_value_sql(generator)),
93 Operator::Lte => format!("_el <= {}", self.to_value_sql(generator)),
94 Operator::Fuzzy => {
95 let val = match &self.value {
96 Value::String(s) => format!("'%{}%'", s),
97 Value::Param(n) => {
98 let p = generator.placeholder(*n);
99 generator.string_concat(&["'%'", &p, "'%'"])
100 },
101 v => format!("'%{}%'", v),
102 };
103 format!("_el {} {}", generator.fuzzy_operator(), val)
104 }
105 _ => format!("_el = {}", self.to_value_sql(generator)),
106 };
107 return format!(
108 "EXISTS (SELECT 1 FROM unnest({}) _el WHERE {})",
109 col, inner_condition
110 );
111 }
112
113 match self.op {
115 Operator::Eq => format!("{} = {}", col, self.to_value_sql(generator)),
116 Operator::Ne => format!("{} != {}", col, self.to_value_sql(generator)),
117 Operator::Gt => format!("{} > {}", col, self.to_value_sql(generator)),
118 Operator::Gte => format!("{} >= {}", col, self.to_value_sql(generator)),
119 Operator::Lt => format!("{} < {}", col, self.to_value_sql(generator)),
120 Operator::Lte => format!("{} <= {}", col, self.to_value_sql(generator)),
121 Operator::Fuzzy => {
122 let val = match &self.value {
123 Value::String(s) => format!("'%{}%'", s),
124 Value::Param(n) => {
125 let p = generator.placeholder(*n);
126 generator.string_concat(&["'%'", &p, "'%'"])
127 },
128 v => format!("'%{}%'", v),
129 };
130 format!("{} {} {}", col, generator.fuzzy_operator(), val)
131 }
132 Operator::In => format!("{} = ANY({})", col, self.value), Operator::NotIn => format!("{} != ALL({})", col, self.value),
134 Operator::IsNull => format!("{} IS NULL", col),
135 Operator::IsNotNull => format!("{} IS NOT NULL", col),
136 Operator::Contains => generator.json_contains(&col, &self.to_value_sql(generator)),
137 Operator::KeyExists => generator.json_key_exists(&col, &self.to_value_sql(generator)),
138 Operator::JsonExists => {
140 let path = self.to_value_sql(generator);
141 generator.json_exists(&col, &path.trim_matches('\''))
142 }
143 Operator::JsonQuery => {
144 let path = self.to_value_sql(generator);
145 generator.json_query(&col, &path.trim_matches('\''))
146 }
147 Operator::JsonValue => {
148 let path = self.to_value_sql(generator);
149 format!("{} = {}", generator.json_value(&col, &path.trim_matches('\'')), self.to_value_sql(generator))
150 }
151 }
152 }
153
154 fn to_value_sql(&self, generator: &Box<dyn SqlGenerator>) -> String {
155 match &self.value {
156 Value::Param(n) => generator.placeholder(*n),
157 Value::String(s) => format!("'{}'", s.replace('\'', "''")),
158 Value::Bool(b) => generator.bool_literal(*b),
159 Value::Subquery(cmd) => {
160 use crate::transpiler::ToSql;
162 format!("({})", cmd.to_sql())
163 }
164 v => v.to_string(),
165 }
166 }
167
168 fn to_sql_parameterized(
169 &self,
170 generator: &Box<dyn SqlGenerator>,
171 context: Option<&QailCmd>,
172 params: &mut ParamContext
173 ) -> String {
174 let col = if let Some(cmd) = context {
175 resolve_col_syntax(&self.column, cmd, generator.as_ref())
176 } else {
177 generator.quote_identifier(&self.column)
178 };
179
180 let value_placeholder = |v: &Value, p: &mut ParamContext| -> String {
182 match v {
183 Value::Param(n) => generator.placeholder(*n), Value::Null => "NULL".to_string(),
185 other => p.add_param(other.clone(), generator.as_ref()),
186 }
187 };
188
189 match self.op {
190 Operator::Eq => format!("{} = {}", col, value_placeholder(&self.value, params)),
191 Operator::Ne => format!("{} != {}", col, value_placeholder(&self.value, params)),
192 Operator::Gt => format!("{} > {}", col, value_placeholder(&self.value, params)),
193 Operator::Gte => format!("{} >= {}", col, value_placeholder(&self.value, params)),
194 Operator::Lt => format!("{} < {}", col, value_placeholder(&self.value, params)),
195 Operator::Lte => format!("{} <= {}", col, value_placeholder(&self.value, params)),
196 Operator::Fuzzy => {
197 let placeholder = value_placeholder(&self.value, params);
199 format!("{} {} {}", col, generator.fuzzy_operator(), placeholder)
200 }
201 Operator::IsNull => format!("{} IS NULL", col),
202 Operator::IsNotNull => format!("{} IS NOT NULL", col),
203 Operator::In => format!("{} = ANY({})", col, value_placeholder(&self.value, params)),
204 Operator::NotIn => format!("{} != ALL({})", col, value_placeholder(&self.value, params)),
205 Operator::Contains => generator.json_contains(&col, &value_placeholder(&self.value, params)),
206 Operator::KeyExists => generator.json_key_exists(&col, &value_placeholder(&self.value, params)),
207 Operator::JsonExists => {
208 let path = value_placeholder(&self.value, params);
209 generator.json_exists(&col, &path)
210 }
211 Operator::JsonQuery => {
212 let path = value_placeholder(&self.value, params);
213 generator.json_query(&col, &path)
214 }
215 Operator::JsonValue => {
216 let path = value_placeholder(&self.value, params);
217 format!("{} = {}", generator.json_value(&col, &path), value_placeholder(&self.value, params))
218 }
219 }
220 }
221}