From 91d5c1af46e7d2c37dc384a7faa24aeaed592ac8 Mon Sep 17 00:00:00 2001 From: Richard Guo Date: Sun, 20 Sep 2026 10:13:25 +0900 Subject: [PATCH v1] Don't use SubPlan-containing expressions for startup-time pruning match_clause_to_partition_key() tells whether a comparison value can be evaluated during executor-startup pruning by checking that it has no Vars, no volatile functions and no PARAM_EXEC Params. A SubPlan normally fails the last test, since the testexpr of an ANY/ALL SubPlan references the subplan's output Params. But if the left-hand side is a NULL constant, the strict comparison operator in the testexpr is const-folded to a NULL Const, and an uncorrelated SubPlan has no parParam or args either, so nothing is left for pull_exec_paramids() to find. The expression then reaches initial_pruning_steps, where ExecDoInitialPruning() must evaluate it before any PlanState exists, and we fail with "SubPlan found with no parent plan". To fix, don't allow such an expression to be used for startup-time pruning. It's still fine for per-scan pruning, which has a PlanState to evaluate it with. Expressions containing exec Params are unaffected. Back-patch to all supported branches. --- src/backend/partitioning/partprune.c | 12 +++++++++++- src/test/regress/expected/partition_prune.out | 16 ++++++++++++++++ src/test/regress/sql/partition_prune.sql | 6 ++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 2efb2c864de..f128b99c36a 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -46,6 +46,7 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/appendinfo.h" +#include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/optimizer.h" #include "optimizer/pathnode.h" @@ -2073,7 +2074,11 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, /* * See if there are any exec Params. If so, we can only use this - * expression during per-scan pruning. + * expression during per-scan pruning. The same is true if it + * contains a SubPlan, as evaluating that requires the parent + * node's PlanState, which startup-time pruning hasn't got. (A + * SubPlan's testexpr normally contains exec Params, so we'd have + * caught it above, but the testexpr can be folded to a Const.) */ paramids = pull_exec_paramids(expr); if (!bms_is_empty(paramids)) @@ -2082,6 +2087,11 @@ match_clause_to_partition_key(GeneratePruningStepsContext *context, if (context->target != PARTTARGET_EXEC) return PARTCLAUSE_UNSUPPORTED; } + else if (contain_subplans((Node *) expr)) + { + if (context->target != PARTTARGET_EXEC) + return PARTCLAUSE_UNSUPPORTED; + } else { /* It's potentially usable, but mutable */ diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out index 191901210a8..3506ecec477 100644 --- a/src/test/regress/expected/partition_prune.out +++ b/src/test/regress/expected/partition_prune.out @@ -2106,6 +2106,22 @@ where asptab.id > ss.b::int; -> Seq Scan on int4_tbl tinner (14 rows) +-- check that a SubPlan in a pruning expression is not used for startup-time +-- pruning, which has no parent PlanState to evaluate the SubPlan with +explain (costs off) +select * from asptab where id = (null::int in (select f1 from int4_tbl))::int; + QUERY PLAN +-------------------------------------------------------------- + Append + -> Index Only Scan using asptab0_pkey on asptab0 asptab_1 + Index Cond: (id = ((ANY NULL::boolean))::integer) + SubPlan any_1 + -> Materialize + -> Seq Scan on int4_tbl + -> Index Only Scan using asptab1_pkey on asptab1 asptab_2 + Index Cond: (id = ((ANY NULL::boolean))::integer) +(8 rows) + drop table asptab; -- -- Test Partition pruning for HASH partitioning diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql index 01f44538fb6..d6e096dc565 100644 --- a/src/test/regress/sql/partition_prune.sql +++ b/src/test/regress/sql/partition_prune.sql @@ -488,6 +488,12 @@ select * from asptab where asptab.id > ss.b::int; +-- check that a SubPlan in a pruning expression is not used for startup-time +-- pruning, which has no parent PlanState to evaluate the SubPlan with + +explain (costs off) +select * from asptab where id = (null::int in (select f1 from int4_tbl))::int; + drop table asptab; -- -- 2.37.1 (Apple Git-137.1)