1use crate::{EntityDescriptor, SelectQuery, Value};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum BinaryOp {
5 Eq,
6 Ne,
7 Gt,
8 Gte,
9 Lt,
10 Lte,
11 Like,
12 NotLike,
13 In,
14 NotIn,
15 InLarge,
16 NotInLarge,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum ExprFunction {
21 Soundex,
22 Gbk,
23 Count,
24 Sum,
25 Avg,
26 Min,
27 Max,
28 Stddev,
29 StddevPop,
30 VarSamp,
31 VarPop,
32 BitAnd,
33 BitOr,
34 BitXor,
35}
36
37#[derive(Debug, Clone, PartialEq)]
38pub enum Expr {
39 Column(String),
40 Value(Value),
41 #[doc(hidden)]
45 LikePattern {
46 pattern: String,
47 original: String,
48 },
49 Function {
50 function: ExprFunction,
51 args: Vec<Expr>,
52 },
53 Binary {
54 left: Box<Expr>,
55 op: BinaryOp,
56 right: Box<Expr>,
57 },
58 SubQuery {
59 left: Box<Expr>,
60 op: BinaryOp,
61 entity: EntityDescriptor,
62 query: Box<SelectQuery>,
63 },
64 Between {
65 expr: Box<Expr>,
66 lower: Box<Expr>,
67 upper: Box<Expr>,
68 },
69 IsNull(Box<Expr>),
70 IsNotNull(Box<Expr>),
71 And(Vec<Expr>),
72 Or(Vec<Expr>),
73 Not(Box<Expr>),
74}
75
76impl Expr {
77 pub const LARGE_IN_THRESHOLD: usize = 20;
80
81 pub fn column(name: impl Into<String>) -> Self {
82 Self::Column(name.into())
83 }
84
85 pub fn value(value: impl Into<Value>) -> Self {
86 Self::Value(value.into())
87 }
88
89 pub fn function(function: ExprFunction, args: impl IntoIterator<Item = Expr>) -> Self {
90 Self::Function {
91 function,
92 args: args.into_iter().collect(),
93 }
94 }
95
96 pub fn soundex(expr: Expr) -> Self {
97 Self::function(ExprFunction::Soundex, [expr])
98 }
99
100 pub fn gbk(expr: Expr) -> Self {
101 Self::function(ExprFunction::Gbk, [expr])
102 }
103
104 pub fn count_all() -> Self {
105 Self::function(ExprFunction::Count, [])
106 }
107
108 pub fn count_expr(expr: Expr) -> Self {
109 Self::function(ExprFunction::Count, [expr])
110 }
111
112 pub fn sum_expr(expr: Expr) -> Self {
113 Self::function(ExprFunction::Sum, [expr])
114 }
115
116 pub fn avg_expr(expr: Expr) -> Self {
117 Self::function(ExprFunction::Avg, [expr])
118 }
119
120 pub fn min_expr(expr: Expr) -> Self {
121 Self::function(ExprFunction::Min, [expr])
122 }
123
124 pub fn max_expr(expr: Expr) -> Self {
125 Self::function(ExprFunction::Max, [expr])
126 }
127
128 pub fn stddev_expr(expr: Expr) -> Self {
129 Self::function(ExprFunction::Stddev, [expr])
130 }
131
132 pub fn stddev_pop_expr(expr: Expr) -> Self {
133 Self::function(ExprFunction::StddevPop, [expr])
134 }
135
136 pub fn var_samp_expr(expr: Expr) -> Self {
137 Self::function(ExprFunction::VarSamp, [expr])
138 }
139
140 pub fn var_pop_expr(expr: Expr) -> Self {
141 Self::function(ExprFunction::VarPop, [expr])
142 }
143
144 pub fn bit_and_expr(expr: Expr) -> Self {
145 Self::function(ExprFunction::BitAnd, [expr])
146 }
147
148 pub fn bit_or_expr(expr: Expr) -> Self {
149 Self::function(ExprFunction::BitOr, [expr])
150 }
151
152 pub fn bit_xor_expr(expr: Expr) -> Self {
153 Self::function(ExprFunction::BitXor, [expr])
154 }
155
156 pub fn sound_like(column: impl Into<String>, value: impl Into<Value>) -> Self {
157 Self::binary(
158 Self::soundex(Self::column(column)),
159 BinaryOp::Eq,
160 Self::soundex(Self::value(value)),
161 )
162 }
163
164 pub fn eq(column: impl Into<String>, value: impl Into<Value>) -> Self {
165 Self::binary(Self::column(column), BinaryOp::Eq, Self::value(value))
166 }
167
168 pub fn ne(column: impl Into<String>, value: impl Into<Value>) -> Self {
169 Self::binary(Self::column(column), BinaryOp::Ne, Self::value(value))
170 }
171
172 pub fn gt(column: impl Into<String>, value: impl Into<Value>) -> Self {
173 Self::binary(Self::column(column), BinaryOp::Gt, Self::value(value))
174 }
175
176 pub fn gte(column: impl Into<String>, value: impl Into<Value>) -> Self {
177 Self::binary(Self::column(column), BinaryOp::Gte, Self::value(value))
178 }
179
180 pub fn lt(column: impl Into<String>, value: impl Into<Value>) -> Self {
181 Self::binary(Self::column(column), BinaryOp::Lt, Self::value(value))
182 }
183
184 pub fn lte(column: impl Into<String>, value: impl Into<Value>) -> Self {
185 Self::binary(Self::column(column), BinaryOp::Lte, Self::value(value))
186 }
187
188 pub fn like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
189 Self::binary(
190 Self::column(column),
191 BinaryOp::Like,
192 Self::value(pattern.into()),
193 )
194 }
195
196 pub fn not_like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
197 Self::binary(
198 Self::column(column),
199 BinaryOp::NotLike,
200 Self::value(pattern.into()),
201 )
202 }
203
204 pub fn contain(column: impl Into<String>, value: impl Into<String>) -> Self {
205 Self::typed_like(column, value.into(), BinaryOp::Like, "%", "%")
206 }
207
208 pub fn not_contain(column: impl Into<String>, value: impl Into<String>) -> Self {
209 Self::typed_like(column, value.into(), BinaryOp::NotLike, "%", "%")
210 }
211
212 pub fn begin_with(column: impl Into<String>, value: impl Into<String>) -> Self {
213 Self::typed_like(column, value.into(), BinaryOp::Like, "", "%")
214 }
215
216 pub fn not_begin_with(column: impl Into<String>, value: impl Into<String>) -> Self {
217 Self::typed_like(column, value.into(), BinaryOp::NotLike, "", "%")
218 }
219
220 pub fn end_with(column: impl Into<String>, value: impl Into<String>) -> Self {
221 Self::typed_like(column, value.into(), BinaryOp::Like, "%", "")
222 }
223
224 pub fn not_end_with(column: impl Into<String>, value: impl Into<String>) -> Self {
225 Self::typed_like(column, value.into(), BinaryOp::NotLike, "%", "")
226 }
227
228 fn typed_like(
229 column: impl Into<String>,
230 original: String,
231 op: BinaryOp,
232 prefix: &str,
233 suffix: &str,
234 ) -> Self {
235 Self::binary(
236 Self::column(column),
237 op,
238 Self::LikePattern {
239 pattern: format!("{prefix}{original}{suffix}"),
240 original,
241 },
242 )
243 }
244
245 pub fn binary(left: Expr, op: BinaryOp, right: Expr) -> Self {
246 Self::Binary {
247 left: Box::new(left),
248 op,
249 right: Box::new(right),
250 }
251 }
252
253 pub fn compare_columns(
254 left_column: impl Into<String>,
255 op: BinaryOp,
256 right_column: impl Into<String>,
257 ) -> Self {
258 Self::binary(Self::column(left_column), op, Self::column(right_column))
259 }
260
261 pub fn in_list(column: impl Into<String>, values: impl IntoIterator<Item = Value>) -> Self {
262 let values = values.into_iter().collect::<Vec<_>>();
263 Self::binary(
264 Self::column(column),
265 if values.len() > Self::LARGE_IN_THRESHOLD {
266 BinaryOp::InLarge
267 } else {
268 BinaryOp::In
269 },
270 Self::Value(Value::List(values)),
271 )
272 }
273
274 pub fn not_in_list(column: impl Into<String>, values: impl IntoIterator<Item = Value>) -> Self {
275 let values = values.into_iter().collect::<Vec<_>>();
276 Self::binary(
277 Self::column(column),
278 if values.len() > Self::LARGE_IN_THRESHOLD {
279 BinaryOp::NotInLarge
280 } else {
281 BinaryOp::NotIn
282 },
283 Self::Value(Value::List(values)),
284 )
285 }
286
287 pub fn in_large(column: impl Into<String>, values: impl IntoIterator<Item = Value>) -> Self {
288 Self::binary(
289 Self::column(column),
290 BinaryOp::InLarge,
291 Self::Value(Value::List(values.into_iter().collect())),
292 )
293 }
294
295 pub fn not_in_large(
296 column: impl Into<String>,
297 values: impl IntoIterator<Item = Value>,
298 ) -> Self {
299 Self::binary(
300 Self::column(column),
301 BinaryOp::NotInLarge,
302 Self::Value(Value::List(values.into_iter().collect())),
303 )
304 }
305
306 pub fn in_subquery(
307 column: impl Into<String>,
308 entity: EntityDescriptor,
309 query: SelectQuery,
310 field: impl Into<String>,
311 ) -> Self {
312 Self::subquery(Self::column(column), BinaryOp::In, entity, query, field)
313 }
314
315 pub fn not_in_subquery(
316 column: impl Into<String>,
317 entity: EntityDescriptor,
318 query: SelectQuery,
319 field: impl Into<String>,
320 ) -> Self {
321 Self::subquery(Self::column(column), BinaryOp::NotIn, entity, query, field)
322 }
323
324 pub fn subquery(
325 left: Expr,
326 op: BinaryOp,
327 entity: EntityDescriptor,
328 mut query: SelectQuery,
329 field: impl Into<String>,
330 ) -> Self {
331 let field = field.into();
332 if matches!(op, BinaryOp::NotIn | BinaryOp::NotInLarge) {
336 query = query.and_filter(Self::is_not_null(field.clone()));
337 }
338 query.projection = vec![field];
339 Self::SubQuery {
340 left: Box::new(left),
341 op,
342 entity,
343 query: Box::new(query),
344 }
345 }
346
347 pub fn between(
348 column: impl Into<String>,
349 lower: impl Into<Value>,
350 upper: impl Into<Value>,
351 ) -> Self {
352 Self::Between {
353 expr: Box::new(Self::column(column)),
354 lower: Box::new(Self::value(lower)),
355 upper: Box::new(Self::value(upper)),
356 }
357 }
358
359 pub fn is_null(column: impl Into<String>) -> Self {
360 Self::IsNull(Box::new(Self::column(column)))
361 }
362
363 pub fn is_not_null(column: impl Into<String>) -> Self {
364 Self::IsNotNull(Box::new(Self::column(column)))
365 }
366
367 pub fn and(parts: impl IntoIterator<Item = Expr>) -> Self {
368 let mut unique = Vec::new();
369 for part in parts {
370 if !unique.contains(&part) {
371 unique.push(part);
372 }
373 }
374 Self::And(unique)
375 }
376
377 pub fn or(parts: impl IntoIterator<Item = Expr>) -> Self {
378 let mut unique = Vec::new();
379 for part in parts {
380 if !unique.contains(&part) {
381 unique.push(part);
382 }
383 }
384 Self::Or(unique)
385 }
386
387 pub fn negate(expr: Expr) -> Self {
388 Self::Not(Box::new(expr))
389 }
390
391 pub fn and_expr(self, other: Expr) -> Self {
392 if self == other {
393 return self;
394 }
395 match self {
396 Self::And(mut parts) => {
397 if !parts.contains(&other) {
398 parts.push(other);
399 }
400 Self::And(parts)
401 }
402 expr => Self::And(vec![expr, other]),
403 }
404 }
405
406 pub fn or_expr(self, other: Expr) -> Self {
407 if self == other {
408 return self;
409 }
410 match self {
411 Self::Or(mut parts) => {
412 if !parts.contains(&other) {
413 parts.push(other);
414 }
415 Self::Or(parts)
416 }
417 expr => Self::Or(vec![expr, other]),
418 }
419 }
420}