Skip to main content

sea_query/query/
condition.rs

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