-
Couldn't load subscription status.
- Fork 28.9k
[SPARK-30768][SQL] Constraints inferred from inequality attributes #27518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b59a810
999126c
8843d45
d4eb2d7
df679e6
3c9968a
f9a90aa
bfa6039
248e3cc
af55a08
5c76b9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,12 +55,25 @@ trait QueryPlanConstraints extends ConstraintHelper { self: LogicalPlan => | |
|
|
||
| trait ConstraintHelper { | ||
|
|
||
| /** | ||
| * Infers an additional set of constraints from a given set of constraints. | ||
| */ | ||
| def inferAdditionalConstraints(constraints: Set[Expression]): Set[Expression] = { | ||
| var inferred = inferEqualityConstraints(constraints) | ||
| var lastInequalityInferred = Set.empty[Expression] | ||
| do { | ||
| lastInequalityInferred = inferInequalityConstraints(constraints ++ inferred) | ||
| inferred ++= lastInequalityInferred | ||
| } while (lastInequalityInferred.nonEmpty) | ||
| inferred | ||
| } | ||
|
|
||
| /** | ||
| * Infers an additional set of constraints from a given set of equality constraints. | ||
| * For e.g., if an operator has constraints of the form (`a = 5`, `a = b`), this returns an | ||
| * additional constraint of the form `b = 5`. | ||
| */ | ||
| def inferAdditionalConstraints(constraints: Set[Expression]): Set[Expression] = { | ||
| def inferEqualityConstraints(constraints: Set[Expression]): Set[Expression] = { | ||
| var inferredConstraints = Set.empty[Expression] | ||
| // IsNotNull should be constructed by `constructIsNotNullConstraints`. | ||
| val predicates = constraints.filterNot(_.isInstanceOf[IsNotNull]) | ||
|
|
@@ -78,6 +91,72 @@ trait ConstraintHelper { | |
| inferredConstraints -- constraints | ||
| } | ||
|
|
||
| /** | ||
| * Infers an additional set of constraints from a given set of inequality constraints. | ||
| * For e.g., if an operator has constraints of the form (`a > b`, `b > 5`), this returns an | ||
| * additional constraint of the form `a > 5`. | ||
| */ | ||
| def inferInequalityConstraints(constraints: Set[Expression]): Set[Expression] = { | ||
| val binaryComparisons = constraints.filter { | ||
| case _: GreaterThan => true | ||
| case _: GreaterThanOrEqual => true | ||
| case _: LessThan => true | ||
| case _: LessThanOrEqual => true | ||
| case _: EqualTo => true | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example: |
||
| case _ => false | ||
| } | ||
|
|
||
| val greaterThans = binaryComparisons.map { | ||
| case EqualTo(l, r) if l.foldable => EqualTo(r, l) | ||
| case LessThan(l, r) => GreaterThan(r, l) | ||
| case LessThanOrEqual(l, r) => GreaterThanOrEqual(r, l) | ||
| case other => other | ||
| } | ||
|
|
||
| val lessThans = binaryComparisons.map { | ||
| case EqualTo(l, r) if l.foldable => EqualTo(r, l) | ||
| case GreaterThan(l, r) => LessThan(r, l) | ||
| case GreaterThanOrEqual(l, r) => LessThanOrEqual(r, l) | ||
| case other => other | ||
| } | ||
|
Comment on lines
+116
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't this duplicate the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. for example: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it because of the foldable check? Without it, it should be inferable. |
||
|
|
||
| var inferredConstraints = Set.empty[Expression] | ||
| greaterThans.foreach { | ||
| case op @ BinaryComparison(source: Attribute, destination: Expression) | ||
| if destination.foldable => | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that the foldability is not needed here. The new constraints do not have to only involve constants, but also any attribute. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid generating too many constraints. For example: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a and c are in tihe same side of a join, then it can be pushed down. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How to push down There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sorry, I used a wrong word. I meant pushed through the join into one of the sides. |
||
| inferredConstraints ++= (greaterThans - op).map { | ||
| case GreaterThan(l, r) if r.semanticEquals(source) => | ||
| GreaterThan(l, destination) | ||
| case GreaterThanOrEqual(l, r) | ||
| if r.semanticEquals(source) && op.isInstanceOf[GreaterThan] => | ||
| GreaterThan(l, destination) | ||
| case GreaterThanOrEqual(l, r) if r.semanticEquals(source) => | ||
| GreaterThanOrEqual(l, destination) | ||
| case other => other | ||
| } | ||
| case _ => // No inference | ||
| } | ||
|
|
||
| lessThans.foreach { | ||
| case op @ BinaryComparison(source: Attribute, destination: Expression) | ||
| if destination.foldable => | ||
| inferredConstraints ++= (lessThans - op).map { | ||
| case LessThan(l, r) if r.semanticEquals(source) => | ||
| LessThan(l, destination) | ||
| case LessThanOrEqual(l, r) | ||
| if r.semanticEquals(source) && op.isInstanceOf[LessThan] => | ||
| LessThan(l, destination) | ||
| case LessThanOrEqual(l, r) if r.semanticEquals(source) => | ||
| LessThanOrEqual(l, destination) | ||
| case other => other | ||
| } | ||
| case _ => // No inference | ||
| } | ||
|
|
||
| (inferredConstraints -- constraints -- greaterThans -- lessThans) | ||
| .filterNot(i => constraints.exists(_.semanticEquals(i))) | ||
| } | ||
|
|
||
| private def replaceConstraints( | ||
| constraints: Set[Expression], | ||
| source: Expression, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you hit a infinite loop with non deterministic filters? As they are never semantically equal to any other expression (including themselves). I hit that problem in #29650, where I was also working on constraint inference , but from
EqualNullSafe.