1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

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.

This has been broken since d47cbf474, which moved initial pruning out
of ExecInitNode(); before that, pruning always had the parent node's
PlanState available.  Back-patch to v18.

Author: Richard Guo <guofenglinux@gmail.com>
Reviewed-by: Amit Langote <amitlangote09@gmail.com>
Discussion: https://postgr.es/m/CAMbWs48nYDu7joYeyMKYdX8kHoL_ASEax2fqZH6L+HHqO2nkXA@mail.gmail.com
Backpatch-through: 18
This commit is contained in:
Richard Guo
2026-10-14 14:54:45 +09:00
parent b84ad0cd90
commit eacee6b618
3 changed files with 33 additions and 1 deletions

View File

@@ -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 */

View File

@@ -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

View File

@@ -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;
--