Skip to content

Commit 68c8986

Browse files
committed
[SQL][Minor] Added comments and examples to explain BooleanSimplification.
1 parent c1f3c27 commit 68c8986

File tree

1 file changed

+94
-83
lines changed
  • sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer

1 file changed

+94
-83
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/optimizer/Optimizer.scala

Lines changed: 94 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -302,89 +302,100 @@ object OptimizeIn extends Rule[LogicalPlan] {
302302
object BooleanSimplification extends Rule[LogicalPlan] with PredicateHelper {
303303
def apply(plan: LogicalPlan): LogicalPlan = plan transform {
304304
case q: LogicalPlan => q transformExpressionsUp {
305-
case and @ And(left, right) =>
306-
(left, right) match {
307-
case (Literal(true, BooleanType), r) => r
308-
case (l, Literal(true, BooleanType)) => l
309-
case (Literal(false, BooleanType), _) => Literal(false)
310-
case (_, Literal(false, BooleanType)) => Literal(false)
311-
// a && a => a
312-
case (l, r) if l fastEquals r => l
313-
case (_, _) =>
314-
/* Do optimize for predicates using formula (a || b) && (a || c) => a || (b && c)
315-
* 1. Split left and right to get the disjunctive predicates,
316-
* i.e. lhsSet = (a, b), rhsSet = (a, c)
317-
* 2. Find the common predict between lhsSet and rhsSet, i.e. common = (a)
318-
* 3. Remove common predict from lhsSet and rhsSet, i.e. ldiff = (b), rdiff = (c)
319-
* 4. Apply the formula, get the optimized predict: common || (ldiff && rdiff)
320-
*/
321-
val lhsSet = splitDisjunctivePredicates(left).toSet
322-
val rhsSet = splitDisjunctivePredicates(right).toSet
323-
val common = lhsSet.intersect(rhsSet)
324-
val ldiff = lhsSet.diff(common)
325-
val rdiff = rhsSet.diff(common)
326-
if (ldiff.size == 0 || rdiff.size == 0) {
327-
// a && (a || b) => a
328-
common.reduce(Or)
329-
} else {
330-
// (a || b || c || ...) && (a || b || d || ...) && (a || b || e || ...) ... =>
331-
// (a || b) || ((c || ...) && (f || ...) && (e || ...) && ...)
332-
(ldiff.reduceOption(Or) ++ rdiff.reduceOption(Or))
333-
.reduceOption(And)
334-
.map(_ :: common.toList)
335-
.getOrElse(common.toList)
336-
.reduce(Or)
337-
}
338-
}
339-
340-
case or @ Or(left, right) =>
341-
(left, right) match {
342-
case (Literal(true, BooleanType), _) => Literal(true)
343-
case (_, Literal(true, BooleanType)) => Literal(true)
344-
case (Literal(false, BooleanType), r) => r
345-
case (l, Literal(false, BooleanType)) => l
346-
// a || a => a
347-
case (l, r) if l fastEquals r => l
348-
case (_, _) =>
349-
/* Do optimize for predicates using formula (a && b) || (a && c) => a && (b || c)
350-
* 1. Split left and right to get the conjunctive predicates,
351-
* i.e. lhsSet = (a, b), rhsSet = (a, c)
352-
* 2. Find the common predict between lhsSet and rhsSet, i.e. common = (a)
353-
* 3. Remove common predict from lhsSet and rhsSet, i.e. ldiff = (b), rdiff = (c)
354-
* 4. Apply the formula, get the optimized predict: common && (ldiff || rdiff)
355-
*/
356-
val lhsSet = splitConjunctivePredicates(left).toSet
357-
val rhsSet = splitConjunctivePredicates(right).toSet
358-
val common = lhsSet.intersect(rhsSet)
359-
val ldiff = lhsSet.diff(common)
360-
val rdiff = rhsSet.diff(common)
361-
if ( ldiff.size == 0 || rdiff.size == 0) {
362-
// a || (b && a) => a
363-
common.reduce(And)
364-
} else {
365-
// (a && b && c && ...) || (a && b && d && ...) || (a && b && e && ...) ... =>
366-
// a && b && ((c && ...) || (d && ...) || (e && ...) || ...)
367-
(ldiff.reduceOption(And) ++ rdiff.reduceOption(And))
368-
.reduceOption(Or)
369-
.map(_ :: common.toList)
370-
.getOrElse(common.toList)
371-
.reduce(And)
372-
}
373-
}
374-
375-
case not @ Not(exp) =>
376-
exp match {
377-
case Literal(true, BooleanType) => Literal(false)
378-
case Literal(false, BooleanType) => Literal(true)
379-
case GreaterThan(l, r) => LessThanOrEqual(l, r)
380-
case GreaterThanOrEqual(l, r) => LessThan(l, r)
381-
case LessThan(l, r) => GreaterThanOrEqual(l, r)
382-
case LessThanOrEqual(l, r) => GreaterThan(l, r)
383-
case Not(e) => e
384-
case _ => not
385-
}
386-
387-
// Turn "if (true) a else b" into "a", and if (false) a else b" into "b".
305+
case and @ And(left, right) => (left, right) match {
306+
// true && r => r
307+
case (Literal(true, BooleanType), r) => r
308+
// l && true => l
309+
case (l, Literal(true, BooleanType)) => l
310+
// false && r => false
311+
case (Literal(false, BooleanType), _) => Literal(false)
312+
// l && false => false
313+
case (_, Literal(false, BooleanType)) => Literal(false)
314+
// a && a => a
315+
case (l, r) if l fastEquals r => l
316+
// (a || b) && (a || c) => a || (b && c)
317+
case (_, _) =>
318+
// 1. Split left and right to get the disjunctive predicates,
319+
// i.e. lhsSet = (a, b), rhsSet = (a, c)
320+
// 2. Find the common predict between lhsSet and rhsSet, i.e. common = (a)
321+
// 3. Remove common predict from lhsSet and rhsSet, i.e. ldiff = (b), rdiff = (c)
322+
// 4. Apply the formula, get the optimized predicate: common || (ldiff && rdiff)
323+
val lhsSet = splitDisjunctivePredicates(left).toSet
324+
val rhsSet = splitDisjunctivePredicates(right).toSet
325+
val common = lhsSet.intersect(rhsSet)
326+
val ldiff = lhsSet.diff(common)
327+
val rdiff = rhsSet.diff(common)
328+
if (ldiff.size == 0 || rdiff.size == 0) {
329+
// a && (a || b) => a
330+
common.reduce(Or)
331+
} else {
332+
// (a || b || c || ...) && (a || b || d || ...) && (a || b || e || ...) ... =>
333+
// (a || b) || ((c || ...) && (f || ...) && (e || ...) && ...)
334+
(ldiff.reduceOption(Or) ++ rdiff.reduceOption(Or))
335+
.reduceOption(And)
336+
.map(_ :: common.toList)
337+
.getOrElse(common.toList)
338+
.reduce(Or)
339+
}
340+
} // end of And(left, right)
341+
342+
case or @ Or(left, right) => (left, right) match {
343+
// true || r => true
344+
case (Literal(true, BooleanType), _) => Literal(true)
345+
// r || true => true
346+
case (_, Literal(true, BooleanType)) => Literal(true)
347+
// false || r => r
348+
case (Literal(false, BooleanType), r) => r
349+
// l || false => l
350+
case (l, Literal(false, BooleanType)) => l
351+
// a || a => a
352+
case (l, r) if l fastEquals r => l
353+
// (a && b) || (a && c) => a && (b || c)
354+
case (_, _) =>
355+
// 1. Split left and right to get the conjunctive predicates,
356+
// i.e. lhsSet = (a, b), rhsSet = (a, c)
357+
// 2. Find the common predict between lhsSet and rhsSet, i.e. common = (a)
358+
// 3. Remove common predict from lhsSet and rhsSet, i.e. ldiff = (b), rdiff = (c)
359+
// 4. Apply the formula, get the optimized predicate: common && (ldiff || rdiff)
360+
val lhsSet = splitConjunctivePredicates(left).toSet
361+
val rhsSet = splitConjunctivePredicates(right).toSet
362+
val common = lhsSet.intersect(rhsSet)
363+
val ldiff = lhsSet.diff(common)
364+
val rdiff = rhsSet.diff(common)
365+
if ( ldiff.size == 0 || rdiff.size == 0) {
366+
// a || (b && a) => a
367+
common.reduce(And)
368+
} else {
369+
// (a && b && c && ...) || (a && b && d && ...) || (a && b && e && ...) ... =>
370+
// a && b && ((c && ...) || (d && ...) || (e && ...) || ...)
371+
(ldiff.reduceOption(And) ++ rdiff.reduceOption(And))
372+
.reduceOption(Or)
373+
.map(_ :: common.toList)
374+
.getOrElse(common.toList)
375+
.reduce(And)
376+
}
377+
} // end of Or(left, right)
378+
379+
case not @ Not(exp) => exp match {
380+
// not(true) => false
381+
case Literal(true, BooleanType) => Literal(false)
382+
// not(false) => true
383+
case Literal(false, BooleanType) => Literal(true)
384+
// not(l > r) => l <= r
385+
case GreaterThan(l, r) => LessThanOrEqual(l, r)
386+
// not(l >= r) => l < r
387+
case GreaterThanOrEqual(l, r) => LessThan(l, r)
388+
// not(l < r) => l >= r
389+
case LessThan(l, r) => GreaterThanOrEqual(l, r)
390+
// not(l <= r) => l > r
391+
case LessThanOrEqual(l, r) => GreaterThan(l, r)
392+
// not(not(e)) => e
393+
case Not(e) => e
394+
case _ => not
395+
} // end of Not(exp)
396+
397+
// if (true) a else b => a
398+
// if (false) a else b => b
388399
case e @ If(Literal(v, _), trueValue, falseValue) => if (v == true) trueValue else falseValue
389400
}
390401
}

0 commit comments

Comments
 (0)