Skip to content

Commit 9a48df9

Browse files
Furkan AkgulFurkan Akgul
authored andcommitted
Fix wrong results for NOT (x IS DISTINCT FROM y) with recursive planning
When ReplaceRTERelationWithRteSubquery wraps a local table into a subquery, restrictions are pushed into the subplan but remain on the outer query. If restriction-referenced columns are not in requiredAttrNumbers, the outer subquery projects them as NULL, causing the outer WHERE to evaluate incorrectly (e.g., NOT (0 IS DISTINCT FROM NULL) is always FALSE). Fix by including restriction-referenced column Vars in requiredAttrNumbers before building the subquery. Fixes #8468 Made-with: Cursor
1 parent 366fd64 commit 9a48df9

File tree

4 files changed

+220
-3
lines changed

4 files changed

+220
-3
lines changed

src/backend/distributed/planner/recursive_planning.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,14 +1843,23 @@ ReplaceRTERelationWithRteSubquery(RangeTblEntry *rangeTableEntry,
18431843
RecursivePlanningContext *context,
18441844
RTEPermissionInfo *perminfo)
18451845
{
1846+
List *restrictionList =
1847+
GetRestrictInfoListForRelation(rangeTableEntry,
1848+
context->plannerRestrictionContext);
1849+
1850+
List *restrictionVars = pull_var_clause_default((Node *) restrictionList);
1851+
Var *restrictionVar = NULL;
1852+
foreach_declared_ptr(restrictionVar, restrictionVars)
1853+
{
1854+
requiredAttrNumbers = list_append_unique_int(requiredAttrNumbers,
1855+
restrictionVar->varattno);
1856+
}
1857+
18461858
Query *subquery = WrapRteRelationIntoSubquery(rangeTableEntry, requiredAttrNumbers,
18471859
perminfo);
18481860
List *outerQueryTargetList = CreateAllTargetListForRelation(rangeTableEntry->relid,
18491861
requiredAttrNumbers);
18501862

1851-
List *restrictionList =
1852-
GetRestrictInfoListForRelation(rangeTableEntry,
1853-
context->plannerRestrictionContext);
18541863
List *copyRestrictionList = copyObject(restrictionList);
18551864
Expr *andedBoundExpressions = make_ands_explicit(copyRestrictionList);
18561865
subquery->jointree->quals = (Node *) andedBoundExpressions;
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
--
2+
-- ISSUE_8468_NUM_NULLS
3+
--
4+
-- Test for GitHub issue #8468: TLP test fail on sqlancer due to
5+
-- num_nulls interpreted differently on workers.
6+
-- Root cause: when wrapping local tables into subqueries during
7+
-- recursive planning, restriction-referenced columns were projected
8+
-- as NULL in the outer subquery, causing wrong WHERE clause results.
9+
--
10+
CREATE SCHEMA issue_8468;
11+
SET search_path TO issue_8468;
12+
SET citus.shard_count TO 4;
13+
SET citus.next_shard_id TO 9468000;
14+
CREATE TABLE t5(c0 FLOAT);
15+
SELECT create_distributed_table('t5', 'c0');
16+
create_distributed_table
17+
---------------------------------------------------------------------
18+
19+
(1 row)
20+
21+
INSERT INTO t5(c0) VALUES(0.009452163), (1.4691802E9), (0.005109378), (0.6941109),
22+
(0.7013781), (0.8670044), (-1.6739732E9), (-4.5730365E8);
23+
CREATE TABLE t1_parent(c0 FLOAT);
24+
CREATE TABLE t2_child(c0 FLOAT, c1 CHAR(20), c2 DECIMAL) INHERITS(t1_parent);
25+
NOTICE: merging column "c0" with inherited definition
26+
INSERT INTO t1_parent(c0) VALUES(-1.6739732E9), (0), (0.32921866), ('-Infinity');
27+
INSERT INTO t2_child(c1, c2, c0) VALUES('', 0.19, 0), ('test', 0.33, 0.89),
28+
('abc', 0.58, 0.68), ('', 0.18, 0.74), ('', 0.22, 0.71);
29+
CREATE TEMP TABLE t0(c0 FLOAT);
30+
INSERT INTO t0(c0) VALUES(-1.9619044E9), (0.18373421), (6.175733E8), (0.58579546);
31+
-- t1_parent* = 9 rows (4 from parent + 5 from child)
32+
SELECT count(*) AS parent_star_count FROM t1_parent;
33+
parent_star_count
34+
---------------------------------------------------------------------
35+
9
36+
(1 row)
37+
38+
-- Original: 9 * (4*8) = 288
39+
SELECT count(*) AS original FROM (
40+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
41+
) sub;
42+
original
43+
---------------------------------------------------------------------
44+
288
45+
(1 row)
46+
47+
-- TLP branches should sum to original
48+
SELECT count(*) AS branch_true FROM (
49+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
50+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0)
51+
) sub;
52+
branch_true
53+
---------------------------------------------------------------------
54+
224
55+
(1 row)
56+
57+
SELECT count(*) AS branch_false FROM (
58+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
59+
WHERE NOT (0::double precision IS DISTINCT FROM t1_parent.c0)
60+
) sub;
61+
branch_false
62+
---------------------------------------------------------------------
63+
64
64+
(1 row)
65+
66+
SELECT count(*) AS branch_null FROM (
67+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
68+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0) IS NULL
69+
) sub;
70+
branch_null
71+
---------------------------------------------------------------------
72+
0
73+
(1 row)
74+
75+
-- TLP combined must equal original
76+
SELECT count(*) AS tlp_total FROM (
77+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
78+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0)
79+
UNION ALL
80+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
81+
WHERE NOT (0::double precision IS DISTINCT FROM t1_parent.c0)
82+
UNION ALL
83+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
84+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0) IS NULL
85+
) sub;
86+
tlp_total
87+
---------------------------------------------------------------------
88+
288
89+
(1 row)
90+
91+
-- Verify with num_nulls (the original condition from the bug report)
92+
SELECT count(*) AS num_nulls_tlp FROM (
93+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
94+
WHERE (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0)
95+
UNION ALL
96+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
97+
WHERE NOT (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0)
98+
UNION ALL
99+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
100+
WHERE (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0) IS NULL
101+
) sub;
102+
num_nulls_tlp
103+
---------------------------------------------------------------------
104+
288
105+
(1 row)
106+
107+
-- Also test the plain equality condition
108+
SELECT count(*) AS equals_zero FROM (
109+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
110+
WHERE t1_parent.c0 = 0
111+
) sub;
112+
equals_zero
113+
---------------------------------------------------------------------
114+
64
115+
(1 row)
116+
117+
-- Clean up
118+
SET client_min_messages TO ERROR;
119+
DROP SCHEMA issue_8468 CASCADE;

