Commit b11123b6 authored by Tom Lane's avatar Tom Lane

Fix parameter recalculation for Limit nodes: during a ReScan call we must

recompute the limit/offset immediately, so that the updated values are
available when the child's ReScan function is invoked.  Add a regression
test for this, too.  Bug is new in HEAD (due to the bounded-sorting patch)
so no need for back-patch.

I did not do anything about merging this signaling with chgParam processing,
but if we were to do that we'd still need to compute the updated values
at this point rather than during the first ProcNode call.

Per observation and test case from Greg Stark, though I didn't use his patch.
parent 64058429
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.30 2007/05/04 01:13:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.31 2007/05/17 19:35:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -55,17 +55,22 @@ ExecLimit(LimitState *node) ...@@ -55,17 +55,22 @@ ExecLimit(LimitState *node)
case LIMIT_INITIAL: case LIMIT_INITIAL:
/* /*
* If backwards scan, just return NULL without changing state. * First call for this node, so compute limit/offset. (We can't do
* this any earlier, because parameters from upper nodes will not
* be set during ExecInitLimit.) This also sets position = 0
* and changes the state to LIMIT_RESCAN.
*/ */
if (!ScanDirectionIsForward(direction)) recompute_limits(node);
return NULL;
/* FALL THRU */
case LIMIT_RESCAN:
/* /*
* First call for this scan, so compute limit/offset. (We can't do * If backwards scan, just return NULL without changing state.
* this any earlier, because parameters from upper nodes may not
* be set until now.) This also sets position = 0.
*/ */
recompute_limits(node); if (!ScanDirectionIsForward(direction))
return NULL;
/* /*
* Check for empty window; if so, treat like empty subplan. * Check for empty window; if so, treat like empty subplan.
...@@ -217,7 +222,7 @@ ExecLimit(LimitState *node) ...@@ -217,7 +222,7 @@ ExecLimit(LimitState *node)
} }
/* /*
* Evaluate the limit/offset expressions --- done at start of each scan. * Evaluate the limit/offset expressions --- done at startup or rescan.
* *
* This is also a handy place to reset the current-position state info. * This is also a handy place to reset the current-position state info.
*/ */
...@@ -281,6 +286,9 @@ recompute_limits(LimitState *node) ...@@ -281,6 +286,9 @@ recompute_limits(LimitState *node)
node->position = 0; node->position = 0;
node->subSlot = NULL; node->subSlot = NULL;
/* Set state-machine state */
node->lstate = LIMIT_RESCAN;
/* /*
* If we have a COUNT, and our input is a Sort node, notify it that it can * If we have a COUNT, and our input is a Sort node, notify it that it can
* use bounded sort. * use bounded sort.
...@@ -403,8 +411,12 @@ ExecEndLimit(LimitState *node) ...@@ -403,8 +411,12 @@ ExecEndLimit(LimitState *node)
void void
ExecReScanLimit(LimitState *node, ExprContext *exprCtxt) ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
{ {
/* resetting lstate will force offset/limit recalculation */ /*
node->lstate = LIMIT_INITIAL; * Recompute limit/offset in case parameters changed, and reset the
* state machine. We must do this before rescanning our child node,
* in case it's a Sort that we are passing the parameters down to.
*/
recompute_limits(node);
/* /*
* if chgParam of subnode is not null then plan will be re-scanned by * if chgParam of subnode is not null then plan will be re-scanned by
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.88 2007/04/26 23:24:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/nodeSubplan.c,v 1.89 2007/05/17 19:35:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -242,6 +242,9 @@ ExecScanSubPlan(SubPlanState *node, ...@@ -242,6 +242,9 @@ ExecScanSubPlan(SubPlanState *node,
planstate->chgParam = bms_add_member(planstate->chgParam, paramid); planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
} }
/*
* Now that we've set up its parameters, we can reset the subplan.
*/
ExecReScan(planstate, NULL); ExecReScan(planstate, NULL);
/* /*
...@@ -901,6 +904,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) ...@@ -901,6 +904,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
subLinkType == ALL_SUBLINK) subLinkType == ALL_SUBLINK)
elog(ERROR, "ANY/ALL subselect unsupported as initplan"); elog(ERROR, "ANY/ALL subselect unsupported as initplan");
/*
* By definition, an initplan has no parameters from our query level,
* but it could have some from an outer level. Rescan it if needed.
*/
if (planstate->chgParam != NULL) if (planstate->chgParam != NULL)
ExecReScan(planstate, NULL); ExecReScan(planstate, NULL);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.173 2007/05/04 01:13:45 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.174 2007/05/17 19:35:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1416,6 +1416,7 @@ typedef struct SetOpState ...@@ -1416,6 +1416,7 @@ typedef struct SetOpState
typedef enum typedef enum
{ {
LIMIT_INITIAL, /* initial state for LIMIT node */ LIMIT_INITIAL, /* initial state for LIMIT node */
LIMIT_RESCAN, /* rescan after recomputing parameters */
LIMIT_EMPTY, /* there are no returnable rows */ LIMIT_EMPTY, /* there are no returnable rows */
LIMIT_INWINDOW, /* have returned a row in the window */ LIMIT_INWINDOW, /* have returned a row in the window */
LIMIT_SUBPLANEOF, /* at EOF of subplan (within window) */ LIMIT_SUBPLANEOF, /* at EOF of subplan (within window) */
......
...@@ -108,3 +108,24 @@ SELECT ''::text AS five, unique1, unique2, stringu1 ...@@ -108,3 +108,24 @@ SELECT ''::text AS five, unique1, unique2, stringu1
| 904 | 793 | UIAAAA | 904 | 793 | UIAAAA
(5 rows) (5 rows)
-- Stress test for variable LIMIT in conjunction with bounded-heap sorting
SELECT
(SELECT n
FROM (VALUES (1)) AS x,
(SELECT n FROM generate_series(1,10) AS n
ORDER BY n LIMIT 1 OFFSET s-1) AS y) AS z
FROM generate_series(1,10) AS s;
z
----
1
2
3
4
5
6
7
8
9
10
(10 rows)
...@@ -30,3 +30,12 @@ SELECT ''::text AS five, unique1, unique2, stringu1 ...@@ -30,3 +30,12 @@ SELECT ''::text AS five, unique1, unique2, stringu1
SELECT ''::text AS five, unique1, unique2, stringu1 SELECT ''::text AS five, unique1, unique2, stringu1
FROM onek FROM onek
ORDER BY unique1 LIMIT 5 OFFSET 900; ORDER BY unique1 LIMIT 5 OFFSET 900;
-- Stress test for variable LIMIT in conjunction with bounded-heap sorting
SELECT
(SELECT n
FROM (VALUES (1)) AS x,
(SELECT n FROM generate_series(1,10) AS n
ORDER BY n LIMIT 1 OFFSET s-1) AS y) AS z
FROM generate_series(1,10) AS s;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment