diff --git a/src/backend/distributed/planner/local_distributed_join_planner.c b/src/backend/distributed/planner/local_distributed_join_planner.c index 7b44a9c219d..42a3e5cd22c 100644 --- a/src/backend/distributed/planner/local_distributed_join_planner.c +++ b/src/backend/distributed/planner/local_distributed_join_planner.c @@ -476,7 +476,8 @@ AppendUniqueIndexColumnsToList(Form_pg_index indexForm, List **uniqueIndexGroups */ List * RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry, - PlannerRestrictionContext *plannerRestrictionContext) + PlannerRestrictionContext *plannerRestrictionContext, + int originalRteIndex) { RelationRestriction *relationRestriction = RelationRestrictionForRelation(rangeTableEntry, plannerRestrictionContext); @@ -498,7 +499,35 @@ RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry, */ Query *queryToProcess = plannerInfo->parse; - return RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex); + List *result = RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex); + + /* + * When PostgreSQL expands inheritance tables, expand_single_inheritance_child() + * copies the parent RTE via memcpy, which duplicates Citus's identity marker + * (values_lists). This can cause RelationRestrictionForRelation() to return a + * restriction for a child RTE whose index differs from the original parent + * position. Since Vars in plannerInfo->parse still reference the parent's + * original position, we must also search at originalRteIndex to find them. + */ + if (originalRteIndex > 0 && originalRteIndex != rteIndex) + { + List *additional = RequiredAttrNumbersForRelationInternal(queryToProcess, + originalRteIndex); +#if PG_VERSION_NUM >= 170000 + foreach_int(attrNo, additional) + { + result = list_append_unique_int(result, attrNo); + } +#else + ListCell *lc; + foreach(lc, additional) + { + result = list_append_unique_int(result, lfirst_int(lc)); + } +#endif + } + + return result; } @@ -541,9 +570,12 @@ CreateConversionCandidates(PlannerRestrictionContext *plannerRestrictionContext, palloc0(sizeof(ConversionCandidates)); + int rangeTableIndex = 0; RangeTblEntry *rangeTableEntry = NULL; foreach_declared_ptr(rangeTableEntry, rangeTableList) { + rangeTableIndex++; + /* we're only interested in tables */ if (!IsRecursivelyPlannableRelation(rangeTableEntry)) { @@ -566,7 +598,8 @@ CreateConversionCandidates(PlannerRestrictionContext *plannerRestrictionContext, rangeTableEntryDetails->rangeTableEntry = rangeTableEntry; rangeTableEntryDetails->requiredAttributeNumbers = - RequiredAttrNumbersForRelation(rangeTableEntry, plannerRestrictionContext); + RequiredAttrNumbersForRelation(rangeTableEntry, plannerRestrictionContext, + rangeTableIndex); rangeTableEntryDetails->hasConstantFilterOnUniqueColumn = HasConstantFilterOnUniqueColumn(rangeTableEntry, relationRestriction); rangeTableEntryDetails->perminfo = NULL; diff --git a/src/backend/distributed/planner/recursive_planning.c b/src/backend/distributed/planner/recursive_planning.c index faad287c3e0..d0ae06acd1a 100644 --- a/src/backend/distributed/planner/recursive_planning.c +++ b/src/backend/distributed/planner/recursive_planning.c @@ -971,7 +971,8 @@ RecursivelyPlanDistributedJoinNode(Node *node, Query *query, PlannerRestrictionContext *restrictionContext = GetPlannerRestrictionContext(recursivePlanningContext); List *requiredAttributes = - RequiredAttrNumbersForRelation(distributedRte, restrictionContext); + RequiredAttrNumbersForRelation(distributedRte, restrictionContext, + rangeTableRef->rtindex); RTEPermissionInfo *perminfo = NULL; if (distributedRte->perminfoindex) diff --git a/src/include/distributed/local_distributed_join_planner.h b/src/include/distributed/local_distributed_join_planner.h index 3390ab213eb..714e3dde969 100644 --- a/src/include/distributed/local_distributed_join_planner.h +++ b/src/include/distributed/local_distributed_join_planner.h @@ -33,7 +33,8 @@ extern void RecursivelyPlanLocalTableJoins(Query *query, RecursivePlanningContext *context); extern List * RequiredAttrNumbersForRelation(RangeTblEntry *relationRte, PlannerRestrictionContext * - plannerRestrictionContext); + plannerRestrictionContext, + int originalRteIndex); extern List * RequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex); #endif /* LOCAL_DISTRIBUTED_JOIN_PLANNER_H */ diff --git a/src/test/regress/expected/local_dist_join.out b/src/test/regress/expected/local_dist_join.out index 68a71bbc1aa..5ae8ef59cdc 100644 --- a/src/test/regress/expected/local_dist_join.out +++ b/src/test/regress/expected/local_dist_join.out @@ -887,3 +887,35 @@ SELECT COUNT(DISTINCT name) FROM distributed; (1 row) ROLLBACK; +-- Test for inheritance parent column in WHERE with LEFT JOIN ON FALSE +-- Regression test for https://github.com/citusdata/citus/issues/8553 +-- When a local inheritance parent table is cross-joined with a distributed +-- table through LEFT JOIN ... ON FALSE, a WHERE clause on the parent column +-- should not incorrectly drop all rows. +SET citus.use_citus_managed_tables TO off; +CREATE TABLE inh_parent(c0 REAL); +CREATE TABLE inh_child(c1 INT) INHERITS (inh_parent); +RESET citus.use_citus_managed_tables; +INSERT INTO inh_child VALUES (1.0, 1); +-- This query should return 101 rows (1 child row x 101 distributed rows) +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 IS NOT NULL; + count +--------------------------------------------------------------------- + 101 +(1 row) + +-- Additional variations to test the same pattern +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 = 1; + count +--------------------------------------------------------------------- + 101 +(1 row) + +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE; + count +--------------------------------------------------------------------- + 101 +(1 row) + +DROP TABLE inh_child; +DROP TABLE inh_parent; diff --git a/src/test/regress/sql/local_dist_join.sql b/src/test/regress/sql/local_dist_join.sql index f92c3f2c95d..32773d5b696 100644 --- a/src/test/regress/sql/local_dist_join.sql +++ b/src/test/regress/sql/local_dist_join.sql @@ -341,3 +341,24 @@ WHERE distributed.id = local.id; SELECT COUNT(DISTINCT name) FROM distributed; ROLLBACK; + +-- Test for inheritance parent column in WHERE with LEFT JOIN ON FALSE +-- Regression test for https://github.com/citusdata/citus/issues/8553 +-- When a local inheritance parent table is cross-joined with a distributed +-- table through LEFT JOIN ... ON FALSE, a WHERE clause on the parent column +-- should not incorrectly drop all rows. +SET citus.use_citus_managed_tables TO off; +CREATE TABLE inh_parent(c0 REAL); +CREATE TABLE inh_child(c1 INT) INHERITS (inh_parent); +RESET citus.use_citus_managed_tables; +INSERT INTO inh_child VALUES (1.0, 1); + +-- This query should return 101 rows (1 child row x 101 distributed rows) +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 IS NOT NULL; + +-- Additional variations to test the same pattern +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE WHERE inh_child.c0 = 1; +SELECT count(*) FROM distributed, inh_child LEFT JOIN inh_parent ON FALSE; + +DROP TABLE inh_child; +DROP TABLE inh_parent;