Skip to content

[SPARK-17618] Fix invalid comparisons between UnsafeRow and other row formats #15185

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

Closed
wants to merge 3 commits into from

Conversation

JoshRosen
Copy link
Contributor

What changes were proposed in this pull request?

This patch addresses a correctness bug in Spark 1.6.x in where coalesce() declares that it can process UnsafeRows but mis-declares that it always outputs safe rows. If UnsafeRow and other Row types are compared for equality then we will get spurious false comparisons, leading to wrong answers in operators which perform whole-row comparison (such as distinct() or except()). An example of a query impacted by this bug is given in the JIRA ticket.

The problem is that the validity of our row format conversion rules depends on operators which handle unsafeRows (signalled by overriding canProcessUnsafeRows) correctly reporting their output row format (which is done by overriding outputsUnsafeRows). In #9024, we overrode canProcessUnsafeRows but forgot to override outputsUnsafeRows, leading to the incorrect equals() comparison.

Our interface design is flawed because correctness depends on operators correctly overriding multiple methods this problem could have been prevented by a design which coupled row format methods / metadata into a single method / class so that all three methods had to be overridden at the same time.

This patch addresses this issue by adding missing outputsUnsafeRows overrides. In order to ensure that bugs in this logic are uncovered sooner, I have modified UnsafeRow.equals() to throw an IllegalArgumentException if it is called with an object that is not an UnsafeRow.

How was this patch tested?

I believe that the stronger misuse-checking in UnsafeRow.equals() is sufficient to detect and prevent this class of bug.

@SparkQA
Copy link

SparkQA commented Sep 21, 2016

Test build #65733 has finished for PR 15185 at commit 9d4cf44.

  • This patch fails Spark unit tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@hvanhovell
Copy link
Contributor

LGTM

@SparkQA
Copy link

SparkQA commented Sep 21, 2016

Test build #65734 has finished for PR 15185 at commit 1319e82.

  • This patch fails PySpark unit tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

@JoshRosen
Copy link
Contributor Author

Jenkins, retest this please.

@SparkQA
Copy link

SparkQA commented Sep 21, 2016

Test build #65741 has finished for PR 15185 at commit 1319e82.

  • This patch fails to build.
  • This patch merges cleanly.
  • This patch adds no public classes.

@JoshRosen
Copy link
Contributor Author

Jenkins, retest this please.

@SparkQA
Copy link

SparkQA commented Sep 22, 2016

Test build #65750 has finished for PR 15185 at commit 1319e82.

  • This patch passes all tests.
  • This patch merges cleanly.
  • This patch adds no public classes.

asfgit pushed a commit that referenced this pull request Sep 27, 2016
… formats

## What changes were proposed in this pull request?

This patch addresses a correctness bug in Spark 1.6.x in where `coalesce()` declares that it can process `UnsafeRows` but mis-declares that it always outputs safe rows. If UnsafeRow and other Row types are compared for equality then we will get spurious `false` comparisons, leading to wrong answers in operators which perform whole-row comparison (such as `distinct()` or `except()`). An example of a query impacted by this bug is given in the [JIRA ticket](https://issues.apache.org/jira/browse/SPARK-17618).

The problem is that the validity of our row format conversion rules depends on operators which handle `unsafeRows` (signalled by overriding `canProcessUnsafeRows`) correctly reporting their output row format (which is done by overriding `outputsUnsafeRows`). In #9024, we overrode `canProcessUnsafeRows` but forgot to override `outputsUnsafeRows`, leading to the incorrect `equals()` comparison.

Our interface design is flawed because correctness depends on operators correctly overriding multiple methods this problem could have been prevented by a design which coupled row format methods / metadata into a single method / class so that all three methods had to be overridden at the same time.

This patch addresses this issue by adding missing `outputsUnsafeRows` overrides. In order to ensure that bugs in this logic are uncovered sooner, I have modified `UnsafeRow.equals()` to throw an `IllegalArgumentException` if it is called with an object that is not an `UnsafeRow`.

## How was this patch tested?

I believe that the stronger misuse-checking in `UnsafeRow.equals()` is sufficient to detect and prevent this class of bug.

Author: Josh Rosen <joshrosen@databricks.com>

Closes #15185 from JoshRosen/SPARK-17618.
@hvanhovell
Copy link
Contributor

Merging to master. Thanks!

@JoshRosen JoshRosen closed this Sep 27, 2016
@JoshRosen JoshRosen deleted the SPARK-17618 branch September 27, 2016 18:19
asfgit pushed a commit that referenced this pull request Sep 27, 2016
… other formats

This patch ports changes from #15185 to Spark 2.x. In that patch, a  correctness bug in Spark 1.6.x which was caused by an invalid `equals()` comparison between an `UnsafeRow` and another row of a different format. Spark 2.x is not affected by that specific correctness bug but it can still reap the error-prevention benefits of that patch's changes, which modify  ``UnsafeRow.equals()` to throw an IllegalArgumentException if it is called with an object that is not an `UnsafeRow`.

Author: Josh Rosen <joshrosen@databricks.com>

Closes #15265 from JoshRosen/SPARK-17618-master.
asfgit pushed a commit that referenced this pull request Sep 27, 2016
… other formats

This patch ports changes from #15185 to Spark 2.x. In that patch, a  correctness bug in Spark 1.6.x which was caused by an invalid `equals()` comparison between an `UnsafeRow` and another row of a different format. Spark 2.x is not affected by that specific correctness bug but it can still reap the error-prevention benefits of that patch's changes, which modify  ``UnsafeRow.equals()` to throw an IllegalArgumentException if it is called with an object that is not an `UnsafeRow`.

Author: Josh Rosen <joshrosen@databricks.com>

Closes #15265 from JoshRosen/SPARK-17618-master.

(cherry picked from commit 2f84a68)
Signed-off-by: Josh Rosen <joshrosen@databricks.com>
zzcclp pushed a commit to zzcclp/spark that referenced this pull request Sep 28, 2016
… formats

## What changes were proposed in this pull request?

This patch addresses a correctness bug in Spark 1.6.x in where `coalesce()` declares that it can process `UnsafeRows` but mis-declares that it always outputs safe rows. If UnsafeRow and other Row types are compared for equality then we will get spurious `false` comparisons, leading to wrong answers in operators which perform whole-row comparison (such as `distinct()` or `except()`). An example of a query impacted by this bug is given in the [JIRA ticket](https://issues.apache.org/jira/browse/SPARK-17618).

The problem is that the validity of our row format conversion rules depends on operators which handle `unsafeRows` (signalled by overriding `canProcessUnsafeRows`) correctly reporting their output row format (which is done by overriding `outputsUnsafeRows`). In apache#9024, we overrode `canProcessUnsafeRows` but forgot to override `outputsUnsafeRows`, leading to the incorrect `equals()` comparison.

Our interface design is flawed because correctness depends on operators correctly overriding multiple methods this problem could have been prevented by a design which coupled row format methods / metadata into a single method / class so that all three methods had to be overridden at the same time.

This patch addresses this issue by adding missing `outputsUnsafeRows` overrides. In order to ensure that bugs in this logic are uncovered sooner, I have modified `UnsafeRow.equals()` to throw an `IllegalArgumentException` if it is called with an object that is not an `UnsafeRow`.

## How was this patch tested?

I believe that the stronger misuse-checking in `UnsafeRow.equals()` is sufficient to detect and prevent this class of bug.

Author: Josh Rosen <joshrosen@databricks.com>

Closes apache#15185 from JoshRosen/SPARK-17618.

(cherry picked from commit e2ce0ca)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants