Skip to content

Commit d06e2c0

Browse files
committed
Fix wrong results for WHERE on inheritance parent column with LEFT JOIN ON FALSE
When a local inheritance parent table is cross-joined with a distributed table through LEFT JOIN ... ON FALSE, PostgreSQL's expand_single_inheritance_child() creates child RTEs via memcpy, duplicating Citus's identity marker (values_lists). This causes RelationRestrictionForRelation() to return the child's restriction instead of the parent's. Since Vars in plannerInfo->parse still reference the parent's original rtable position, RequiredAttrNumbersForRelationInternal() finds no matching Vars, causing all columns to be replaced with NULL. Fix by adding an originalRteIndex parameter to RequiredAttrNumbersForRelation() that also searches at the RTE's original position in the query's rtable when it differs from the restriction's index. Fixes: #8553
1 parent c2bd6ae commit d06e2c0

5 files changed

Lines changed: 93 additions & 5 deletions

File tree

src/backend/distributed/planner/local_distributed_join_planner.c

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ AppendUniqueIndexColumnsToList(Form_pg_index indexForm, List **uniqueIndexGroups
476476
*/
477477
List *
478478
RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry,
479-
PlannerRestrictionContext *plannerRestrictionContext)
479+
PlannerRestrictionContext *plannerRestrictionContext,
480+
int originalRteIndex)
480481
{
481482
RelationRestriction *relationRestriction =
482483
RelationRestrictionForRelation(rangeTableEntry, plannerRestrictionContext);
@@ -498,7 +499,35 @@ RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry,
498499
*/
499500
Query *queryToProcess = plannerInfo->parse;
500501

501-
return RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex);
502+
List *result = RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex);
503+
504+
/*
505+
* When PostgreSQL expands inheritance tables, expand_single_inheritance_child()
506+
* copies the parent RTE via memcpy, which duplicates Citus's identity marker
507+
* (values_lists). This can cause RelationRestrictionForRelation() to return a
508+
* restriction for a child RTE whose index differs from the original parent
509+
* position. Since Vars in plannerInfo->parse still reference the parent's
510+
* original position, we must also search at originalRteIndex to find them.
511+
*/
512+
if (originalRteIndex > 0 && originalRteIndex != rteIndex)
513+
{
514+
List *additional = RequiredAttrNumbersForRelationInternal(queryToProcess,
515+
originalRteIndex);
516+
#if PG_VERSION_NUM >= 170000
517+
foreach_int(attrNo, additional)
518+
{
519+
result = list_append_unique_int(result, attrNo);
520+
}
521+
#else
522+
ListCell *lc;
523+
foreach(lc, additional)
524+
{
525+
result = list_append_unique_int(result, lfirst_int(lc));
526+
}
527+
#endif
528+
}
529+
530+
return result;
502531
}
503532

504533

