1 /*-------------------------------------------------------------------------
4 * Routines to handle limiting of query results where appropriate
6 * Portions Copyright (c) 1996-2006, 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.27 2006/07/26 19:31:50 tgl 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 * If backwards scan, just return NULL without changing state.
60 if (!ScanDirectionIsForward(direction))
64 * First call for this scan, so compute limit/offset. (We can't do
65 * this any earlier, because parameters from upper nodes may not
66 * be set until now.) This also sets position = 0.
68 recompute_limits(node);
71 * Check for empty window; if so, treat like empty subplan.
73 if (node->count <= 0 && !node->noCount)
75 node->lstate = LIMIT_EMPTY;
80 * Fetch rows from subplan until we reach position > offset.
84 slot = ExecProcNode(outerPlan);
88 * The subplan returns too few tuples for us to produce
91 node->lstate = LIMIT_EMPTY;
95 if (++node->position > node->offset)
100 * Okay, we have the first tuple of the window.
102 node->lstate = LIMIT_INWINDOW;
108 * The subplan is known to return no tuples (or not more than
109 * OFFSET tuples, in general). So we return no tuples.
114 if (ScanDirectionIsForward(direction))
117 * Forwards scan, so check for stepping off end of window. If
118 * we are at the end of the window, return NULL without
119 * advancing the subplan or the position variable; but change
120 * the state machine state to record having done so.
122 if (!node->noCount &&
123 node->position >= node->offset + node->count)
125 node->lstate = LIMIT_WINDOWEND;
130 * Get next tuple from subplan, if any.
132 slot = ExecProcNode(outerPlan);
135 node->lstate = LIMIT_SUBPLANEOF;
138 node->subSlot = slot;
144 * Backwards scan, so check for stepping off start of window.
145 * As above, change only state-machine status if so.
147 if (node->position <= node->offset + 1)
149 node->lstate = LIMIT_WINDOWSTART;
154 * Get previous tuple from subplan; there should be one!
156 slot = ExecProcNode(outerPlan);
158 elog(ERROR, "LIMIT subplan failed to run backwards");
159 node->subSlot = slot;
164 case LIMIT_SUBPLANEOF:
165 if (ScanDirectionIsForward(direction))
169 * Backing up from subplan EOF, so re-fetch previous tuple; there
170 * should be one! Note previous tuple must be in window.
172 slot = ExecProcNode(outerPlan);
174 elog(ERROR, "LIMIT subplan failed to run backwards");
175 node->subSlot = slot;
176 node->lstate = LIMIT_INWINDOW;
177 /* position does not change 'cause we didn't advance it before */
180 case LIMIT_WINDOWEND:
181 if (ScanDirectionIsForward(direction))
185 * Backing up from window end: simply re-return the last tuple
186 * fetched from the subplan.
188 slot = node->subSlot;
189 node->lstate = LIMIT_INWINDOW;
190 /* position does not change 'cause we didn't advance it before */
193 case LIMIT_WINDOWSTART:
194 if (!ScanDirectionIsForward(direction))
198 * Advancing after having backed off window start: simply
199 * re-return the last tuple fetched from the subplan.
201 slot = node->subSlot;
202 node->lstate = LIMIT_INWINDOW;
203 /* position does not change 'cause we didn't change it before */
207 elog(ERROR, "impossible LIMIT state: %d",
209 slot = NULL; /* keep compiler quiet */
213 /* Return the current tuple */
214 Assert(!TupIsNull(slot));
220 * Evaluate the limit/offset expressions --- done at start of each scan.
222 * This is also a handy place to reset the current-position state info.
225 recompute_limits(LimitState *node)
227 ExprContext *econtext = node->ps.ps_ExprContext;
230 if (node->limitOffset)
233 DatumGetInt64(ExecEvalExprSwitchContext(node->limitOffset,
237 /* Interpret NULL offset as no offset */
240 else if (node->offset < 0)
245 /* No OFFSET supplied */
249 if (node->limitCount)
251 node->noCount = false;
253 DatumGetInt64(ExecEvalExprSwitchContext(node->limitCount,
257 /* Interpret NULL count as no count (LIMIT ALL) */
259 node->noCount = true;
260 else if (node->count < 0)
265 /* No COUNT supplied */
267 node->noCount = true;
270 /* Reset position to start-of-scan */
272 node->subSlot = NULL;
275 /* ----------------------------------------------------------------
278 * This initializes the limit node state structures and
279 * the node's subplan.
280 * ----------------------------------------------------------------
283 ExecInitLimit(Limit *node, EState *estate, int eflags)
285 LimitState *limitstate;
288 /* check for unsupported flags */
289 Assert(!(eflags & EXEC_FLAG_MARK));
292 * create state structure
294 limitstate = makeNode(LimitState);
295 limitstate->ps.plan = (Plan *) node;
296 limitstate->ps.state = estate;
298 limitstate->lstate = LIMIT_INITIAL;
301 * Miscellaneous initialization
303 * Limit nodes never call ExecQual or ExecProject, but they need an
304 * exprcontext anyway to evaluate the limit/offset parameters in.
306 ExecAssignExprContext(estate, &limitstate->ps);
309 * initialize child expressions
311 limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
312 (PlanState *) limitstate);
313 limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
314 (PlanState *) limitstate);
316 #define LIMIT_NSLOTS 1
319 * Tuple table initialization (XXX not actually used...)
321 ExecInitResultTupleSlot(estate, &limitstate->ps);
324 * then initialize outer plan
326 outerPlan = outerPlan(node);
327 outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
330 * limit nodes do no projections, so initialize projection info for this
333 ExecAssignResultTypeFromTL(&limitstate->ps);
334 limitstate->ps.ps_ProjInfo = NULL;
340 ExecCountSlotsLimit(Limit *node)
342 return ExecCountSlotsNode(outerPlan(node)) +
343 ExecCountSlotsNode(innerPlan(node)) +
347 /* ----------------------------------------------------------------
350 * This shuts down the subplan and frees resources allocated
352 * ----------------------------------------------------------------
355 ExecEndLimit(LimitState *node)
357 ExecFreeExprContext(&node->ps);
358 ExecEndNode(outerPlanState(node));
363 ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
365 /* resetting lstate will force offset/limit recalculation */
366 node->lstate = LIMIT_INITIAL;
369 * if chgParam of subnode is not null then plan will be re-scanned by
370 * first ExecProcNode.
372 if (((PlanState *) node)->lefttree->chgParam == NULL)
373 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);