1 /*-------------------------------------------------------------------------
4 * Routines to handle limiting of query results where appropriate
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.38 2009/04/02 20:59:10 momjian Exp $
13 *-------------------------------------------------------------------------
17 * ExecLimit - extract a limited range of tuples
18 * ExecInitLimit - initialize node and subnodes..
19 * ExecEndLimit - shutdown node and subnodes
24 #include "executor/executor.h"
25 #include "executor/nodeLimit.h"
27 static void recompute_limits(LimitState *node);
30 /* ----------------------------------------------------------------
33 * This is a very simple node which just performs LIMIT/OFFSET
34 * filtering on the stream of tuples returned by a subplan.
35 * ----------------------------------------------------------------
37 TupleTableSlot * /* return: a tuple or NULL */
38 ExecLimit(LimitState *node)
40 ScanDirection direction;
45 * get information from the node
47 direction = node->ps.state->es_direction;
48 outerPlan = outerPlanState(node);
51 * The main logic is a simple state machine.
58 * First call for this node, so compute limit/offset. (We can't do
59 * this any earlier, because parameters from upper nodes will not
60 * be set during ExecInitLimit.) This also sets position = 0 and
61 * changes the state to LIMIT_RESCAN.
63 recompute_limits(node);
70 * If backwards scan, just return NULL without changing state.
72 if (!ScanDirectionIsForward(direction))
76 * Check for empty window; if so, treat like empty subplan.
78 if (node->count <= 0 && !node->noCount)
80 node->lstate = LIMIT_EMPTY;
85 * Fetch rows from subplan until we reach position > offset.
89 slot = ExecProcNode(outerPlan);
93 * The subplan returns too few tuples for us to produce
96 node->lstate = LIMIT_EMPTY;
100 if (++node->position > node->offset)
105 * Okay, we have the first tuple of the window.
107 node->lstate = LIMIT_INWINDOW;
113 * The subplan is known to return no tuples (or not more than
114 * OFFSET tuples, in general). So we return no tuples.
119 if (ScanDirectionIsForward(direction))
122 * Forwards scan, so check for stepping off end of window. If
123 * we are at the end of the window, return NULL without
124 * advancing the subplan or the position variable; but change
125 * the state machine state to record having done so.
127 if (!node->noCount &&
128 node->position >= node->offset + node->count)
130 node->lstate = LIMIT_WINDOWEND;
135 * Get next tuple from subplan, if any.
137 slot = ExecProcNode(outerPlan);
140 node->lstate = LIMIT_SUBPLANEOF;
143 node->subSlot = slot;
149 * Backwards scan, so check for stepping off start of window.
150 * As above, change only state-machine status if so.
152 if (node->position <= node->offset + 1)
154 node->lstate = LIMIT_WINDOWSTART;
159 * Get previous tuple from subplan; there should be one!
161 slot = ExecProcNode(outerPlan);
163 elog(ERROR, "LIMIT subplan failed to run backwards");
164 node->subSlot = slot;
169 case LIMIT_SUBPLANEOF:
170 if (ScanDirectionIsForward(direction))
174 * Backing up from subplan EOF, so re-fetch previous tuple; there
175 * should be one! Note previous tuple must be in window.
177 slot = ExecProcNode(outerPlan);
179 elog(ERROR, "LIMIT subplan failed to run backwards");
180 node->subSlot = slot;
181 node->lstate = LIMIT_INWINDOW;
182 /* position does not change 'cause we didn't advance it before */
185 case LIMIT_WINDOWEND:
186 if (ScanDirectionIsForward(direction))
190 * Backing up from window end: simply re-return the last tuple
191 * fetched from the subplan.
193 slot = node->subSlot;
194 node->lstate = LIMIT_INWINDOW;
195 /* position does not change 'cause we didn't advance it before */
198 case LIMIT_WINDOWSTART:
199 if (!ScanDirectionIsForward(direction))
203 * Advancing after having backed off window start: simply
204 * re-return the last tuple fetched from the subplan.
206 slot = node->subSlot;
207 node->lstate = LIMIT_INWINDOW;
208 /* position does not change 'cause we didn't change it before */
212 elog(ERROR, "impossible LIMIT state: %d",
214 slot = NULL; /* keep compiler quiet */
218 /* Return the current tuple */
219 Assert(!TupIsNull(slot));
225 * Evaluate the limit/offset expressions --- done at startup or rescan.
227 * This is also a handy place to reset the current-position state info.
230 recompute_limits(LimitState *node)
232 ExprContext *econtext = node->ps.ps_ExprContext;
236 if (node->limitOffset)
238 val = ExecEvalExprSwitchContext(node->limitOffset,
242 /* Interpret NULL offset as no offset */
247 node->offset = DatumGetInt64(val);
248 if (node->offset < 0)
250 (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
251 errmsg("OFFSET must not be negative")));
256 /* No OFFSET supplied */
260 if (node->limitCount)
262 val = ExecEvalExprSwitchContext(node->limitCount,
266 /* Interpret NULL count as no count (LIMIT ALL) */
270 node->noCount = true;
274 node->count = DatumGetInt64(val);
277 (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
278 errmsg("LIMIT must not be negative")));
279 node->noCount = false;
284 /* No COUNT supplied */
286 node->noCount = true;
289 /* Reset position to start-of-scan */
291 node->subSlot = NULL;
293 /* Set state-machine state */
294 node->lstate = LIMIT_RESCAN;
297 * If we have a COUNT, and our input is a Sort node, notify it that it can
300 * This is a bit of a kluge, but we don't have any more-abstract way of
301 * communicating between the two nodes; and it doesn't seem worth trying
302 * to invent one without some more examples of special communication
305 * Note: it is the responsibility of nodeSort.c to react properly to
306 * changes of these parameters. If we ever do redesign this, it'd be a
307 * good idea to integrate this signaling with the parameter-change
310 if (IsA(outerPlanState(node), SortState))
312 SortState *sortState = (SortState *) outerPlanState(node);
313 int64 tuples_needed = node->count + node->offset;
315 /* negative test checks for overflow */
316 if (node->noCount || tuples_needed < 0)
318 /* make sure flag gets reset if needed upon rescan */
319 sortState->bounded = false;
323 sortState->bounded = true;
324 sortState->bound = tuples_needed;
329 /* ----------------------------------------------------------------
332 * This initializes the limit node state structures and
333 * the node's subplan.
334 * ----------------------------------------------------------------
337 ExecInitLimit(Limit *node, EState *estate, int eflags)
339 LimitState *limitstate;
342 /* check for unsupported flags */
343 Assert(!(eflags & EXEC_FLAG_MARK));
346 * create state structure
348 limitstate = makeNode(LimitState);
349 limitstate->ps.plan = (Plan *) node;
350 limitstate->ps.state = estate;
352 limitstate->lstate = LIMIT_INITIAL;
355 * Miscellaneous initialization
357 * Limit nodes never call ExecQual or ExecProject, but they need an
358 * exprcontext anyway to evaluate the limit/offset parameters in.
360 ExecAssignExprContext(estate, &limitstate->ps);
363 * initialize child expressions
365 limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
366 (PlanState *) limitstate);
367 limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
368 (PlanState *) limitstate);
370 #define LIMIT_NSLOTS 1
373 * Tuple table initialization (XXX not actually used...)
375 ExecInitResultTupleSlot(estate, &limitstate->ps);
378 * then initialize outer plan
380 outerPlan = outerPlan(node);
381 outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
384 * limit nodes do no projections, so initialize projection info for this
387 ExecAssignResultTypeFromTL(&limitstate->ps);
388 limitstate->ps.ps_ProjInfo = NULL;
394 ExecCountSlotsLimit(Limit *node)
396 return ExecCountSlotsNode(outerPlan(node)) +
397 ExecCountSlotsNode(innerPlan(node)) +
401 /* ----------------------------------------------------------------
404 * This shuts down the subplan and frees resources allocated
406 * ----------------------------------------------------------------
409 ExecEndLimit(LimitState *node)
411 ExecFreeExprContext(&node->ps);
412 ExecEndNode(outerPlanState(node));
417 ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
420 * Recompute limit/offset in case parameters changed, and reset the state
421 * machine. We must do this before rescanning our child node, in case
422 * it's a Sort that we are passing the parameters down to.
424 recompute_limits(node);
427 * if chgParam of subnode is not null then plan will be re-scanned by
428 * first ExecProcNode.
430 if (((PlanState *) node)->lefttree->chgParam == NULL)
431 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);