@@ -541,9 +570,12 @@ CreateConversionCandidates(PlannerRestrictionContext *plannerRestrictionContext,
541570
palloc0(sizeof(ConversionCandidates));
542571

543572

573+
int rangeTableIndex = 0;
544574
RangeTblEntry *rangeTableEntry = NULL;
545575
foreach_declared_ptr(rangeTableEntry, rangeTableList)
546576
{
577+
rangeTableIndex++;
578+
547579
/* we're only interested in tables */
548580
if (!IsRecursivelyPlannableRelation(rangeTableEntry))
549581
{
@@ -566,7 +598,8 @@ CreateConversionCandidates(PlannerRestrictionContext *plannerRestrictionContext,
566598

567599
rangeTableEntryDetails->rangeTableEntry = rangeTableEntry;
568600
rangeTableEntryDetails->requiredAttributeNumbers =
569-
RequiredAttrNumbersForRelation(rangeTableEntry, plannerRestrictionContext);
601+
RequiredAttrNumbersForRelation(rangeTableEntry, plannerRestrictionContext,
602+
rangeTableIndex);
570603
rangeTableEntryDetails->hasConstantFilterOnUniqueColumn =
571604
HasConstantFilterOnUniqueColumn(rangeTableEntry, relationRestriction);
572605
rangeTableEntryDetails->perminfo = NULL;

src/backend/distributed/planner/recursive_planning.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,8 @@ RecursivelyPlanDistributedJoinNode(Node *node, Query *query,
971971
PlannerRestrictionContext *restrictionContext =
972972
GetPlannerRestrictionContext(recursivePlanningContext);
973973
List *requiredAttributes =
974-
RequiredAttrNumbersForRelation(distributedRte, restrictionContext);
974+
RequiredAttrNumbersForRelation(distributedRte, restrictionContext,
975+
rangeTableRef->rtindex);
975976

976977
RTEPermissionInfo *perminfo = NULL;
977978
if (distributedRte->perminfoindex)

src/include/distributed/local_distributed_join_planner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ extern void RecursivelyPlanLocalTableJoins(Query *query,
3333
RecursivePlanningContext *context);
3434
extern List * RequiredAttrNumbersForRelation(RangeTblEntry *relationRte,
3535
PlannerRestrictionContext *
36-
plannerRestrictionContext);
36+
plannerRestrictionContext,
37+
int originalRteIndex);
3738
extern List * RequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex);
3839

3940
#endif /* LOCAL_DISTRIBUTED_JOIN_PLANNER_H */

src/test/regress/expected/local_dist_join.out

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,3 +887,35 @@ SELECT COUNT(DISTINCT name) FROM distributed;
887887
(1 row)
888888

889889
ROLLBACK;
890+
-- Test for inheritance parent column in WHERE with LEFT JOIN ON FALSE
891+
-- Regression test for https://github.com/citusdata/citus/issues/8553
892+
-- When a local inheritance parent table is cross-joined with a distributed
893+
-- table through LEFT JOIN ... ON FALSE, a WHERE clause on the parent column
894+
-- should not incorrectly drop all rows.
895+
SET citus.use_citus_managed_tables TO off;
896+
CREATE TABLE inh_parent(c0 REAL);
897+
CREATE TABLE inh_child(c1 INT) INHERITS (inh_parent);
898+
RESET citus.use_citus_managed_tables;
899+
INSERT INTO inh_child VALUES (1.0, 1);
900+
-- This query should return 101 rows (1 child row x 101 distributed rows)
901+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 IS NOT NULL;
902+
count
903+
---------------------------------------------------------------------
904+
101
905+
(1 row)
906+
907+
-- Additional variations to test the same pattern
908+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 = 1;
909+
count
910+
---------------------------------------------------------------------
911+
101
912+
(1 row)
913+
914+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE;
915+
count
916+
---------------------------------------------------------------------
917+
101
918+
(1 row)
919+
920+
DROP TABLE inh_child;
921+
DROP TABLE inh_parent;

src/test/regress/sql/local_dist_join.sql

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,24 @@ WHERE
341341
distributed.id = local.id;
342342
SELECT COUNT(DISTINCT name) FROM distributed;
343343
ROLLBACK;
344+
345+
-- Test for inheritance parent column in WHERE with LEFT JOIN ON FALSE
346+
-- Regression test for https://github.com/citusdata/citus/issues/8553
347+
-- When a local inheritance parent table is cross-joined with a distributed
348+
-- table through LEFT JOIN ... ON FALSE, a WHERE clause on the parent column
349+
-- should not incorrectly drop all rows.
350+
SET citus.use_citus_managed_tables TO off;
351+
CREATE TABLE inh_parent(c0 REAL);
352+
CREATE TABLE inh_child(c1 INT) INHERITS (inh_parent);
353+
RESET citus.use_citus_managed_tables;
354+
INSERT INTO inh_child VALUES (1.0, 1);
355+
356+
-- This query should return 101 rows (1 child row x 101 distributed rows)
357+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 IS NOT NULL;
358+
359+
-- Additional variations to test the same pattern
360+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 = 1;
361+
SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE;
362+
363+
DROP TABLE inh_child;
364+
DROP TABLE inh_parent;

0 commit comments

Comments
 (0)