Skip to content

Wrong results: WHERE on inheritance parent column drops all rows when cross-joined with a distributed table through LEFT JOIN ... ON FALSE #8553

@mrigger

Description

@mrigger

Wrong results: WHERE on inheritance parent column drops all rows when cross-joined with a distributed table through LEFT JOIN ... ON FALSE

Summary

When a regular table that has an inheritance child is cross-joined with a distributed table and combined with LEFT JOIN ... ON FALSE, a WHERE predicate on a column of the inheritance parent always evaluates to false on the worker, so the query returns zero rows.

Versions affected

  • Citus 13.0 (citusdata/citus:13.0-pg16)
  • Citus 14.0 (citusdata/citus:postgres_17)
  • Citus nightly — extension 15.0-1 on PostgreSQL 18.1

Vanilla PostgreSQL 17 (clean postgres:17 image, shared_preload_libraries empty, citus extension not installed) returns the correct answer.

Repro

CREATE TABLE t0(c0 REAL);
CREATE TABLE t1() INHERITS(t0);
CREATE TABLE t3(c0 REAL);
SELECT create_distributed_table('t3', 'c0');
INSERT INTO t1(c0) VALUES (0.5);
INSERT INTO t3(c0) VALUES (1);

SELECT 1 FROM t3, t0 LEFT JOIN t1 ON FALSE;                          -- returns 1 (correct)
SELECT 1 FROM t3, t0 LEFT JOIN t1 ON FALSE WHERE t0.c0 IS NOT NULL;  -- returns 0 (expected: 1)
SELECT 1 FROM t3, t0 LEFT JOIN t1 ON FALSE WHERE TRUE;               -- returns 1 (correct)

t0.c0 is 0.5 (inherited from t1), so the second query should return one row.

Diagnosis

EXPLAIN (ANALYZE, VERBOSE) of the failing query shows the worker SQL Citus emits:

SELECT 1
FROM (SELECT NULL::real AS c0 WHERE false) t3(c0),
     ( (SELECT NULL::real AS c0
        FROM (SELECT intermediate_result."dummy-1"
              FROM read_intermediate_result('1_1','binary')
                   intermediate_result("dummy-1" integer)) t0_1) t0
       LEFT JOIN
       (SELECT NULL::real AS c0
        FROM (SELECT intermediate_result."dummy-1"
              FROM read_intermediate_result('1_2','binary')
                   intermediate_result("dummy-1" integer)) t1_1) t1
       ON (false) )
WHERE (t0.c0 IS NOT NULL)
->  Result   One-Time Filter: false

t0.c0 is rewritten to a literal NULL::real, so WHERE (NULL IS NOT NULL) constant-folds to false. The real values are pulled into intermediate_result but stay buried in the inner sub-select that nothing references.

Trigger conditions (each removal makes the bug disappear): inheritance child of t0, the cross-joined t3 being distributed, the LEFT JOIN ... ON FALSE, and a WHERE referencing the inheritance parent's column.


This bug was analyzed with the help of Claude.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions