nom_sql/
condition.rs

1use nom::character::complete::{multispace0, multispace1};
2use std::collections::{HashSet, VecDeque};
3use std::fmt;
4use std::str;
5
6use arithmetic::{arithmetic_expression, ArithmeticExpression};
7use column::Column;
8use common::{
9    binary_comparison_operator, column_identifier, literal, value_list, Literal, Operator,
10};
11
12use nom::branch::alt;
13use nom::bytes::complete::{tag, tag_no_case};
14use nom::combinator::{map, opt};
15use nom::sequence::{delimited, pair, preceded, separated_pair, terminated, tuple};
16use nom::IResult;
17use select::{nested_selection, SelectStatement};
18
19#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
20pub enum ConditionBase {
21    Field(Column),
22    Literal(Literal),
23    LiteralList(Vec<Literal>),
24    NestedSelect(Box<SelectStatement>),
25}
26
27impl fmt::Display for ConditionBase {
28    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29        match *self {
30            ConditionBase::Field(ref col) => write!(f, "{}", col),
31            ConditionBase::Literal(ref literal) => write!(f, "{}", literal.to_string()),
32            ConditionBase::LiteralList(ref ll) => write!(
33                f,
34                "({})",
35                ll.iter()
36                    .map(|l| l.to_string())
37                    .collect::<Vec<_>>()
38                    .join(", ")
39            ),
40            ConditionBase::NestedSelect(ref select) => write!(f, "{}", select),
41        }
42    }
43}
44
45#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
46pub struct ConditionTree {
47    pub operator: Operator,
48    pub left: Box<ConditionExpression>,
49    pub right: Box<ConditionExpression>,
50}
51
52impl<'a> ConditionTree {
53    pub fn contained_columns(&'a self) -> HashSet<&'a Column> {
54        let mut s = HashSet::new();
55        let mut q = VecDeque::<&'a ConditionTree>::new();
56        q.push_back(self);
57        while let Some(ref ct) = q.pop_front() {
58            match *ct.left.as_ref() {
59                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
60                    s.insert(c);
61                }
62                ConditionExpression::LogicalOp(ref ct)
63                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
64                _ => (),
65            }
66            match *ct.right.as_ref() {
67                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
68                    s.insert(c);
69                }
70                ConditionExpression::LogicalOp(ref ct)
71                | ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
72                _ => (),
73            }
74        }
75        s
76    }
77}
78
79impl fmt::Display for ConditionTree {
80    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        write!(f, "{}", self.left)?;
82        write!(f, " {} ", self.operator)?;
83        write!(f, "{}", self.right)
84    }
85}
86
87#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
88pub enum ConditionExpression {
89    ComparisonOp(ConditionTree),
90    LogicalOp(ConditionTree),
91    NegationOp(Box<ConditionExpression>),
92    Base(ConditionBase),
93    Arithmetic(Box<ArithmeticExpression>),
94    Bracketed(Box<ConditionExpression>),
95}
96
97impl fmt::Display for ConditionExpression {
98    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99        match *self {
100            ConditionExpression::ComparisonOp(ref tree) => write!(f, "{}", tree),
101            ConditionExpression::LogicalOp(ref tree) => write!(f, "{}", tree),
102            ConditionExpression::NegationOp(ref expr) => write!(f, "NOT {}", expr),
103            ConditionExpression::Bracketed(ref expr) => write!(f, "({})", expr),
104            ConditionExpression::Base(ref base) => write!(f, "{}", base),
105            ConditionExpression::Arithmetic(ref expr) => write!(f, "{}", expr),
106        }
107    }
108}
109
110// Parse a conditional expression into a condition tree structure
111pub fn condition_expr(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
112    let cond = map(
113        separated_pair(
114            and_expr,
115            delimited(multispace0, tag_no_case("or"), multispace1),
116            condition_expr,
117        ),
118        |p| {
119            ConditionExpression::LogicalOp(ConditionTree {
120                operator: Operator::Or,
121                left: Box::new(p.0),
122                right: Box::new(p.1),
123            })
124        },
125    );
126
127    alt((cond, and_expr))(i)
128}
129
130pub fn and_expr(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
131    let cond = map(
132        separated_pair(
133            parenthetical_expr,
134            delimited(multispace0, tag_no_case("and"), multispace1),
135            and_expr,
136        ),
137        |p| {
138            ConditionExpression::LogicalOp(ConditionTree {
139                operator: Operator::And,
140                left: Box::new(p.0),
141                right: Box::new(p.1),
142            })
143        },
144    );
145
146    alt((cond, parenthetical_expr))(i)
147}
148
149fn parenthetical_expr_helper(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
150    let (remaining_input, (_, _, left_expr, _, _, _, operator, _, right_expr)) = tuple((
151        tag("("),
152        multispace0,
153        simple_expr,
154        multispace0,
155        tag(")"),
156        multispace0,
157        binary_comparison_operator,
158        multispace0,
159        simple_expr,
160    ))(i)?;
161
162    let left = Box::new(ConditionExpression::Bracketed(Box::new(left_expr)));
163    let right = Box::new(right_expr);
164    let cond = ConditionExpression::ComparisonOp(ConditionTree {
165        operator,
166        left,
167        right,
168    });
169
170    Ok((remaining_input, cond))
171}
172
173pub fn parenthetical_expr(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
174    alt((
175        parenthetical_expr_helper,
176        map(
177            delimited(
178                terminated(tag("("), multispace0),
179                condition_expr,
180                delimited(multispace0, tag(")"), multispace0),
181            ),
182            |inner| ConditionExpression::Bracketed(Box::new(inner)),
183        ),
184        not_expr,
185    ))(i)
186}
187
188pub fn not_expr(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
189    alt((
190        map(
191            preceded(pair(tag_no_case("not"), multispace1), parenthetical_expr),
192            |right| ConditionExpression::NegationOp(Box::new(right)),
193        ),
194        boolean_primary,
195    ))(i)
196}
197
198fn is_null(i: &[u8]) -> IResult<&[u8], (Operator, ConditionExpression)> {
199    let (remaining_input, (_, _, not, _, _)) = tuple((
200        tag_no_case("is"),
201        multispace0,
202        opt(tag_no_case("not")),
203        multispace0,
204        tag_no_case("null"),
205    ))(i)?;
206
207    // XXX(malte): bit of a hack; would consumers ever need to know
208    // about "IS NULL" vs. "= NULL"?
209    Ok((
210        remaining_input,
211        (
212            if not.is_some() {
213                Operator::NotEqual
214            } else {
215                Operator::Equal
216            },
217            ConditionExpression::Base(ConditionBase::Literal(Literal::Null)),
218        ),
219    ))
220}
221
222fn boolean_primary_rest(i: &[u8]) -> IResult<&[u8], (Operator, ConditionExpression)> {
223    alt((
224        is_null,
225        separated_pair(binary_comparison_operator, multispace0, predicate),
226    ))(i)
227}
228
229fn boolean_primary(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
230    alt((
231        map(
232            separated_pair(predicate, multispace0, boolean_primary_rest),
233            |e: (ConditionExpression, (Operator, ConditionExpression))| {
234                ConditionExpression::ComparisonOp(ConditionTree {
235                    operator: (e.1).0,
236                    left: Box::new(e.0),
237                    right: Box::new((e.1).1),
238                })
239            },
240        ),
241        predicate,
242    ))(i)
243}
244
245fn predicate(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
246    let nested_sel_pred = map(
247        separated_pair(
248            opt(preceded(multispace0, tag_no_case("not"))),
249            delimited(multispace1, tag_no_case("in"), multispace1),
250            nested_selection,
251        ),
252        |p| {
253            let nested = ConditionExpression::Base(ConditionBase::NestedSelect(Box::new(p.1)));
254            if (p.0).is_some() {
255                ConditionExpression::NegationOp(Box::new(nested))
256            } else {
257                nested
258            }
259        },
260    );
261    let in_list_pred = map(
262        separated_pair(
263            opt(preceded(multispace0, tag_no_case("not"))),
264            delimited(multispace1, tag_no_case("in"), multispace1),
265            delimited(tag("("), value_list, tag(")")),
266        ),
267        |p| {
268            let list = ConditionExpression::Base(ConditionBase::LiteralList(p.1));
269            if (p.0).is_some() {
270                ConditionExpression::NegationOp(Box::new(list))
271            } else {
272                list
273            }
274        },
275    );
276
277    let (remaining_input, (left, op_right)) =
278        tuple((simple_expr, opt(alt((nested_sel_pred, in_list_pred)))))(i)?;
279
280    match op_right {
281        Some(right) => Ok((
282            remaining_input,
283            ConditionExpression::ComparisonOp(ConditionTree {
284                operator: Operator::In,
285                left: Box::new(left),
286                right: Box::new(right),
287            }),
288        )),
289        None => Ok((remaining_input, left)),
290    }
291}
292
293fn simple_expr(i: &[u8]) -> IResult<&[u8], ConditionExpression> {
294    alt((
295        map(arithmetic_expression, |e| {
296            ConditionExpression::Arithmetic(Box::new(e))
297        }),
298        map(
299            delimited(
300                terminated(tag("("), multispace0),
301                arithmetic_expression,
302                preceded(multispace0, tag(")")),
303            ),
304            |e| {
305                ConditionExpression::Bracketed(Box::new(ConditionExpression::Arithmetic(Box::new(
306                    e,
307                ))))
308            },
309        ),
310        map(literal, |lit| {
311            ConditionExpression::Base(ConditionBase::Literal(lit))
312        }),
313        map(column_identifier, |f| {
314            ConditionExpression::Base(ConditionBase::Field(f))
315        }),
316        map(delimited(tag("("), nested_selection, tag(")")), |s| {
317            ConditionExpression::Base(ConditionBase::NestedSelect(Box::new(s)))
318        }),
319    ))(i)
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325    use arithmetic::{ArithmeticBase, ArithmeticOperator};
326    use column::Column;
327    use common::{FieldDefinitionExpression, Literal, Operator};
328
329    fn columns(cols: &[&str]) -> Vec<FieldDefinitionExpression> {
330        cols.iter()
331            .map(|c| FieldDefinitionExpression::Col(Column::from(*c)))
332            .collect()
333    }
334
335    fn flat_condition_tree(
336        op: Operator,
337        l: ConditionBase,
338        r: ConditionBase,
339    ) -> ConditionExpression {
340        ConditionExpression::ComparisonOp(ConditionTree {
341            operator: op,
342            left: Box::new(ConditionExpression::Base(l)),
343            right: Box::new(ConditionExpression::Base(r)),
344        })
345    }
346
347    #[test]
348    fn ct_contained_columns() {
349        use std::collections::HashSet;
350
351        let cond = "a.foo = ? and b.bar = 42";
352
353        let res = condition_expr(cond.as_bytes());
354        let c1 = Column::from("a.foo");
355        let c2 = Column::from("b.bar");
356        let mut expected_cols = HashSet::new();
357        expected_cols.insert(&c1);
358        expected_cols.insert(&c2);
359        match res.unwrap().1 {
360            ConditionExpression::LogicalOp(ct) => {
361                assert_eq!(ct.contained_columns(), expected_cols);
362            }
363            _ => panic!(),
364        }
365    }
366
367    #[test]
368    fn equality_placeholder() {
369        let cond = "foo = ?";
370
371        let res = condition_expr(cond.as_bytes());
372        assert_eq!(
373            res.unwrap().1,
374            flat_condition_tree(
375                Operator::Equal,
376                ConditionBase::Field(Column::from("foo")),
377                ConditionBase::Literal(Literal::Placeholder)
378            )
379        );
380    }
381
382    fn x_operator_value(op: ArithmeticOperator, value: Literal) -> ConditionExpression {
383        ConditionExpression::Arithmetic(Box::new(ArithmeticExpression::new(
384            op,
385            ArithmeticBase::Column(Column::from("x")),
386            ArithmeticBase::Scalar(value),
387            None,
388        )))
389    }
390    #[test]
391    fn simple_arithmetic_expression() {
392        let cond = "x + 3";
393
394        let res = simple_expr(cond.as_bytes());
395        assert_eq!(
396            res.unwrap().1,
397            x_operator_value(ArithmeticOperator::Add, 3.into())
398        );
399    }
400
401    #[test]
402    fn simple_arithmetic_expression_with_parenthesis() {
403        let cond = "( x - 2 )";
404
405        let res = simple_expr(cond.as_bytes());
406        assert_eq!(
407            res.unwrap().1,
408            ConditionExpression::Bracketed(Box::new(x_operator_value(
409                ArithmeticOperator::Subtract,
410                2.into()
411            )))
412        );
413    }
414
415    #[test]
416    fn parenthetical_arithmetic_expression() {
417        let cond = "( x * 5 )";
418
419        let res = parenthetical_expr(cond.as_bytes());
420        assert_eq!(
421            res.unwrap().1,
422            ConditionExpression::Bracketed(Box::new(x_operator_value(
423                ArithmeticOperator::Multiply,
424                5.into()
425            )))
426        );
427    }
428
429    #[test]
430    fn condition_expression_with_arithmetics() {
431        let cond = "x * 3 = 21";
432
433        let res = condition_expr(cond.as_bytes());
434        assert_eq!(
435            res.unwrap().1,
436            ConditionExpression::ComparisonOp(ConditionTree {
437                operator: Operator::Equal,
438                left: Box::new(x_operator_value(ArithmeticOperator::Multiply, 3.into())),
439                right: Box::new(ConditionExpression::Base(ConditionBase::Literal(21.into())))
440            })
441        );
442    }
443    #[test]
444    fn condition_expression_with_arithmetics_and_parenthesis() {
445        let cond = "(x - 7 = 15)";
446
447        let res = condition_expr(cond.as_bytes());
448        assert_eq!(
449            res.unwrap().1,
450            ConditionExpression::Bracketed(Box::new(ConditionExpression::ComparisonOp(
451                ConditionTree {
452                    operator: Operator::Equal,
453                    left: Box::new(x_operator_value(ArithmeticOperator::Subtract, 7.into())),
454                    right: Box::new(ConditionExpression::Base(ConditionBase::Literal(15.into())))
455                }
456            )))
457        );
458    }
459
460    #[test]
461    fn condition_expression_with_arithmetics_in_parenthesis() {
462        let cond = "( x + 2) = 15";
463
464        let res = condition_expr(cond.as_bytes());
465        assert_eq!(
466            res.unwrap().1,
467            ConditionExpression::ComparisonOp(ConditionTree {
468                operator: Operator::Equal,
469                left: Box::new(ConditionExpression::Bracketed(Box::new(x_operator_value(
470                    ArithmeticOperator::Add,
471                    2.into()
472                )))),
473                right: Box::new(ConditionExpression::Base(ConditionBase::Literal(15.into())))
474            })
475        );
476    }
477
478    #[test]
479    fn condition_expression_with_arithmetics_in_parenthesis_in_both_side() {
480        let cond = "( x + 2) =(x*3)";
481
482        let res = condition_expr(cond.as_bytes());
483        assert_eq!(
484            res.unwrap().1,
485            ConditionExpression::ComparisonOp(ConditionTree {
486                operator: Operator::Equal,
487                left: Box::new(ConditionExpression::Bracketed(Box::new(x_operator_value(
488                    ArithmeticOperator::Add,
489                    2.into()
490                )))),
491                right: Box::new(ConditionExpression::Bracketed(Box::new(x_operator_value(
492                    ArithmeticOperator::Multiply,
493                    3.into()
494                ))))
495            })
496        );
497    }
498
499    #[test]
500    fn equality_literals() {
501        let cond1 = "foo = 42";
502        let cond2 = "foo = \"hello\"";
503
504        let res1 = condition_expr(cond1.as_bytes());
505        assert_eq!(
506            res1.unwrap().1,
507            flat_condition_tree(
508                Operator::Equal,
509                ConditionBase::Field(Column::from("foo")),
510                ConditionBase::Literal(Literal::Integer(42 as i64))
511            )
512        );
513
514        let res2 = condition_expr(cond2.as_bytes());
515        assert_eq!(
516            res2.unwrap().1,
517            flat_condition_tree(
518                Operator::Equal,
519                ConditionBase::Field(Column::from("foo")),
520                ConditionBase::Literal(Literal::String(String::from("hello")))
521            )
522        );
523    }
524
525    #[test]
526    fn inequality_literals() {
527        let cond1 = "foo >= 42";
528        let cond2 = "foo <= 5";
529
530        let res1 = condition_expr(cond1.as_bytes());
531        assert_eq!(
532            res1.unwrap().1,
533            flat_condition_tree(
534                Operator::GreaterOrEqual,
535                ConditionBase::Field(Column::from("foo")),
536                ConditionBase::Literal(Literal::Integer(42 as i64))
537            )
538        );
539
540        let res2 = condition_expr(cond2.as_bytes());
541        assert_eq!(
542            res2.unwrap().1,
543            flat_condition_tree(
544                Operator::LessOrEqual,
545                ConditionBase::Field(Column::from("foo")),
546                ConditionBase::Literal(Literal::Integer(5 as i64))
547            )
548        );
549    }
550
551    #[test]
552    fn empty_string_literal() {
553        let cond = "foo = ''";
554
555        let res = condition_expr(cond.as_bytes());
556        assert_eq!(
557            res.unwrap().1,
558            flat_condition_tree(
559                Operator::Equal,
560                ConditionBase::Field(Column::from("foo")),
561                ConditionBase::Literal(Literal::String(String::from("")))
562            )
563        );
564    }
565
566    #[test]
567    fn parenthesis() {
568        let cond = "(foo = ? or bar = 12) and foobar = 'a'";
569
570        use common::Literal;
571        use ConditionBase::*;
572        use ConditionExpression::*;
573
574        let a = ComparisonOp(ConditionTree {
575            operator: Operator::Equal,
576            left: Box::new(Base(Field("foo".into()))),
577            right: Box::new(Base(Literal(Literal::Placeholder))),
578        });
579
580        let b = ComparisonOp(ConditionTree {
581            operator: Operator::Equal,
582            left: Box::new(Base(Field("bar".into()))),
583            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
584        });
585
586        let left = Bracketed(Box::new(LogicalOp(ConditionTree {
587            operator: Operator::Or,
588            left: Box::new(a),
589            right: Box::new(b),
590        })));
591
592        let right = ComparisonOp(ConditionTree {
593            operator: Operator::Equal,
594            left: Box::new(Base(Field("foobar".into()))),
595            right: Box::new(Base(Literal(Literal::String("a".into())))),
596        });
597
598        let complete = LogicalOp(ConditionTree {
599            operator: Operator::And,
600            left: Box::new(left),
601            right: Box::new(right),
602        });
603
604        let res = condition_expr(cond.as_bytes());
605        assert_eq!(res.unwrap().1, complete);
606    }
607
608    #[test]
609    fn order_of_operations() {
610        let cond = "foo = ? and bar = 12 or foobar = 'a'";
611
612        use common::Literal;
613        use ConditionBase::*;
614        use ConditionExpression::*;
615
616        let a = ComparisonOp(ConditionTree {
617            operator: Operator::Equal,
618            left: Box::new(Base(Field("foo".into()))),
619            right: Box::new(Base(Literal(Literal::Placeholder))),
620        });
621
622        let b = ComparisonOp(ConditionTree {
623            operator: Operator::Equal,
624            left: Box::new(Base(Field("bar".into()))),
625            right: Box::new(Base(Literal(Literal::Integer(12.into())))),
626        });
627
628        let left = LogicalOp(ConditionTree {
629            operator: Operator::And,
630            left: Box::new(a),
631            right: Box::new(b),
632        });
633
634        let right = ComparisonOp(ConditionTree {
635            operator: Operator::Equal,
636            left: Box::new(Base(Field("foobar".into()))),
637            right: Box::new(Base(Literal(Literal::String("a".into())))),
638        });
639
640        let complete = LogicalOp(ConditionTree {
641            operator: Operator::Or,
642            left: Box::new(left),
643            right: Box::new(right),
644        });
645
646        let res = condition_expr(cond.as_bytes());
647        assert_eq!(res.unwrap().1, complete);
648    }
649
650    #[test]
651    fn negation() {
652        let cond = "not bar = 12 or foobar = 'a'";
653
654        use common::Literal::*;
655        use ConditionBase::*;
656        use ConditionExpression::*;
657
658        let left = NegationOp(Box::new(ComparisonOp(ConditionTree {
659            operator: Operator::Equal,
660            left: Box::new(Base(Field("bar".into()))),
661            right: Box::new(Base(Literal(Integer(12.into())))),
662        })));
663
664        let right = ComparisonOp(ConditionTree {
665            operator: Operator::Equal,
666            left: Box::new(Base(Field("foobar".into()))),
667            right: Box::new(Base(Literal(String("a".into())))),
668        });
669
670        let complete = LogicalOp(ConditionTree {
671            operator: Operator::Or,
672            left: Box::new(left),
673            right: Box::new(right),
674        });
675
676        let res = condition_expr(cond.as_bytes());
677        assert_eq!(res.unwrap().1, complete);
678    }
679
680    #[test]
681    fn nested_select() {
682        use select::SelectStatement;
683        use std::default::Default;
684        use table::Table;
685        use ConditionBase::*;
686
687        let cond = "bar in (select col from foo)";
688
689        let res = condition_expr(cond.as_bytes());
690
691        let nested_select = Box::new(SelectStatement {
692            tables: vec![Table::from("foo")],
693            fields: columns(&["col"]),
694            ..Default::default()
695        });
696
697        let expected = flat_condition_tree(
698            Operator::In,
699            Field("bar".into()),
700            NestedSelect(nested_select),
701        );
702
703        assert_eq!(res.unwrap().1, expected);
704    }
705
706    #[test]
707    fn and_with_nested_select() {
708        use select::SelectStatement;
709        use std::default::Default;
710        use table::Table;
711        use ConditionBase::*;
712
713        let cond = "paperId in (select paperId from PaperConflict) and size > 0";
714
715        let res = condition_expr(cond.as_bytes());
716
717        let nested_select = Box::new(SelectStatement {
718            tables: vec![Table::from("PaperConflict")],
719            fields: columns(&["paperId"]),
720            ..Default::default()
721        });
722
723        let left = flat_condition_tree(
724            Operator::In,
725            Field("paperId".into()),
726            NestedSelect(nested_select),
727        );
728
729        let right = flat_condition_tree(Operator::Greater, Field("size".into()), Literal(0.into()));
730
731        let expected = ConditionExpression::LogicalOp(ConditionTree {
732            left: Box::new(left),
733            right: Box::new(right),
734            operator: Operator::And,
735        });
736
737        assert_eq!(res.unwrap().1, expected);
738    }
739
740    #[test]
741    fn in_list_of_values() {
742        use ConditionBase::*;
743
744        let cond = "bar in (0)";
745
746        let res = condition_expr(cond.as_bytes());
747
748        let expected = flat_condition_tree(
749            Operator::In,
750            Field("bar".into()),
751            LiteralList(vec![0.into()]),
752        );
753
754        assert_eq!(res.unwrap().1, expected);
755    }
756
757    #[test]
758    fn is_null() {
759        use common::Literal;
760        use ConditionBase::*;
761
762        let cond = "bar IS NULL";
763
764        let res = condition_expr(cond.as_bytes());
765        let expected =
766            flat_condition_tree(Operator::Equal, Field("bar".into()), Literal(Literal::Null));
767        assert_eq!(res.unwrap().1, expected);
768
769        let cond = "bar IS NOT NULL";
770
771        let res = condition_expr(cond.as_bytes());
772        let expected = flat_condition_tree(
773            Operator::NotEqual,
774            Field("bar".into()),
775            Literal(Literal::Null),
776        );
777        assert_eq!(res.unwrap().1, expected);
778    }
779
780    #[test]
781    fn complex_bracketing() {
782        use common::Literal;
783        use ConditionBase::*;
784
785        let cond = "`read_ribbons`.`is_following` = 1 \
786                    AND `comments`.`user_id` <> `read_ribbons`.`user_id` \
787                    AND `saldo` >= 0 \
788                    AND ( `parent_comments`.`user_id` = `read_ribbons`.`user_id` \
789                    OR ( `parent_comments`.`user_id` IS NULL \
790                    AND `stories`.`user_id` = `read_ribbons`.`user_id` ) ) \
791                    AND ( `parent_comments`.`id` IS NULL \
792                    OR `saldo` >= 0 ) \
793                    AND `read_ribbons`.`user_id` = ?";
794
795        let res = condition_expr(cond.as_bytes());
796        let expected = ConditionExpression::LogicalOp(ConditionTree {
797            operator: Operator::And,
798            left: Box::new(flat_condition_tree(
799                Operator::Equal,
800                Field("read_ribbons.is_following".into()),
801                Literal(Literal::Integer(1.into())),
802            )),
803            right: Box::new(ConditionExpression::LogicalOp(ConditionTree {
804                operator: Operator::And,
805                left: Box::new(flat_condition_tree(
806                    Operator::NotEqual,
807                    Field("comments.user_id".into()),
808                    Field("read_ribbons.user_id".into()),
809                )),
810                right: Box::new(ConditionExpression::LogicalOp(ConditionTree {
811                    operator: Operator::And,
812                    left: Box::new(flat_condition_tree(
813                        Operator::GreaterOrEqual,
814                        Field("saldo".into()),
815                        Literal(Literal::Integer(0.into())),
816                    )),
817                    right: Box::new(ConditionExpression::LogicalOp(ConditionTree {
818                        operator: Operator::And,
819                        left: Box::new(ConditionExpression::Bracketed(Box::new(
820                            ConditionExpression::LogicalOp(ConditionTree {
821                                operator: Operator::Or,
822                                left: Box::new(flat_condition_tree(
823                                    Operator::Equal,
824                                    Field("parent_comments.user_id".into()),
825                                    Field("read_ribbons.user_id".into()),
826                                )),
827                                right: Box::new(ConditionExpression::Bracketed(Box::new(
828                                    ConditionExpression::LogicalOp(ConditionTree {
829                                        operator: Operator::And,
830                                        left: Box::new(flat_condition_tree(
831                                            Operator::Equal,
832                                            Field("parent_comments.user_id".into()),
833                                            Literal(Literal::Null),
834                                        )),
835                                        right: Box::new(flat_condition_tree(
836                                            Operator::Equal,
837                                            Field("stories.user_id".into()),
838                                            Field("read_ribbons.user_id".into()),
839                                        )),
840                                    }),
841                                ))),
842                            }),
843                        ))),
844                        right: Box::new(ConditionExpression::LogicalOp(ConditionTree {
845                            operator: Operator::And,
846                            left: Box::new(ConditionExpression::Bracketed(Box::new(
847                                ConditionExpression::LogicalOp(ConditionTree {
848                                    operator: Operator::Or,
849                                    left: Box::new(flat_condition_tree(
850                                        Operator::Equal,
851                                        Field("parent_comments.id".into()),
852                                        Literal(Literal::Null),
853                                    )),
854                                    right: Box::new(flat_condition_tree(
855                                        Operator::GreaterOrEqual,
856                                        Field("saldo".into()),
857                                        Literal(Literal::Integer(0)),
858                                    )),
859                                }),
860                            ))),
861                            right: Box::new(flat_condition_tree(
862                                Operator::Equal,
863                                Field("read_ribbons.user_id".into()),
864                                Literal(Literal::Placeholder),
865                            )),
866                        })),
867                    })),
868                })),
869            })),
870        });
871        let res = res.unwrap().1;
872        assert_eq!(res, expected);
873    }
874}