Set scan direction appropriately for SubPlans (bug #15336)
authorAndrew Gierth
Fri, 17 Aug 2018 14:04:26 +0000 (15:04 +0100)
committerAndrew Gierth
Fri, 17 Aug 2018 15:23:56 +0000 (16:23 +0100)
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
src/test/regress/expected/subselect.out
src/test/regress/sql/subselect.sql

index 5d02d9420b1d0d8260f4f0cf7f62e02aad46d068..225b323f55ed9fd33f90a32e3039446188246186 100644 (file)
@@ -63,6 +63,9 @@ ExecSubPlan(SubPlanState *node,
            ExprDoneCond *isDone)
 {
    SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
+   EState     *estate = node->planstate->state;
+   ScanDirection dir = estate->es_direction;
+   Datum       retval;
 
    /* Set default values for result flags: non-null, not a set result */
    *isNull = false;
@@ -75,11 +78,19 @@ ExecSubPlan(SubPlanState *node,
    if (subplan->setParam != NIL)
        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;
 }
 
 /*
@@ -906,6 +917,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
    SubPlan    *subplan = (SubPlan *) node->xprstate.expr;
    PlanState  *planstate = node->planstate;
    SubLinkType subLinkType = subplan->subLinkType;
+   EState     *estate = planstate->state;
+   ScanDirection dir = estate->es_direction;
    MemoryContext oldcontext;
    TupleTableSlot *slot;
    ListCell   *l;
@@ -918,6 +931,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;
+
    /*
     * Must switch to per-query memory context.
     */
@@ -1048,6 +1067,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
    }
 
    MemoryContextSwitchTo(oldcontext);
+
+   /* restore scan direction */
+   estate->es_direction = dir;
 }
 
 /*
index 7532c272a52ce28ddbd85b71d7dc46e8089b289b..5bad96366b6c92e31f27c33ab11dabf6e156447c 100644 (file)
@@ -851,3 +851,20 @@ select nextval('ts1');
       11
 (1 row)
 
+--
+-- 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;
index 81cf79928b3d8bcf795e97d478e875bda1b99960..decfffd3e4b166106984da7124f2a1dd3740b497 100644 (file)
@@ -464,3 +464,19 @@ select * from
   order by 1;
 
 select nextval('ts1');
+
+--
+-- 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;