Skip to content
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

roachtest: costfuzz failed - incorrect results with array concatenation and full join #87919

Closed
mgartner opened this issue Sep 13, 2022 · 1 comment · Fixed by #88425
Closed
Assignees
Labels
C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. S-1 High impact: many users impacted, serious risk of high unavailability or data loss T-sql-queries SQL Queries Team

Comments

@mgartner
Copy link
Collaborator

mgartner commented Sep 13, 2022

Costfuzz discovered a correctness bug here. The two identical SELECT statements below return different results.

CREATE TABLE t1 (
  a  INT
);

CREATE TABLE t2 (
  b INT,
  c TIME[]
);

INSERT INTO t1 (a) VALUES (NULL);

INSERT INTO t2 (c) VALUES (ARRAY['03:23:06.042923']);

SELECT ('09:20:35.19023'::TIME || c)::TIME[] AS col_25551
FROM t1
FULL JOIN t2 ON a = b
ORDER BY a;

SET testing_optimizer_random_seed = 9004529874258270860;

SET testing_optimizer_cost_perturbation = 1.0;

SELECT ('09:20:35.19023'::TIME || c)::TIME[] AS col_25551
FROM t1
FULL JOIN t2 ON a = b
ORDER BY a;

The first one produces:

             col_25551
------------------------------------
  {09:20:35.19023,03:23:06.042923}
  {09:20:35.19023,03:23:06.042923}
(2 rows)

While the second one produces:

             col_25551
------------------------------------
  {09:20:35.19023}
  {09:20:35.19023,03:23:06.042923}
(2 rows)

The second one is the correct answer. The query plans are slightly different, but both look valid to me:

diff --git a/qp1 b/qp2
index 8eba362060..dc2fb51e6e 100644
--- a/qp1
+++ b/qp2
@@ -1,42 +1,41 @@
   distribute
    ├── columns: col_25551:10  [hidden: a:1]
    ├── immutable
    ├── stats: [rows=10000]
-   ├── cost: 5612.94388
+   ├── cost: 4135.70093
    ├── ordering: +1
    ├── distribution: us-east1
    ├── input distribution:
    ├── prune: (1,10)
-   └── sort
-        ├── columns: a:1 col_25551:10
-        ├── immutable
-        ├── stats: [rows=10000]
-        ├── cost: 5612.91388
-        ├── ordering: +1
-        ├── prune: (1,10)
    └── project
         ├── columns: col_25551:10 a:1
         ├── immutable
         ├── stats: [rows=10000]
-             ├── cost: 2539.82625
+        ├── cost: 4135.66162
+        ├── ordering: +1
         ├── prune: (1,10)
-             ├── full-join (hash)
+        ├── sort
+        │    ├── columns: a:1 b:5 c:6
+        │    ├── stats: [rows=10000]
+        │    ├── cost: 3876.3125
+        │    ├── ordering: +1
+        │    └── full-join (hash)
         │         ├── columns: a:1 b:5 c:6
         │         ├── stats: [rows=10000]
-             │    ├── cost: 2339.80625
+        │         ├── cost: 2712.64675
         │         ├── scan t1
         │         │    ├── columns: a:1
         │         │    ├── stats: [rows=1000, distinct(1)=100, null(1)=10]
-             │    │    ├── cost: 1084.62
+        │         │    ├── cost: 1026.49187
         │         │    ├── prune: (1)
         │         │    └── unfiltered-cols: (1-4)
         │         ├── scan t2
         │         │    ├── columns: b:5 c:6
         │         │    ├── stats: [rows=1000, distinct(5)=100, null(5)=10]
-             │    │    ├── cost: 1125.02
+        │         │    ├── cost: 1607.78625
         │         │    ├── prune: (5,6)
         │         │    └── unfiltered-cols: (5-9)
         │         └── filters
         │              └── a:1 = b:5 [outer=(1,5), constraints=(/1: (/NULL - ]; /5: (/NULL - ]), fd=(1)==(5), (5)==(1)]
         └── projections
              └── '09:20:35.19023' || c:6 [as=col_25551:10, outer=(6), immutable]

