sea_query/query/
condition.rs

1use crate::{ExprTrait, expr::Expr, types::LogicalChainOper};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum ConditionType {
5    Any,
6    All,
7}
8
9/// Represents the value of an [`Condition::any`] or [`Condition::all`]: a set of disjunctive or conjunctive conditions.
10#[derive(Debug, Clone, PartialEq)]
11pub struct Condition {
12    pub(crate) negate: bool,
13    pub(crate) condition_type: ConditionType,
14    pub(crate) conditions: Vec<ConditionExpression>,
15}
16
17pub type Cond = Condition;
18
19impl From<Expr> for Condition {
20    fn from(expr: Expr) -> Self {
21        Condition {
22            negate: false,
23            condition_type: ConditionType::All,
24            conditions: vec![ConditionExpression::Expr(expr)],
25        }
26    }
27}
28
29impl From<ConditionExpression> for Condition {
30    fn from(ce: ConditionExpression) -> Self {
31        Condition {
32            negate: false,
33            condition_type: ConditionType::All,
34            conditions: vec![ce],
35        }
36    }
37}
38
39/// A helper trait.
40///
41/// You shouldn't implement this manually.
42pub trait IntoCondition: Into<Condition> {
43    #[inline(always)]
44    fn into_condition(self) -> Condition {
45        self.into()
46    }
47}
48
49impl<T> IntoCondition for T where T: Into<Condition> {}
50
51/// An internal representation of conditions.
52/// May be refactored away in the future if we can get our head around it.
53///
54/// It used to be in the public interface of [`Condition::add`] and [`Condition::add_option`],
55/// in order to accept anything resembling a condition or an expression.
56/// Nowadays, we achieve that with traits.
57#[derive(Debug, Clone, PartialEq)]
58pub(crate) enum ConditionExpression {
59    Condition(Condition),
60    Expr(Expr),
61}
62
63#[derive(Default, Debug, Clone, PartialEq)]
64pub enum ConditionHolderContents {
65    #[default]
66    Empty,
67    Chain(Vec<LogicalChainOper>),
68    Condition(Condition),
69}
70
71#[derive(Default, Debug, Clone, PartialEq)]
72pub struct ConditionHolder {
73    pub contents: ConditionHolderContents,
74}
75
76impl Condition {
77    /// Add a condition to the set.
78    ///
79    /// If it's an [`Condition::any`], it will be separated from the others by an `" OR "` in the query. If it's
80    /// an [`Condition::all`], it will be separated by an `" AND "`.
81    ///
82    /// ```
83    /// use sea_query::{tests_cfg::*, *};
84    ///
85    /// let statement = Query::select()
86    ///     .column(Glyph::Id)
87    ///     .from(Glyph::Table)
88    ///     .cond_where(
89    ///         Cond::all()
90    ///             .add(Expr::col(Glyph::Aspect).eq(0).into_condition().not())
91    ///             .add(Expr::col(Glyph::Id).eq(0).into_condition().not()),
92    ///     )
93    ///     .to_string(PostgresQueryBuilder);
94    /// assert_eq!(
95    ///     statement,
96    ///     r#"SELECT "id" FROM "glyph" WHERE (NOT "aspect" = 0) AND (NOT "id" = 0)"#
97    /// );
98    /// ```
99    #[allow(clippy::should_implement_trait)]
100    pub fn add<C>(mut self, condition: C) -> Self
101    where
102        C: Into<Condition>,
103    {
104        let condition: Condition = condition.into();
105        let condition: ConditionExpression = condition.into();
106        self.conditions.push(condition);
107        self
108    }
109
110    /// Add an optional condition to the set.
111    ///
112    /// Shorthand for `if o.is_some() { self.add(o) }`
113    ///
114    /// # Examples
115    ///
116    /// ```
117    /// use sea_query::{tests_cfg::*, *};
118    ///
119    /// let query = Query::select()
120    ///     .column(Glyph::Image)
121    ///     .from(Glyph::Table)
122    ///     .cond_where(
123    ///         Cond::all()
124    ///             .add_option(Some(Expr::col((Glyph::Table, Glyph::Image)).like("A%")))
125    ///             .add_option(None::<Expr>),
126    ///     )
127    ///     .to_owned();
128    ///
129    /// assert_eq!(
130    ///     query.to_string(MysqlQueryBuilder),
131    ///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`image` LIKE 'A%'"#
132    /// );
133    /// ```
134    #[allow(clippy::should_implement_trait)]
135    pub fn add_option<C>(self, other: Option<C>) -> Self
136    where
137        C: Into<Condition>,
138    {
139        if let Some(other) = other {
140            self.add(other)
141        } else {
142            self
143        }
144    }
145
146    /// Create a condition that is true if any of the conditions is true.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use sea_query::{*, tests_cfg::*};
152    ///
153    /// let query = Query::select()
154    ///     .column(Glyph::Image)
155    ///     .from(Glyph::Table)
156    ///     .cond_where(
157    ///         Cond::any()
158    ///             .add(Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]))
159    ///             .add(Expr::col((Glyph::Table, Glyph::Image)).like("A%"))
160    ///     )
161    ///     .to_owned();
162    ///
163    /// assert_eq!(
164    ///     query.to_string(MysqlQueryBuilder),
165    ///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`aspect` IN (3, 4) OR `glyph`.`image` LIKE 'A%'"#
166    /// );
167    /// ```
168    pub fn any() -> Condition {
169        Condition {
170            negate: false,
171            condition_type: ConditionType::Any,
172            conditions: Vec::new(),
173        }
174    }
175
176    /// Create a condition that is false if any of the conditions is false.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use sea_query::{*, tests_cfg::*};
182    ///
183    /// let query = Query::select()
184    ///     .column(Glyph::Image)
185    ///     .from(Glyph::Table)
186    ///     .cond_where(
187    ///         Cond::all()
188    ///             .add(Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]))
189    ///             .add(Expr::col((Glyph::Table, Glyph::Image)).like("A%"))
190    ///     )
191    ///     .to_owned();
192    ///
193    /// assert_eq!(
194    ///     query.to_string(MysqlQueryBuilder),
195    ///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`aspect` IN (3, 4) AND `glyph`.`image` LIKE 'A%'"#
196    /// );
197    /// ```
198    pub fn all() -> Condition {
199        Condition {
200            negate: false,
201            condition_type: ConditionType::All,
202            conditions: Vec::new(),
203        }
204    }
205
206    /// Negates a condition.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// use sea_query::{tests_cfg::*, *};
212    ///
213    /// let query = Query::select()
214    ///     .column(Glyph::Image)
215    ///     .from(Glyph::Table)
216    ///     .cond_where(
217    ///         Cond::all()
218    ///             .not()
219    ///             .add(Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]))
220    ///             .add(Expr::col((Glyph::Table, Glyph::Image)).like("A%"))
221    ///     )
222    ///     .to_owned();
223    ///
224    /// assert_eq!(
225    ///     query.to_string(MysqlQueryBuilder),
226    ///     r#"SELECT `image` FROM `glyph` WHERE NOT (`glyph`.`aspect` IN (3, 4) AND `glyph`.`image` LIKE 'A%')"#
227    /// );
228    /// ```
229    ///
230    /// # More Examples
231    ///
232    /// ```
233    /// use sea_query::{tests_cfg::*, *};
234    ///
235    /// let query = Query::select()
236    ///     .column(Glyph::Id)
237    ///     .cond_where(
238    ///         Cond::all()
239    ///             .add(
240    ///                 Cond::all()
241    ///                     .not()
242    ///                     .add(Expr::val(1).eq(1))
243    ///                     .add(Expr::val(2).eq(2)),
244    ///             )
245    ///             .add(Cond::any().add(Expr::val(3).eq(3)).add(Expr::val(4).eq(4))),
246    ///     )
247    ///     .to_owned();
248    ///
249    /// assert_eq!(
250    ///     query.to_string(MysqlQueryBuilder),
251    ///     r#"SELECT `id` WHERE (NOT (1 = 1 AND 2 = 2)) AND (3 = 3 OR 4 = 4)"#
252    /// );
253    /// ```
254    #[allow(clippy::should_implement_trait)]
255    pub fn not(mut self) -> Self {
256        self.negate = !self.negate;
257        self
258    }
259
260    /// Whether or not any condition has been added
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// use sea_query::{tests_cfg::*, *};
266    ///
267    /// let is_empty = Cond::all().is_empty();
268    ///
269    /// assert!(is_empty);
270    /// ```
271    pub fn is_empty(&self) -> bool {
272        self.conditions.is_empty()
273    }
274
275    /// How many conditions were added
276    ///
277    /// # Examples
278    ///
279    /// ```
280    /// use sea_query::{tests_cfg::*, *};
281    ///
282    /// let len = Cond::all().len();
283    ///
284    /// assert_eq!(len, 0);
285    /// ```
286    pub fn len(&self) -> usize {
287        self.conditions.len()
288    }
289}
290
291impl From<Condition> for Expr {
292    fn from(cond: Condition) -> Self {
293        let mut inner_exprs = vec![];
294        for ce in cond.conditions {
295            inner_exprs.push(match ce {
296                ConditionExpression::Condition(c) => c.into(),
297                ConditionExpression::Expr(e) => e,
298            });
299        }
300        let mut inner_exprs_into_iter = inner_exprs.into_iter();
301        let expr = if let Some(first_expr) = inner_exprs_into_iter.next() {
302            let mut out_expr = first_expr;
303            for e in inner_exprs_into_iter {
304                out_expr = match cond.condition_type {
305                    ConditionType::Any => out_expr.or(e),
306                    ConditionType::All => out_expr.and(e),
307                };
308            }
309            out_expr
310        } else {
311            Expr::Constant(match cond.condition_type {
312                ConditionType::Any => false.into(),
313                ConditionType::All => true.into(),
314            })
315        };
316        if cond.negate { expr.not() } else { expr }
317    }
318}
319
320impl From<ConditionExpression> for Expr {
321    fn from(ce: ConditionExpression) -> Self {
322        match ce {
323            ConditionExpression::Condition(c) => c.into(),
324            ConditionExpression::Expr(e) => e,
325        }
326    }
327}
328
329impl From<Condition> for ConditionExpression {
330    fn from(condition: Condition) -> Self {
331        ConditionExpression::Condition(condition)
332    }
333}
334
335impl From<Expr> for ConditionExpression {
336    fn from(condition: Expr) -> Self {
337        ConditionExpression::Expr(condition)
338    }
339}
340
341/// Macro to easily create an [`Condition::any`].
342///
343/// # Examples
344///
345/// ```
346/// use sea_query::{*, tests_cfg::*};
347///
348/// let query = Query::select()
349///     .column(Glyph::Image)
350///     .from(Glyph::Table)
351///     .cond_where(
352///         any![
353///             Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]),
354///             Expr::col((Glyph::Table, Glyph::Image)).like("A%")
355///         ]
356///     )
357///     .to_owned();
358///
359/// assert_eq!(
360///     query.to_string(MysqlQueryBuilder),
361///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`aspect` IN (3, 4) OR `glyph`.`image` LIKE 'A%'"#
362/// );
363/// ```
364#[macro_export]
365macro_rules! any {
366    ( $( $x:expr ),* $(,)?) => {
367        {
368            let mut tmp = $crate::Condition::any();
369            $(
370                tmp = tmp.add($x);
371            )*
372            tmp
373        }
374    };
375}
376
377/// Macro to easily create an [`Condition::all`].
378///
379/// # Examples
380///
381/// ```
382/// use sea_query::{*, tests_cfg::*};
383///
384/// let query = Query::select()
385///     .column(Glyph::Image)
386///     .from(Glyph::Table)
387///     .cond_where(
388///         all![
389///             Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]),
390///             Expr::col((Glyph::Table, Glyph::Image)).like("A%")
391///         ]
392///     )
393///     .to_owned();
394///
395/// assert_eq!(
396///     query.to_string(MysqlQueryBuilder),
397///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`aspect` IN (3, 4) AND `glyph`.`image` LIKE 'A%'"#
398/// );
399#[macro_export]
400macro_rules! all {
401    ( $( $x:expr ),* $(,)?) => {
402        {
403            let mut tmp = $crate::Condition::all();
404            $(
405                tmp = tmp.add($x);
406            )*
407            tmp
408        }
409    };
410}
411
412pub trait ConditionalStatement {
413    /// And where condition.
414    /// Calling `or_where` after `and_where` will panic.
415    ///
416    /// # Examples
417    ///
418    /// ```
419    /// use sea_query::{*, tests_cfg::*};
420    ///
421    /// let query = Query::select()
422    ///     .column(Glyph::Image)
423    ///     .from(Glyph::Table)
424    ///     .and_where(Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]))
425    ///     .and_where(Expr::col((Glyph::Table, Glyph::Image)).like("A%"))
426    ///     .to_owned();
427    ///
428    /// assert_eq!(
429    ///     query.to_string(MysqlQueryBuilder),
430    ///     r#"SELECT `image` FROM `glyph` WHERE `glyph`.`aspect` IN (3, 4) AND `glyph`.`image` LIKE 'A%'"#
431    /// );
432    /// ```
433    fn and_where(&mut self, other: Expr) -> &mut Self {
434        self.cond_where(other)
435    }
436
437    /// Optional and where, short hand for `if c.is_some() q.and_where(c)`.
438    ///
439    /// ```
440    /// use sea_query::{tests_cfg::*, *};
441    ///
442    /// let query = Query::select()
443    ///     .column(Glyph::Image)
444    ///     .from(Glyph::Table)
445    ///     .and_where(Expr::col(Glyph::Aspect).is_in([3, 4]))
446    ///     .and_where_option(Some(Expr::col(Glyph::Image).like("A%")))
447    ///     .and_where_option(None)
448    ///     .to_owned();
449    ///
450    /// assert_eq!(
451    ///     query.to_string(MysqlQueryBuilder),
452    ///     r#"SELECT `image` FROM `glyph` WHERE `aspect` IN (3, 4) AND `image` LIKE 'A%'"#
453    /// );
454    /// ```
455    fn and_where_option(&mut self, other: Option<Expr>) -> &mut Self {
456        if let Some(other) = other {
457            self.and_where(other);
458        }
459        self
460    }
461
462    #[doc(hidden)]
463    // Trait implementation.
464    fn and_or_where(&mut self, condition: LogicalChainOper) -> &mut Self;
465
466    /// Where condition, expressed with `any` and `all`.
467    /// Calling `cond_where` multiple times will conjoin them.
468    /// Calling `or_where` after `cond_where` will panic.
469    ///
470    /// # Examples
471    ///
472    /// ```
473    /// use sea_query::{*, tests_cfg::*};
474    ///
475    /// let query = Query::select()
476    ///     .column(Glyph::Image)
477    ///     .from(Glyph::Table)
478    ///     .cond_where(
479    ///         Cond::all()
480    ///             .add(Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]))
481    ///             .add(Cond::any()
482    ///                 .add(Expr::col((Glyph::Table, Glyph::Image)).like("A%"))
483    ///                 .add(Expr::col((Glyph::Table, Glyph::Image)).like("B%"))
484    ///             )
485    ///     )
486    ///     .to_owned();
487    ///
488    /// assert_eq!(
489    ///     query.to_string(PostgresQueryBuilder),
490    ///     r#"SELECT "image" FROM "glyph" WHERE "glyph"."aspect" IN (3, 4) AND ("glyph"."image" LIKE 'A%' OR "glyph"."image" LIKE 'B%')"#
491    /// );
492    /// ```
493    ///
494    /// Using macro
495    ///
496    /// ```
497    /// use sea_query::{*, tests_cfg::*};
498    ///
499    /// let query = Query::select()
500    ///     .column(Glyph::Image)
501    ///     .from(Glyph::Table)
502    ///     .cond_where(
503    ///         all![
504    ///             Expr::col((Glyph::Table, Glyph::Aspect)).is_in([3, 4]),
505    ///             any![
506    ///                 Expr::col((Glyph::Table, Glyph::Image)).like("A%"),
507    ///                 Expr::col((Glyph::Table, Glyph::Image)).like("B%"),
508    ///             ]
509    ///         ])
510    ///     .to_owned();
511    ///
512    /// assert_eq!(
513    ///     query.to_string(PostgresQueryBuilder),
514    ///     r#"SELECT "image" FROM "glyph" WHERE "glyph"."aspect" IN (3, 4) AND ("glyph"."image" LIKE 'A%' OR "glyph"."image" LIKE 'B%')"#
515    /// );
516    /// ```
517    ///
518    /// Calling multiple times; the following two are equivalent:
519    ///
520    /// ```
521    /// use sea_query::{tests_cfg::*, *};
522    ///
523    /// assert_eq!(
524    ///     Query::select()
525    ///         .column(Glyph::Id)
526    ///         .from(Glyph::Table)
527    ///         .cond_where(Expr::col(Glyph::Id).eq(1))
528    ///         .cond_where(any![Expr::col(Glyph::Id).eq(2), Expr::col(Glyph::Id).eq(3)])
529    ///         .to_owned()
530    ///         .to_string(PostgresQueryBuilder),
531    ///     r#"SELECT "id" FROM "glyph" WHERE "id" = 1 AND ("id" = 2 OR "id" = 3)"#
532    /// );
533    ///
534    /// assert_eq!(
535    ///     Query::select()
536    ///         .column(Glyph::Id)
537    ///         .from(Glyph::Table)
538    ///         .cond_where(any![Expr::col(Glyph::Id).eq(2), Expr::col(Glyph::Id).eq(3)])
539    ///         .cond_where(Expr::col(Glyph::Id).eq(1))
540    ///         .to_owned()
541    ///         .to_string(PostgresQueryBuilder),
542    ///     r#"SELECT "id" FROM "glyph" WHERE ("id" = 2 OR "id" = 3) AND "id" = 1"#
543    /// );
544    /// ```
545    ///
546    /// Calling multiple times; will be ANDed togother
547    ///
548    /// ```
549    /// use sea_query::{tests_cfg::*, *};
550    ///
551    /// assert_eq!(
552    ///     Query::select()
553    ///         .column(Glyph::Id)
554    ///         .from(Glyph::Table)
555    ///         .cond_where(any![Expr::col(Glyph::Id).eq(1), Expr::col(Glyph::Id).eq(2)])
556    ///         .cond_where(any![Expr::col(Glyph::Id).eq(3), Expr::col(Glyph::Id).eq(4)])
557    ///         .to_owned()
558    ///         .to_string(PostgresQueryBuilder),
559    ///     r#"SELECT "id" FROM "glyph" WHERE ("id" = 1 OR "id" = 2) AND ("id" = 3 OR "id" = 4)"#
560    /// );
561    ///
562    /// assert_eq!(
563    ///     Query::select()
564    ///         .column(Glyph::Id)
565    ///         .from(Glyph::Table)
566    ///         .cond_where(all![Expr::col(Glyph::Id).eq(1), Expr::col(Glyph::Id).eq(2)])
567    ///         .cond_where(all![Expr::col(Glyph::Id).eq(3), Expr::col(Glyph::Id).eq(4)])
568    ///         .to_owned()
569    ///         .to_string(PostgresQueryBuilder),
570    ///     r#"SELECT "id" FROM "glyph" WHERE "id" = 1 AND "id" = 2 AND "id" = 3 AND "id" = 4"#
571    /// );
572    /// ```
573    ///
574    /// Some more test cases involving negation
575    ///
576    /// ```
577    /// use sea_query::{tests_cfg::*, *};
578    ///
579    /// assert_eq!(
580    ///     Query::select()
581    ///         .column(Glyph::Id)
582    ///         .from(Glyph::Table)
583    ///         .cond_where(
584    ///             Cond::all()
585    ///                 .not()
586    ///                 .add(Expr::col(Glyph::Id).eq(1))
587    ///                 .add(Expr::col(Glyph::Id).eq(2)),
588    ///         )
589    ///         .cond_where(
590    ///             Cond::all()
591    ///                 .add(Expr::col(Glyph::Id).eq(3))
592    ///                 .add(Expr::col(Glyph::Id).eq(4)),
593    ///         )
594    ///         .to_owned()
595    ///         .to_string(PostgresQueryBuilder),
596    ///     r#"SELECT "id" FROM "glyph" WHERE (NOT ("id" = 1 AND "id" = 2)) AND ("id" = 3 AND "id" = 4)"#
597    /// );
598    ///
599    /// assert_eq!(
600    ///     Query::select()
601    ///         .column(Glyph::Id)
602    ///         .from(Glyph::Table)
603    ///         .cond_where(
604    ///             Cond::all()
605    ///                 .add(Expr::col(Glyph::Id).eq(3))
606    ///                 .add(Expr::col(Glyph::Id).eq(4)),
607    ///         )
608    ///         .cond_where(
609    ///             Cond::all()
610    ///                 .not()
611    ///                 .add(Expr::col(Glyph::Id).eq(1))
612    ///                 .add(Expr::col(Glyph::Id).eq(2)),
613    ///         )
614    ///         .to_owned()
615    ///         .to_string(PostgresQueryBuilder),
616    ///     r#"SELECT "id" FROM "glyph" WHERE "id" = 3 AND "id" = 4 AND (NOT ("id" = 1 AND "id" = 2))"#
617    /// );
618    /// ```
619    fn cond_where<C>(&mut self, condition: C) -> &mut Self
620    where
621        C: IntoCondition;
622}
623
624impl ConditionHolder {
625    pub fn new() -> Self {
626        Self::default()
627    }
628
629    pub fn new_with_condition(condition: Condition) -> Self {
630        let contents = ConditionHolderContents::Condition(condition);
631        Self { contents }
632    }
633
634    pub fn is_empty(&self) -> bool {
635        match &self.contents {
636            ConditionHolderContents::Empty => true,
637            ConditionHolderContents::Chain(c) => c.is_empty(),
638            ConditionHolderContents::Condition(c) => c.conditions.is_empty(),
639        }
640    }
641
642    pub fn is_one(&self) -> bool {
643        match &self.contents {
644            ConditionHolderContents::Empty => true,
645            ConditionHolderContents::Chain(c) => c.len() == 1,
646            ConditionHolderContents::Condition(c) => c.conditions.len() == 1,
647        }
648    }
649
650    pub fn add_and_or(&mut self, condition: LogicalChainOper) {
651        match &mut self.contents {
652            ConditionHolderContents::Empty => {
653                self.contents = ConditionHolderContents::Chain(vec![condition])
654            }
655            ConditionHolderContents::Chain(c) => c.push(condition),
656            ConditionHolderContents::Condition(_) => {
657                panic!("Cannot mix `and_where`/`or_where` and `cond_where` in statements")
658            }
659        }
660    }
661
662    pub fn add_condition(&mut self, mut addition: Condition) {
663        match std::mem::take(&mut self.contents) {
664            ConditionHolderContents::Empty => {
665                self.contents = ConditionHolderContents::Condition(addition);
666            }
667            ConditionHolderContents::Condition(mut current) => {
668                if current.condition_type == ConditionType::All && !current.negate {
669                    if addition.condition_type == ConditionType::All && !addition.negate {
670                        current.conditions.append(&mut addition.conditions);
671                        self.contents = ConditionHolderContents::Condition(current);
672                    } else {
673                        self.contents = ConditionHolderContents::Condition(current.add(addition));
674                    }
675                } else {
676                    self.contents = ConditionHolderContents::Condition(
677                        Condition::all().add(current).add(addition),
678                    );
679                }
680            }
681            ConditionHolderContents::Chain(_) => {
682                panic!("Cannot mix `and_where`/`or_where` and `cond_where` in statements")
683            }
684        }
685    }
686}
687
688#[cfg(test)]
689mod test {
690    use crate::{tests_cfg::*, *};
691    use pretty_assertions::assert_eq;
692
693    #[test]
694    #[cfg(feature = "backend-mysql")]
695    fn test_blank_condition() {
696        let query = Query::select()
697            .column(Glyph::Image)
698            .from(Glyph::Table)
699            .cond_where(Cond::all())
700            .cond_where(Expr::val(1).eq(1))
701            .cond_where(Expr::val(2).eq(2))
702            .cond_where(Cond::any().add(Expr::val(3).eq(3)).add(Expr::val(4).eq(4)))
703            .to_owned();
704
705        assert_eq!(
706            query.to_string(MysqlQueryBuilder),
707            "SELECT `image` FROM `glyph` WHERE 1 = 1 AND 2 = 2 AND (3 = 3 OR 4 = 4)"
708        );
709    }
710}