mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Set scan direction appropriately for SubPlans (bug #15336)
When executing a SubPlan in an expression, the EState's direction
field was left alone, resulting in an attempt to execute the subplan
backwards if it was encountered during a backwards scan of a cursor.
Also, though much less likely, it was possible to reach the execution
of an InitPlan while in backwards-scan state.
Repair by saving/restoring estate->es_direction and forcing forward
scan mode in the relevant places.
Backpatch all the way, since this has been broken since 8.3 (prior to
commit c7ff7663e
, SubPlans had their own EStates rather than sharing
the parent plan's, so there was no confusion over scan direction).
Per bug #15336 reported by Vladimir Baranoff; analysis and patch by
me, review by Tom Lane.
Discussion: https://postgr.es/m/153449812167.1304.1741624125628126322@wrigleys.postgresql.org
This commit is contained in:
@ -65,6 +65,9 @@ ExecSubPlan(SubPlanState *node,
|
|||||||
bool *isNull)
|
bool *isNull)
|
||||||
{
|
{
|
||||||
SubPlan *subplan = node->subplan;
|
SubPlan *subplan = node->subplan;
|
||||||
|
EState *estate = node->planstate->state;
|
||||||
|
ScanDirection dir = estate->es_direction;
|
||||||
|
Datum retval;
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
@ -77,11 +80,19 @@ ExecSubPlan(SubPlanState *node,
|
|||||||
if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK)
|
if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK)
|
||||||
elog(ERROR, "cannot set parent params from subquery");
|
elog(ERROR, "cannot set parent params from subquery");
|
||||||
|
|
||||||
|
/* Force forward-scan mode for evaluation */
|
||||||
|
estate->es_direction = ForwardScanDirection;
|
||||||
|
|
||||||
/* Select appropriate evaluation strategy */
|
/* Select appropriate evaluation strategy */
|
||||||
if (subplan->useHashTable)
|
if (subplan->useHashTable)
|
||||||
return ExecHashSubPlan(node, econtext, isNull);
|
retval = ExecHashSubPlan(node, econtext, isNull);
|
||||||
else
|
else
|
||||||
return ExecScanSubPlan(node, econtext, isNull);
|
retval = ExecScanSubPlan(node, econtext, isNull);
|
||||||
|
|
||||||
|
/* restore scan direction */
|
||||||
|
estate->es_direction = dir;
|
||||||
|
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1006,6 +1017,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||||||
SubPlan *subplan = node->subplan;
|
SubPlan *subplan = node->subplan;
|
||||||
PlanState *planstate = node->planstate;
|
PlanState *planstate = node->planstate;
|
||||||
SubLinkType subLinkType = subplan->subLinkType;
|
SubLinkType subLinkType = subplan->subLinkType;
|
||||||
|
EState *estate = planstate->state;
|
||||||
|
ScanDirection dir = estate->es_direction;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
ListCell *pvar;
|
ListCell *pvar;
|
||||||
@ -1019,6 +1032,12 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||||||
if (subLinkType == CTE_SUBLINK)
|
if (subLinkType == CTE_SUBLINK)
|
||||||
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
|
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enforce forward scan direction regardless of caller. It's hard but not
|
||||||
|
* impossible to get here in backward scan, so make it work anyway.
|
||||||
|
*/
|
||||||
|
estate->es_direction = ForwardScanDirection;
|
||||||
|
|
||||||
/* Initialize ArrayBuildStateAny in caller's context, if needed */
|
/* Initialize ArrayBuildStateAny in caller's context, if needed */
|
||||||
if (subLinkType == ARRAY_SUBLINK)
|
if (subLinkType == ARRAY_SUBLINK)
|
||||||
astate = initArrayResultAny(subplan->firstColType,
|
astate = initArrayResultAny(subplan->firstColType,
|
||||||
@ -1171,6 +1190,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
|
/* restore scan direction */
|
||||||
|
estate->es_direction = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1137,3 +1137,20 @@ select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
|
|||||||
|
|
||||||
drop function explain_sq_limit();
|
drop function explain_sq_limit();
|
||||||
drop table sq_limit;
|
drop table sq_limit;
|
||||||
|
--
|
||||||
|
-- Ensure that backward scan direction isn't propagated into
|
||||||
|
-- expression subqueries (bug #15336)
|
||||||
|
--
|
||||||
|
begin;
|
||||||
|
declare c1 scroll cursor for
|
||||||
|
select * from generate_series(1,4) i
|
||||||
|
where i <> all (values (2),(3));
|
||||||
|
move forward all in c1;
|
||||||
|
fetch backward all in c1;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
4
|
||||||
|
1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
commit;
|
||||||
|
@ -609,3 +609,19 @@ select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
|
|||||||
drop function explain_sq_limit();
|
drop function explain_sq_limit();
|
||||||
|
|
||||||
drop table sq_limit;
|
drop table sq_limit;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Ensure that backward scan direction isn't propagated into
|
||||||
|
-- expression subqueries (bug #15336)
|
||||||
|
--
|
||||||
|
|
||||||
|
begin;
|
||||||
|
|
||||||
|
declare c1 scroll cursor for
|
||||||
|
select * from generate_series(1,4) i
|
||||||
|
where i <> all (values (2),(3));
|
||||||
|
|
||||||
|
move forward all in c1;
|
||||||
|
fetch backward all in c1;
|
||||||
|
|
||||||
|
commit;
|
||||||
|
Reference in New Issue
Block a user