Jira issue: CRDB-19594

@mgartner mgartner added C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. S-1 High impact: many users impacted, serious risk of high unavailability or data loss labels Sep 13, 2022
@blathers-crl blathers-crl bot added the T-sql-queries SQL Queries Team label Sep 13, 2022
@DrewKimball
Copy link
Collaborator

DrewKimball commented Sep 14, 2022

This one is happening because of the null-handling (or lack thereof) for projecting a concatenation between datums in the vectorized engine. Currently, if the projection ignores nulls (all other projections), we check whether the argument is null and simply skip that row if it is. Otherwise, we read the value from the argument column and proceed as normal, even if the value is supposed to be null. My understanding of the way nulls are represented is that if the Nulls slice indicates a value is null, the actual value in the column could be anything and should be ignored. So probably the solution will be to explicitly pass a DNull value in the case when the argument is null and nulls are not ignored.

// {{if and (eq .Left.VecMethod "Datum") (eq .Right.VecMethod "Datum")}}
hasNullsAndNotCalledOnNullInput := vec.Nulls().MaybeHasNulls() && !p.calledOnNullInput
// {{else}}
hasNullsAndNotCalledOnNullInput := vec.Nulls().MaybeHasNulls()
// {{end}}

@DrewKimball DrewKimball self-assigned this Sep 15, 2022
@mgartner mgartner changed the title roachtest: costfuzz failed - incorrect results with array concatenation and full join sql: vectorized engine requires FoldInEmpty and FoldNotInEmpty normalization rules Sep 19, 2022
@mgartner mgartner changed the title sql: vectorized engine requires FoldInEmpty and FoldNotInEmpty normalization rules roachtest: costfuzz failed - incorrect results with array concatenation and full join Sep 19, 2022
DrewKimball added a commit to DrewKimball/cockroach that referenced this issue Sep 22, 2022
Most projections skip rows for which one or more arguments are null, and
just output a null for those rows. However, some projections can actually
accept null arguments. Previously, we were using the values from the vec
even when the `Nulls` bitmap was set for that row, which invalidates the
data in the vec for that row. This could cause a non-null value to be
unexpectedly concatenated to an array when an argument was null (nothing
should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed
vectors to explicitly set the argument to `tree.DNull` in the case when
the `Nulls` bitmap is set. This ensures that the projection is not
performed with the invalid (and arbitrary) value in the datum vec at that
index.

Fixes cockroachdb#87919

Release note (bug fix): Fixed a bug in `Concat` projection operators
for arrays that could cause non-null values to be added to the array
when one of the arguments was null.
DrewKimball added a commit to DrewKimball/cockroach that referenced this issue Sep 22, 2022
Most projections skip rows for which one or more arguments are null, and
just output a null for those rows. However, some projections can actually
accept null arguments. Previously, we were using the values from the vec
even when the `Nulls` bitmap was set for that row, which invalidates the
data in the vec for that row. This could cause a non-null value to be
unexpectedly concatenated to an array when an argument was null (nothing
should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed
vectors to explicitly set the argument to `tree.DNull` in the case when
the `Nulls` bitmap is set. This ensures that the projection is not
performed with the invalid (and arbitrary) value in the datum vec at that
index.

Fixes cockroachdb#87919

Release note (bug fix): Fixed a bug in `Concat` projection operators
for arrays that could cause non-null values to be added to the array
when one of the arguments was null.
craig bot pushed a commit that referenced this issue Sep 30, 2022
88425: colexec: use tree.DNull when projection is called on null input r=DrewKimball a=DrewKimball

Most projections skip rows for which one or more arguments are null, and just output a null for those rows. However, some projections can actually accept null arguments. Previously, we were using the values from the vec even when the `Nulls` bitmap was set for that row, which invalidates the data in the vec for that row. This could cause a non-null value to be unexpectedly concatenated to an array when an argument was null (nothing should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed vectors to explicitly set the argument to `tree.DNull` in the case when the `Nulls` bitmap is set. This ensures that the projection is not performed with the invalid (and arbitrary) value in the datum vec at that index.

Fixes #87919

Release note (bug fix): Fixed a bug in `Concat` projection operators for arrays that could cause non-null values to be added to the array when one of the arguments was null.

88671: util: avoid allocations when escaping multibyte characters r=[miretskiy,yuzefovich] a=HonoreDB

EncodeEscapedChar (which is called in EncodeSQLStringWithFlags) is pretty optimized, but for escaping a multibyte character it was using fmt.FPrintf, which means every multibyte character ended up on the heap due to golang/go#8618. This had a noticeable impact in changefeed benchmarking.

This commit just hand-compiles the two formatting strings that were being used into reasonably efficient go, eliminating the allocs.

Benchmark encoding the first 10000 runes shows a 4x speedup:

Before: BenchmarkEncodeNonASCIISQLString-16    	     944	   1216130 ns/op
After: BenchmarkEncodeNonASCIISQLString-16    	    3468	    300777 ns/op

Release note: None

Co-authored-by: DrewKimball <[email protected]>
Co-authored-by: Aaron Zinger <[email protected]>
@craig craig bot closed this as completed in 770898a Sep 30, 2022
blathers-crl bot pushed a commit that referenced this issue Sep 30, 2022
Most projections skip rows for which one or more arguments are null, and
just output a null for those rows. However, some projections can actually
accept null arguments. Previously, we were using the values from the vec
even when the `Nulls` bitmap was set for that row, which invalidates the
data in the vec for that row. This could cause a non-null value to be
unexpectedly concatenated to an array when an argument was null (nothing
should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed
vectors to explicitly set the argument to `tree.DNull` in the case when
the `Nulls` bitmap is set. This ensures that the projection is not
performed with the invalid (and arbitrary) value in the datum vec at that
index.

Fixes #87919

Release note (bug fix): Fixed a bug in `Concat` projection operators
for arrays that could cause non-null values to be added to the array
when one of the arguments was null.
blathers-crl bot pushed a commit that referenced this issue Oct 4, 2022
Most projections skip rows for which one or more arguments are null, and
just output a null for those rows. However, some projections can actually
accept null arguments. Previously, we were using the values from the vec
even when the `Nulls` bitmap was set for that row, which invalidates the
data in the vec for that row. This could cause a non-null value to be
unexpectedly concatenated to an array when an argument was null (nothing
should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed
vectors to explicitly set the argument to `tree.DNull` in the case when
the `Nulls` bitmap is set. This ensures that the projection is not
performed with the invalid (and arbitrary) value in the datum vec at that
index.

Fixes #87919

Release note (bug fix): Fixed a bug in `Concat` projection operators
for arrays that could cause non-null values to be added to the array
when one of the arguments was null.
DrewKimball added a commit to DrewKimball/cockroach that referenced this issue Nov 10, 2022
Most projections skip rows for which one or more arguments are null, and
just output a null for those rows. However, some projections can actually
accept null arguments. Previously, we were using the values from the vec
even when the `Nulls` bitmap was set for that row, which invalidates the
data in the vec for that row. This could cause a non-null value to be
unexpectedly concatenated to an array when an argument was null (nothing
should be added to the array in this case).

This commit modifies the projection operators that operate on datum-backed
vectors to explicitly set the argument to `tree.DNull` in the case when
the `Nulls` bitmap is set. This ensures that the projection is not
performed with the invalid (and arbitrary) value in the datum vec at that
index.

Fixes cockroachdb#87919

Release note (bug fix): Fixed a bug in `Concat` projection operators
for arrays that could cause non-null values to be added to the array
when one of the arguments was null.
@mgartner mgartner moved this to Done in SQL Queries Jul 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. S-1 High impact: many users impacted, serious risk of high unavailability or data loss T-sql-queries SQL Queries Team
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

2 participants