Skip to content

Commit 2efca2f

Browse files
committed
Fix a bug in semanticEqual. Add some comment.
1 parent 99626a4 commit 2efca2f

File tree

2 files changed

+24
-7
lines changed

2 files changed

+24
-7
lines changed

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/Expression.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ abstract class Expression extends TreeNode[Expression]{
162162

163163
/**
164164
* Returns a sequence of expressions by removing from q the first expression that is semantically
165-
* equivalent to e.
165+
* equivalent to e. If such an expression was not found, return seq.
166166
*/
167167
def removeFirstSemanticEquivalent(seq: Seq[Expression], e: Expression): Seq[Expression] = {
168168
seq match {

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/predicates.scala

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,17 @@ case class And(left: Expression, right: Expression) extends BinaryOperator
261261
// Recursively call semanticEquals on subexpressions to check the equivalency of two seqs.
262262
var elements1 = splitConjunctivePredicates(this)
263263
val elements2 = splitConjunctivePredicates(other)
264-
for (e <- elements2) elements1 = removeFirstSemanticEquivalent(elements1, e)
265-
elements1.isEmpty
264+
// We can recursively call semanticEquals to check the equivalency for subexpressions, but
265+
// there is no simple solution to compare the equivalency of sequence of expressions.
266+
// Expression class doesn't have order, so we couldn't sort them. We can neither use
267+
// set comparison as Set doesn't support custom compare function, which is semanticEquals.
268+
// To check the equivalency of elements1 and elements2, we first compare their size. Then
269+
// for each element in elements2, we remove its first semantically equivalent expression from
270+
// elements1. If they are semantically equivalent, elements1 should be empty at the end.
271+
elements1.size == elements2.size && {
272+
for (e <- elements2) elements1 = removeFirstSemanticEquivalent(elements1, e)
273+
elements1.isEmpty
274+
}
266275
}
267276

268277
override def genCode(ctx: CodeGenContext, ev: GeneratedExpressionCode): String = {
@@ -319,12 +328,20 @@ case class Or(left: Expression, right: Expression) extends BinaryOperator
319328
// Non-deterministic expressions cannot be semantic equal
320329
if (!deterministic || !other.deterministic) return false
321330

322-
// we know both expressions are Or, so we can tolerate ordering different
323-
// Recursively call semanticEquals on subexpressions to check the equivalency of two seqs.
331+
// We know both expressions are Or, so we can tolerate ordering different
324332
var elements1 = splitDisjunctivePredicates(this)
325333
val elements2 = splitDisjunctivePredicates(other)
326-
for (e <- elements2) elements1 = removeFirstSemanticEquivalent(elements1, e)
327-
elements1.isEmpty
334+
// We can recursively call semanticEquals to check the equivalency for subexpressions, but
335+
// there is no simple solution to compare the equivalency of sequence of expressions.
336+
// Expression class doesn't have order, so we couldn't sort them. We can neither use
337+
// set comparison as Set doesn't support custom compare function, which is semanticEquals.
338+
// To check the equivalency of elements1 and elements2, we first compare their size. Then
339+
// for each element in elements2, we remove its first semantically equivalent expression from
340+
// elements1. If they are semantically equivalent, elements1 should be empty at the end.
341+
elements1.size == elements2.size && {
342+
for (e <- elements2) elements1 = removeFirstSemanticEquivalent(elements1, e)
343+
elements1.isEmpty
344+
}
328345
}
329346

330347
override def genCode(ctx: CodeGenContext, ev: GeneratedExpressionCode): String = {

0 commit comments

Comments
 (0)