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
110pub 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 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}