postgres.git / summary / log / commit / refs

commit    d31ebbff5b2bbe9dfe3fb130448581a4da388031
Author:   Andrew Gierth <rhodiumtoad@postgresql.org>
Date:     Fri Aug 17 14:04:26 2018 +0000

    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


src/backend/executor/nodeSubplan.c | 26 ++++++++++++++++++++++++-- src/test/regress/expected/subselect.out | 17 +++++++++++++++++ src/test/regress/sql/subselect.sql | 16 ++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index fe10e809dfb..84d1b34c16b 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -65,6 +65,9 @@ ExecSubPlan(SubPlanState *node, bool *isNull) { SubPlan *subplan = node->subplan; + EState *estate = node->planstate->state; + ScanDirection dir = estate->es_direction; + Datum retval; CHECK_FOR_INTERRUPTS(); @@ -77,11 +80,19 @@ ExecSubPlan(SubPlanState *node, if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK) elog(ERROR, "cannot set parent params from subquery"); + /* Force forward-scan mode for evaluation */ + estate->es_direction = ForwardScanDirection; + /* Select appropriate evaluation strategy */ if (subplan->useHashTable) - return ExecHashSubPlan(node, econtext, isNull); + retval = ExecHashSubPlan(node, econtext, isNull); else - return ExecScanSubPlan(node, econtext, isNull); + retval = ExecScanSubPlan(node, econtext, isNull); + + /* restore scan direction */ + estate->es_direction = dir; + + return retval; } /* @@ -918,6 +929,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) SubPlan *subplan = node->subplan; PlanState *planstate = node->planstate; SubLinkType subLinkType = subplan->subLinkType; + EState *estate = planstate->state; + ScanDirection dir = estate->es_direction; MemoryContext oldcontext; TupleTableSlot *slot; ListCell *pvar; @@ -931,6 +944,12 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) if (subLinkType == CTE_SUBLINK) 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 */ if (subLinkType == ARRAY_SUBLINK) astate = initArrayResultAny(subplan->firstColType, @@ -1083,6 +1102,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) } MemoryContextSwitchTo(oldcontext); + + /* restore scan direction */ + estate->es_direction = dir; } /* diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index 796426c66e2..2725c03fb95 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -1085,3 +1085,20 @@ NOTICE: x = 9, y = 13 (3 rows) drop function tattle(x int, y int); +-- +-- 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; diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index 15035afd0b0..ca856116021 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -568,3 +568,19 @@ select * from where tattle(x, u); drop function tattle(x int, y int); + +-- +-- 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; [parent: bd30f51c0ec9]