1 /*-------------------------------------------------------------------------
4 * Routines to handle limiting of query results where appropriate
6 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
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_PARAMETER_VALUE
),
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_PARAMETER_VALUE
),
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
);