cockroachdb / cockroachdb/cockroach

tuple `(a, b) >= ANY (correlated subquery)` evaluated in a derived-table projection returns NULL instead of TRUE when the first tuple component decides the order, silently dropping rows

Open
#175,439 2 comments 0 reactions 0 assignees View on GitHub
C-bug O-community S-3-erroneous-edge-case T-sql-queries X-blathers-triaged
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Describe the problem**

A tuple quantified comparison `(a, b) >= ANY (SELECT x, y FROM WHERE )`
diverges between the WHERE path and the relocated projection path when the produced tuple has a NULL
second component.

The tuple comparison itself is correct in isolation — `(1, 5) >= (0, NULL)` evaluates to TRUE because
the first component already decides the lexicographic order — and the WHERE form also returns the row.
But the same predicate evaluated as a derived-table projection returns NULL instead of TRUE, so the
row is silently lost.

**To Reproduce**

1. Start a single-node CockroachDB (tested on v26.3.1, docker image `cockroachdb/cockroach:v26.3.1`,
`cockroach start-single-node --insecure`).

2. Create the schema and one row per table:

```sql
CREATE TABLE t29 (c0 INT8, rowid INT8);
CREATE TABLE t70 (c0 FLOAT8, rowid INT8);
CREATE TABLE t0 (rowid INT8);
INSERT INTO t29 VALUES (-471377708, 1209922362678116353);
INSERT INTO t70 VALUES (NULL, -471377708);
INSERT INTO t0 VALUES (-1664603939);
```

3. Run the two equivalent forms:

```sql
-- (a) predicate in WHERE
SELECT t29.c0, t29.rowid FROM t29
WHERE (t29.c0, t29.rowid) >= ANY (
SELECT t0.rowid, t70.c0 FROM t70, t0 WHERE (t70.rowid = t29.c0) AND (true));

-- (b) the same predicate relocated into a derived-table projection
SELECT ref0 FROM (
SELECT t29.c0 AS ref0, t29.rowid AS ref1,
((t29.c0, t29.rowid) >= ANY (
SELECT t0.rowid, t70.c0 FROM t70, t0 WHERE (t70.rowid = t29.c0) AND (true))) AS ref2
FROM t29
) AS s WHERE ref2;
```

4. See the disagreement:

```
(a) c0 = -471377708, rowid = 1209922362678116353 -- 1 row <-- correct
(b) (empty result) -- 0 rows <-- WRONG, row is lost
```

The subquery produces a single tuple `(-1664603939, NULL)`. The comparison
`(-471377708, 1209922362678116353) >= (-1664603939, NULL)` must be TRUE because the first components
already decide the order (`-471377708 > -1664603939`), exactly as the bare evaluation confirms:

```sql
SELECT (1, 5) >= (0, NULL::FLOAT8); -- true (first component short-circuits)
SELECT (1, 5) >= ANY (SELECT 0, NULL::FLOAT8); -- true
```

**Expected behavior**
Both equivalent forms must return the same row. The relocated projection form must evaluate the tuple
`>= ANY` comparison to TRUE whenever the first tuple component decides the order, regardless of a NULL
later component.

**Additional data / screenshots**
Schema and queries are included above (bare columns, one row per table — nothing else is needed).

Element analysis: both the correlated key and the two-table (cross join) subquery are required — an
uncorrelated two-table subquery and a single-table correlated subquery do NOT diverge with the same
data shape. Plan evidence: the WHERE form is rewritten to a `column20 <= (c0, rowid)` aggregate
comparison over a hash-joined subquery, while the relocated form keeps the correlated ANY as a
per-row evaluation whose tuple comparison is dragged to NULL by the NULL second component instead of
short-circuiting on the first component.

**Environment:**
- CockroachDB version: CCL v26.3.1 (docker image `cockroachdb/cockroach:v26.3.1`, built 2026/08/24)
- Server OS: official Docker image (Linux x86_64 host, Ubuntu 20.04)
- Client app: built-in `cockroach sql` CLI (also reproduced via JDBC)

**Additional context**
What was the impact?

Add any other context about the problem here.

Jira issue: CRDB-68373

Contributor guide

Open the contributing guide

Research direction

Start by running the two SQL forms in the built-in cockroach sql CLI against the supplied single-node schema and compare their results. Then trace the WHERE rewrite to the aggregate comparison and the relocated projection's correlated ANY per-row evaluation, focusing on tuple NULL propagation and lexicographic short-circuiting. Done means both forms return the same row and the comparison remains TRUE when the first component decides the order.

Written by the indexing model from the issue text.

Assessment

Tech stack
sql
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.