Add support for user-defined I/O conversion casts.
[PostgreSQL.git] / src / backend / executor / nodeLimit.c
blobf748ec6e918c70fc60b841d1e7e1b1e9799845c4
1 /*-------------------------------------------------------------------------
3 * nodeLimit.c
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
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
16 * INTERFACE ROUTINES
17 * ExecLimit - extract a limited range of tuples
18 * ExecInitLimit - initialize node and subnodes..
19 * ExecEndLimit - shutdown node and subnodes
22 #include "postgres.h"
24 #include "executor/executor.h"
25 #include "executor/nodeLimit.h"
27 static void recompute_limits(LimitState *node);
30 /* ----------------------------------------------------------------
31 * ExecLimit
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;
41 TupleTableSlot *slot;
42 PlanState *outerPlan;
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.
53 switch (node->lstate)
55 case LIMIT_INITIAL:
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);
65 /* FALL THRU */
67 case LIMIT_RESCAN:
70 * If backwards scan, just return NULL without changing state.
72 if (!ScanDirectionIsForward(direction))
73 return NULL;
76 * Check for empty window; if so, treat like empty subplan.
78 if (node->count <= 0 && !node->noCount)
80 node->lstate = LIMIT_EMPTY;
81 return NULL;
85 * Fetch rows from subplan until we reach position > offset.
87 for (;;)
89 slot = ExecProcNode(outerPlan);
90 if (TupIsNull(slot))
93 * The subplan returns too few tuples for us to produce
94 * any output at all.
96 node->lstate = LIMIT_EMPTY;
97 return NULL;
99 node->subSlot = slot;
100 if (++node->position > node->offset)
101 break;
105 * Okay, we have the first tuple of the window.
107 node->lstate = LIMIT_INWINDOW;
108 break;
110 case LIMIT_EMPTY:
113 * The subplan is known to return no tuples (or not more than
114 * OFFSET tuples, in general). So we return no tuples.
116 return NULL;
118 case LIMIT_INWINDOW:
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;
131 return NULL;
135 * Get next tuple from subplan, if any.
137 slot = ExecProcNode(outerPlan);
138 if (TupIsNull(slot))
140 node->lstate = LIMIT_SUBPLANEOF;
141 return NULL;
143 node->subSlot = slot;
144 node->position++;
146 else
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;
155 return NULL;
159 * Get previous tuple from subplan; there should be one!
161 slot = ExecProcNode(outerPlan);
162 if (TupIsNull(slot))
163 elog(ERROR, "LIMIT subplan failed to run backwards");
164 node->subSlot = slot;
165 node->position--;
167 break;
169 case LIMIT_SUBPLANEOF:
170 if (ScanDirectionIsForward(direction))
171 return NULL;
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);
178 if (TupIsNull(slot))
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 */
183 break;
185 case LIMIT_WINDOWEND:
186 if (ScanDirectionIsForward(direction))
187 return NULL;
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 */
196 break;
198 case LIMIT_WINDOWSTART:
199 if (!ScanDirectionIsForward(direction))
200 return NULL;
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 */
209 break;
211 default:
212 elog(ERROR, "impossible LIMIT state: %d",
213 (int) node->lstate);
214 slot = NULL; /* keep compiler quiet */
215 break;
218 /* Return the current tuple */
219 Assert(!TupIsNull(slot));
221 return 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.
229 static void
230 recompute_limits(LimitState *node)
232 ExprContext *econtext = node->ps.ps_ExprContext;
233 Datum val;
234 bool isNull;
236 if (node->limitOffset)
238 val = ExecEvalExprSwitchContext(node->limitOffset,
239 econtext,
240 &isNull,
241 NULL);
242 /* Interpret NULL offset as no offset */
243 if (isNull)
244 node->offset = 0;
245 else
247 node->offset = DatumGetInt64(val);
248 if (node->offset < 0)
249 ereport(ERROR,
250 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
251 errmsg("OFFSET must not be negative")));
254 else
256 /* No OFFSET supplied */
257 node->offset = 0;
260 if (node->limitCount)
262 val = ExecEvalExprSwitchContext(node->limitCount,
263 econtext,
264 &isNull,
265 NULL);
266 /* Interpret NULL count as no count (LIMIT ALL) */
267 if (isNull)
269 node->count = 0;
270 node->noCount = true;
272 else
274 node->count = DatumGetInt64(val);
275 if (node->count < 0)
276 ereport(ERROR,
277 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
278 errmsg("LIMIT must not be negative")));
279 node->noCount = false;
282 else
284 /* No COUNT supplied */
285 node->count = 0;
286 node->noCount = true;
289 /* Reset position to start-of-scan */
290 node->position = 0;
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
298 * use bounded sort.
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
303 * needs.
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
308 * mechanism.
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;
321 else
323 sortState->bounded = true;
324 sortState->bound = tuples_needed;
329 /* ----------------------------------------------------------------
330 * ExecInitLimit
332 * This initializes the limit node state structures and
333 * the node's subplan.
334 * ----------------------------------------------------------------
336 LimitState *
337 ExecInitLimit(Limit *node, EState *estate, int eflags)
339 LimitState *limitstate;
340 Plan *outerPlan;
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
385 * node appropriately
387 ExecAssignResultTypeFromTL(&limitstate->ps);
388 limitstate->ps.ps_ProjInfo = NULL;
390 return limitstate;
394 ExecCountSlotsLimit(Limit *node)
396 return ExecCountSlotsNode(outerPlan(node)) +
397 ExecCountSlotsNode(innerPlan(node)) +
398 LIMIT_NSLOTS;
401 /* ----------------------------------------------------------------
402 * ExecEndLimit
404 * This shuts down the subplan and frees resources allocated
405 * to this node.
406 * ----------------------------------------------------------------
408 void
409 ExecEndLimit(LimitState *node)
411 ExecFreeExprContext(&node->ps);
412 ExecEndNode(outerPlanState(node));
416 void
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);