src/test/regress/multi_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ test: multi_deparse_shard_query multi_distributed_transaction_id intermediate_re
7979
test: multi_explain
8080
test: hyperscale_tutorial partitioned_intermediate_results distributed_intermediate_results multi_real_time_transaction
8181
test: multi_basic_queries cross_join multi_complex_expressions multi_subquery multi_subquery_complex_queries multi_subquery_behavioral_analytics
82+
test: issue_8468_num_nulls
8283
test: multi_subquery_complex_reference_clause multi_subquery_window_functions multi_view multi_sql_function multi_prepare_sql
8384
test: sql_procedure multi_function_in_join row_types materialized_view
8485
test: multi_subquery_in_where_reference_clause adaptive_executor propagate_set_commands geqo
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
--
2+
-- ISSUE_8468_NUM_NULLS
3+
--
4+
-- Test for GitHub issue #8468: TLP test fail on sqlancer due to
5+
-- num_nulls interpreted differently on workers.
6+
-- Root cause: when wrapping local tables into subqueries during
7+
-- recursive planning, restriction-referenced columns were projected
8+
-- as NULL in the outer subquery, causing wrong WHERE clause results.
9+
--
10+
11+
CREATE SCHEMA issue_8468;
12+
SET search_path TO issue_8468;
13+
SET citus.shard_count TO 4;
14+
SET citus.next_shard_id TO 9468000;
15+
16+
CREATE TABLE t5(c0 FLOAT);
17+
SELECT create_distributed_table('t5', 'c0');
18+
19+
INSERT INTO t5(c0) VALUES(0.009452163), (1.4691802E9), (0.005109378), (0.6941109),
20+
(0.7013781), (0.8670044), (-1.6739732E9), (-4.5730365E8);
21+
22+
CREATE TABLE t1_parent(c0 FLOAT);
23+
CREATE TABLE t2_child(c0 FLOAT, c1 CHAR(20), c2 DECIMAL) INHERITS(t1_parent);
24+
25+
INSERT INTO t1_parent(c0) VALUES(-1.6739732E9), (0), (0.32921866), ('-Infinity');
26+
INSERT INTO t2_child(c1, c2, c0) VALUES('', 0.19, 0), ('test', 0.33, 0.89),
27+
('abc', 0.58, 0.68), ('', 0.18, 0.74), ('', 0.22, 0.71);
28+
29+
CREATE TEMP TABLE t0(c0 FLOAT);
30+
INSERT INTO t0(c0) VALUES(-1.9619044E9), (0.18373421), (6.175733E8), (0.58579546);
31+
32+
-- t1_parent* = 9 rows (4 from parent + 5 from child)
33+
SELECT count(*) AS parent_star_count FROM t1_parent;
34+
35+
-- Original: 9 * (4*8) = 288
36+
SELECT count(*) AS original FROM (
37+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
38+
) sub;
39+
40+
-- TLP branches should sum to original
41+
SELECT count(*) AS branch_true FROM (
42+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
43+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0)
44+
) sub;
45+
46+
SELECT count(*) AS branch_false FROM (
47+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
48+
WHERE NOT (0::double precision IS DISTINCT FROM t1_parent.c0)
49+
) sub;
50+
51+
SELECT count(*) AS branch_null FROM (
52+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
53+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0) IS NULL
54+
) sub;
55+
56+
-- TLP combined must equal original
57+
SELECT count(*) AS tlp_total FROM (
58+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
59+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0)
60+
UNION ALL
61+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
62+
WHERE NOT (0::double precision IS DISTINCT FROM t1_parent.c0)
63+
UNION ALL
64+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
65+
WHERE (0::double precision IS DISTINCT FROM t1_parent.c0) IS NULL
66+
) sub;
67+
68+
-- Verify with num_nulls (the original condition from the bug report)
69+
SELECT count(*) AS num_nulls_tlp FROM (
70+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
71+
WHERE (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0)
72+
UNION ALL
73+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
74+
WHERE NOT (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0)
75+
UNION ALL
76+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
77+
WHERE (num_nulls(t1_parent.c0) IS DISTINCT FROM t1_parent.c0) IS NULL
78+
) sub;
79+
80+
-- Also test the plain equality condition
81+
SELECT count(*) AS equals_zero FROM (
82+
SELECT t5.c0 FROM t1_parent, t0 LEFT OUTER JOIN t5 ON (True)
83+
WHERE t1_parent.c0 = 0
84+
) sub;
85+
86+
-- Clean up
87+
SET client_min_messages TO ERROR;
88+
DROP SCHEMA issue_8468 CASCADE;

0 commit comments

Comments
 (0)