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:
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
--
|
||||
|
||||
Reference in New Issue
Block a user