Skip to main content

chain_builder/query/
common.rs

1//! Common query functionality for WHERE clauses and other query parts
2
3use crate::query::QueryBuilder;
4use crate::types::Common;
5use serde_json::Value;
6
7fn build_placeholders(len: usize) -> String {
8    if len == 0 {
9        return String::new();
10    }
11    let mut s = String::with_capacity(len * 3 - 2);
12    s.push('?');
13    for _ in 1..len {
14        s.push_str(", ?");
15    }
16    s
17}
18
19/// Trait for common query operations
20pub trait QueryCommon {
21    /// Add a WITH clause
22    fn with(&mut self, alias: &str, chain_builder: crate::builder::ChainBuilder);
23
24    /// Add a recursive WITH clause
25    fn with_recursive(&mut self, alias: &str, chain_builder: crate::builder::ChainBuilder);
26
27    /// Add a UNION clause
28    fn union(&mut self, chain_builder: crate::builder::ChainBuilder);
29
30    /// Add a UNION ALL clause
31    fn union_all(&mut self, chain_builder: crate::builder::ChainBuilder);
32
33    /// Add a LIMIT clause
34    fn limit(&mut self, limit: usize);
35
36    /// Add an OFFSET clause
37    fn offset(&mut self, offset: usize);
38
39    /// Add a GROUP BY clause
40    fn group_by(&mut self, columns: Vec<String>);
41
42    /// Add a raw GROUP BY clause
43    fn group_by_raw(&mut self, sql: &str, binds: Option<Vec<Value>>);
44
45    /// Add an ORDER BY clause
46    fn order_by(&mut self, column: &str, order: &str);
47
48    /// Add a raw ORDER BY clause
49    fn order_by_raw(&mut self, sql: &str, binds: Option<Vec<Value>>);
50}
51
52impl QueryCommon for QueryBuilder {
53    fn with(&mut self, alias: &str, chain_builder: crate::builder::ChainBuilder) {
54        self.query_common
55            .push(Common::With(alias.to_string(), false, chain_builder));
56    }
57
58    fn with_recursive(&mut self, alias: &str, chain_builder: crate::builder::ChainBuilder) {
59        self.query_common
60            .push(Common::With(alias.to_string(), true, chain_builder));
61    }
62
63    fn union(&mut self, chain_builder: crate::builder::ChainBuilder) {
64        self.query_common.push(Common::Union(false, chain_builder));
65    }
66
67    fn union_all(&mut self, chain_builder: crate::builder::ChainBuilder) {
68        self.query_common.push(Common::Union(true, chain_builder));
69    }
70
71    fn limit(&mut self, limit: usize) {
72        self.query_common.push(Common::Limit(limit));
73    }
74
75    fn offset(&mut self, offset: usize) {
76        self.query_common.push(Common::Offset(offset));
77    }
78
79    fn group_by(&mut self, columns: Vec<String>) {
80        self.query_common.push(Common::GroupBy(columns));
81    }
82
83    fn group_by_raw(&mut self, sql: &str, binds: Option<Vec<Value>>) {
84        self.query_common
85            .push(Common::GroupByRaw(sql.to_string(), binds));
86    }
87
88    fn order_by(&mut self, column: &str, order: &str) {
89        self.query_common
90            .push(Common::OrderBy(column.to_string(), order.to_string()));
91    }
92
93    fn order_by_raw(&mut self, sql: &str, binds: Option<Vec<Value>>) {
94        self.query_common
95            .push(Common::OrderByRaw(sql.to_string(), binds));
96    }
97}
98
99/// Trait for HAVING clause operations
100pub trait HavingClauses {
101    /// Add a HAVING condition
102    fn having(&mut self, column: &str, operator: &str, value: Value);
103
104    /// Add a HAVING condition with raw SQL
105    fn having_raw(&mut self, sql: &str, binds: Option<Vec<Value>>);
106
107    /// Add a HAVING BETWEEN condition
108    fn having_between(&mut self, column: &str, values: [Value; 2]);
109
110    /// Add a HAVING IN condition
111    fn having_in(&mut self, column: &str, values: Vec<Value>);
112
113    /// Add a HAVING NOT IN condition
114    fn having_not_in(&mut self, column: &str, values: Vec<Value>);
115}
116
117impl HavingClauses for QueryBuilder {
118    fn having(&mut self, column: &str, operator: &str, value: Value) {
119        // NOTE: HAVING operands are expression-oriented (e.g. `COUNT(*)`), so the
120        // column is treated as a raw expression and is NOT identifier-escaped.
121        // Do not pass untrusted input here; use bound `value`s for data.
122        let sql = format!("{} {} ?", column, operator);
123        self.query_common
124            .push(Common::Having(sql, Some(vec![value])));
125    }
126
127    fn having_raw(&mut self, sql: &str, binds: Option<Vec<Value>>) {
128        self.query_common
129            .push(Common::Having(sql.to_string(), binds));
130    }
131
132    fn having_between(&mut self, column: &str, values: [Value; 2]) {
133        // Expression-oriented; see `having` for the escaping rationale.
134        let sql = format!("{} BETWEEN ? AND ?", column);
135        self.query_common
136            .push(Common::Having(sql, Some(values.to_vec())));
137    }
138
139    fn having_in(&mut self, column: &str, values: Vec<Value>) {
140        if values.is_empty() {
141            self.query_common
142                .push(Common::Having("1 = 0".to_string(), None));
143            return;
144        }
145        let placeholders = build_placeholders(values.len());
146        let sql = format!("{} IN ({})", column, placeholders);
147        self.query_common.push(Common::Having(sql, Some(values)));
148    }
149
150    fn having_not_in(&mut self, column: &str, values: Vec<Value>) {
151        if values.is_empty() {
152            self.query_common
153                .push(Common::Having("1 = 1".to_string(), None));
154            return;
155        }
156        let placeholders = build_placeholders(values.len());
157        let sql = format!("{} NOT IN ({})", column, placeholders);
158        self.query_common.push(Common::Having(sql, Some(values)));
159    }
160}
161
162/// Trait for WHERE clause operations
163pub trait WhereClauses {
164    /// Add an equality condition
165    fn where_eq(&mut self, column: &str, value: Value);
166
167    /// Add a not equality condition
168    fn where_ne(&mut self, column: &str, value: Value);
169
170    /// Add an IN condition
171    fn where_in(&mut self, column: &str, values: Vec<Value>);
172
173    /// Add a NOT IN condition
174    fn where_not_in(&mut self, column: &str, values: Vec<Value>);
175
176    /// Add an IS NULL condition
177    fn where_null(&mut self, column: &str);
178
179    /// Add an IS NOT NULL condition
180    fn where_not_null(&mut self, column: &str);
181
182    /// Add a BETWEEN condition
183    fn where_between(&mut self, column: &str, values: [Value; 2]);
184
185    /// Add a NOT BETWEEN condition
186    fn where_not_between(&mut self, column: &str, values: [Value; 2]);
187
188    /// Add a LIKE condition
189    fn where_like(&mut self, column: &str, value: Value);
190
191    /// Add a NOT LIKE condition
192    fn where_not_like(&mut self, column: &str, value: Value);
193
194    /// Add a case-insensitive LIKE condition (ILIKE for Postgres, LOWER() for MySQL)
195    fn where_ilike(&mut self, column: &str, value: Value);
196
197    /// Add a greater than condition
198    fn where_gt(&mut self, column: &str, value: Value);
199
200    /// Add a greater than or equal condition
201    fn where_gte(&mut self, column: &str, value: Value);
202
203    /// Add a less than condition
204    fn where_lt(&mut self, column: &str, value: Value);
205
206    /// Add a less than or equal condition
207    fn where_lte(&mut self, column: &str, value: Value);
208
209    /// Add a column-to-column comparison
210    fn where_column(&mut self, lhs: &str, operator: &str, rhs: &str);
211
212    /// Add an EXISTS condition
213    fn where_exists(&mut self, query: impl FnOnce(&mut crate::builder::ChainBuilder));
214
215    /// Add a NOT EXISTS condition
216    fn where_not_exists(&mut self, query: impl FnOnce(&mut crate::builder::ChainBuilder));
217
218    /// Add a JSON contains condition (MySQL JSON_CONTAINS)
219    fn where_json_contains(&mut self, column: &str, value: Value);
220
221    /// Add a subquery condition
222    fn where_subquery(&mut self, query: impl FnOnce(&mut QueryBuilder));
223
224    /// Add an OR condition
225    fn or(&mut self) -> &mut QueryBuilder;
226
227    /// Add a raw WHERE condition
228    fn where_raw(&mut self, sql: &str, binds: Option<Vec<Value>>);
229}
230
231impl WhereClauses for QueryBuilder {
232    fn where_eq(&mut self, column: &str, value: Value) {
233        self.statement.push(crate::types::Statement::Value(
234            column.to_string(),
235            crate::query::Operator::Equal,
236            value,
237        ));
238    }
239
240    fn where_ne(&mut self, column: &str, value: Value) {
241        self.statement.push(crate::types::Statement::Value(
242            column.to_string(),
243            crate::query::Operator::NotEqual,
244            value,
245        ));
246    }
247
248    fn where_in(&mut self, column: &str, values: Vec<Value>) {
249        self.statement.push(crate::types::Statement::Value(
250            column.to_string(),
251            crate::query::Operator::In,
252            Value::Array(values),
253        ));
254    }
255
256    fn where_not_in(&mut self, column: &str, values: Vec<Value>) {
257        self.statement.push(crate::types::Statement::Value(
258            column.to_string(),
259            crate::query::Operator::NotIn,
260            Value::Array(values),
261        ));
262    }
263
264    fn where_null(&mut self, column: &str) {
265        self.statement.push(crate::types::Statement::Value(
266            column.to_string(),
267            crate::query::Operator::IsNull,
268            Value::Null,
269        ));
270    }
271
272    fn where_not_null(&mut self, column: &str) {
273        self.statement.push(crate::types::Statement::Value(
274            column.to_string(),
275            crate::query::Operator::IsNotNull,
276            Value::Null,
277        ));
278    }
279
280    fn where_between(&mut self, column: &str, values: [Value; 2]) {
281        self.statement.push(crate::types::Statement::Value(
282            column.to_string(),
283            crate::query::Operator::Between,
284            Value::Array(values.to_vec()),
285        ));
286    }
287
288    fn where_not_between(&mut self, column: &str, values: [Value; 2]) {
289        self.statement.push(crate::types::Statement::Value(
290            column.to_string(),
291            crate::query::Operator::NotBetween,
292            Value::Array(values.to_vec()),
293        ));
294    }
295
296    fn where_like(&mut self, column: &str, value: Value) {
297        self.statement.push(crate::types::Statement::Value(
298            column.to_string(),
299            crate::query::Operator::Like,
300            value,
301        ));
302    }
303
304    fn where_not_like(&mut self, column: &str, value: Value) {
305        self.statement.push(crate::types::Statement::Value(
306            column.to_string(),
307            crate::query::Operator::NotLike,
308            value,
309        ));
310    }
311
312    fn where_gt(&mut self, column: &str, value: Value) {
313        self.statement.push(crate::types::Statement::Value(
314            column.to_string(),
315            crate::query::Operator::GreaterThan,
316            value,
317        ));
318    }
319
320    fn where_gte(&mut self, column: &str, value: Value) {
321        self.statement.push(crate::types::Statement::Value(
322            column.to_string(),
323            crate::query::Operator::GreaterThanOrEqual,
324            value,
325        ));
326    }
327
328    fn where_lt(&mut self, column: &str, value: Value) {
329        self.statement.push(crate::types::Statement::Value(
330            column.to_string(),
331            crate::query::Operator::LessThan,
332            value,
333        ));
334    }
335
336    fn where_lte(&mut self, column: &str, value: Value) {
337        self.statement.push(crate::types::Statement::Value(
338            column.to_string(),
339            crate::query::Operator::LessThanOrEqual,
340            value,
341        ));
342    }
343
344    fn where_subquery(&mut self, query: impl FnOnce(&mut QueryBuilder)) {
345        let mut sub_query = QueryBuilder::new(self.client.clone());
346        query(&mut sub_query);
347        self.statement
348            .push(crate::types::Statement::SubChain(Box::new(sub_query)));
349    }
350
351    fn or(&mut self) -> &mut QueryBuilder {
352        let or_query = QueryBuilder::new(self.client.clone());
353        self.statement
354            .push(crate::types::Statement::OrChain(Box::new(or_query)));
355        self.statement.last_mut().unwrap().to_query_builder()
356    }
357
358    fn where_raw(&mut self, sql: &str, binds: Option<Vec<Value>>) {
359        self.statement
360            .push(crate::types::Statement::Raw((sql.to_string(), binds)));
361    }
362
363    fn where_ilike(&mut self, column: &str, value: Value) {
364        // For MySQL, use LOWER() function
365        let column = crate::dialect::escape_identifier(column, &self.client);
366        let sql = format!("LOWER({}) LIKE LOWER(?)", column);
367        self.statement
368            .push(crate::types::Statement::Raw((sql, Some(vec![value]))));
369    }
370
371    fn where_column(&mut self, lhs: &str, operator: &str, rhs: &str) {
372        let lhs = crate::dialect::escape_identifier(lhs, &self.client);
373        let rhs = crate::dialect::escape_identifier(rhs, &self.client);
374        let sql = format!("{} {} {}", lhs, operator, rhs);
375        self.statement
376            .push(crate::types::Statement::Raw((sql, None)));
377    }
378
379    fn where_exists(&mut self, query: impl FnOnce(&mut crate::builder::ChainBuilder)) {
380        let mut sub_builder = crate::builder::ChainBuilder::new(self.client.clone());
381        query(&mut sub_builder);
382        let (sub_sql, sub_binds) = sub_builder.to_sql();
383        let sql = format!("EXISTS ({})", sub_sql);
384        self.statement
385            .push(crate::types::Statement::Raw((sql, Some(sub_binds))));
386    }
387
388    fn where_not_exists(&mut self, query: impl FnOnce(&mut crate::builder::ChainBuilder)) {
389        let mut sub_builder = crate::builder::ChainBuilder::new(self.client.clone());
390        query(&mut sub_builder);
391        let (sub_sql, sub_binds) = sub_builder.to_sql();
392        let sql = format!("NOT EXISTS ({})", sub_sql);
393        self.statement
394            .push(crate::types::Statement::Raw((sql, Some(sub_binds))));
395    }
396
397    fn where_json_contains(&mut self, column: &str, value: Value) {
398        let column = crate::dialect::escape_identifier(column, &self.client);
399        let sql = format!("JSON_CONTAINS({}, ?)", column);
400        self.statement
401            .push(crate::types::Statement::Raw((sql, Some(vec![value]))));
402    }
403}