Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeLimit.c
4 : * Routines to handle limiting of query results where appropriate
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/executor/nodeLimit.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : /*
16 : * INTERFACE ROUTINES
17 : * ExecLimit - extract a limited range of tuples
18 : * ExecInitLimit - initialize node and subnodes..
19 : * ExecEndLimit - shutdown node and subnodes
20 : */
21 :
22 : #include "postgres.h"
23 :
24 : #include "executor/executor.h"
25 : #include "executor/nodeLimit.h"
26 : #include "miscadmin.h"
27 : #include "nodes/nodeFuncs.h"
28 :
29 : static void recompute_limits(LimitState *node);
30 : static int64 compute_tuples_needed(LimitState *node);
31 :
32 :
33 : /* ----------------------------------------------------------------
34 : * ExecLimit
35 : *
36 : * This is a very simple node which just performs LIMIT/OFFSET
37 : * filtering on the stream of tuples returned by a subplan.
38 : * ----------------------------------------------------------------
39 : */
40 : static TupleTableSlot * /* return: a tuple or NULL */
41 912 : ExecLimit(PlanState *pstate)
42 : {
43 912 : LimitState *node = castNode(LimitState, pstate);
44 : ScanDirection direction;
45 : TupleTableSlot *slot;
46 : PlanState *outerPlan;
47 :
48 912 : CHECK_FOR_INTERRUPTS();
49 :
50 : /*
51 : * get information from the node
52 : */
53 912 : direction = node->ps.state->es_direction;
54 912 : outerPlan = outerPlanState(node);
55 :
56 : /*
57 : * The main logic is a simple state machine.
58 : */
59 912 : switch (node->lstate)
60 : {
61 : case LIMIT_INITIAL:
62 :
63 : /*
64 : * First call for this node, so compute limit/offset. (We can't do
65 : * this any earlier, because parameters from upper nodes will not
66 : * be set during ExecInitLimit.) This also sets position = 0 and
67 : * changes the state to LIMIT_RESCAN.
68 : */
69 180 : recompute_limits(node);
70 :
71 : /* FALL THRU */
72 :
73 : case LIMIT_RESCAN:
74 :
75 : /*
76 : * If backwards scan, just return NULL without changing state.
77 : */
78 290 : if (!ScanDirectionIsForward(direction))
79 0 : return NULL;
80 :
81 : /*
82 : * Check for empty window; if so, treat like empty subplan.
83 : */
84 290 : if (node->count <= 0 && !node->noCount)
85 : {
86 22 : node->lstate = LIMIT_EMPTY;
87 22 : return NULL;
88 : }
89 :
90 : /*
91 : * Fetch rows from subplan until we reach position > offset.
92 : */
93 : for (;;)
94 : {
95 3329 : slot = ExecProcNode(outerPlan);
96 3329 : if (TupIsNull(slot))
97 : {
98 : /*
99 : * The subplan returns too few tuples for us to produce
100 : * any output at all.
101 : */
102 48 : node->lstate = LIMIT_EMPTY;
103 48 : return NULL;
104 : }
105 3281 : node->subSlot = slot;
106 3281 : if (++node->position > node->offset)
107 220 : break;
108 3061 : }
109 :
110 : /*
111 : * Okay, we have the first tuple of the window.
112 : */
113 220 : node->lstate = LIMIT_INWINDOW;
114 220 : break;
115 :
116 : case LIMIT_EMPTY:
117 :
118 : /*
119 : * The subplan is known to return no tuples (or not more than
120 : * OFFSET tuples, in general). So we return no tuples.
121 : */
122 0 : return NULL;
123 :
124 : case LIMIT_INWINDOW:
125 616 : if (ScanDirectionIsForward(direction))
126 : {
127 : /*
128 : * Forwards scan, so check for stepping off end of window. If
129 : * we are at the end of the window, return NULL without
130 : * advancing the subplan or the position variable; but change
131 : * the state machine state to record having done so.
132 : */
133 1188 : if (!node->noCount &&
134 582 : node->position - node->offset >= node->count)
135 : {
136 192 : node->lstate = LIMIT_WINDOWEND;
137 192 : return NULL;
138 : }
139 :
140 : /*
141 : * Get next tuple from subplan, if any.
142 : */
143 414 : slot = ExecProcNode(outerPlan);
144 414 : if (TupIsNull(slot))
145 : {
146 10 : node->lstate = LIMIT_SUBPLANEOF;
147 10 : return NULL;
148 : }
149 404 : node->subSlot = slot;
150 404 : node->position++;
151 : }
152 : else
153 : {
154 : /*
155 : * Backwards scan, so check for stepping off start of window.
156 : * As above, change only state-machine status if so.
157 : */
158 10 : if (node->position <= node->offset + 1)
159 : {
160 3 : node->lstate = LIMIT_WINDOWSTART;
161 3 : return NULL;
162 : }
163 :
164 : /*
165 : * Get previous tuple from subplan; there should be one!
166 : */
167 7 : slot = ExecProcNode(outerPlan);
168 7 : if (TupIsNull(slot))
169 0 : elog(ERROR, "LIMIT subplan failed to run backwards");
170 7 : node->subSlot = slot;
171 7 : node->position--;
172 : }
173 411 : break;
174 :
175 : case LIMIT_SUBPLANEOF:
176 2 : if (ScanDirectionIsForward(direction))
177 0 : return NULL;
178 :
179 : /*
180 : * Backing up from subplan EOF, so re-fetch previous tuple; there
181 : * should be one! Note previous tuple must be in window.
182 : */
183 2 : slot = ExecProcNode(outerPlan);
184 2 : if (TupIsNull(slot))
185 0 : elog(ERROR, "LIMIT subplan failed to run backwards");
186 2 : node->subSlot = slot;
187 2 : node->lstate = LIMIT_INWINDOW;
188 : /* position does not change 'cause we didn't advance it before */
189 2 : break;
190 :
191 : case LIMIT_WINDOWEND:
192 1 : if (ScanDirectionIsForward(direction))
193 0 : return NULL;
194 :
195 : /*
196 : * Backing up from window end: simply re-return the last tuple
197 : * fetched from the subplan.
198 : */
199 1 : slot = node->subSlot;
200 1 : node->lstate = LIMIT_INWINDOW;
201 : /* position does not change 'cause we didn't advance it before */
202 1 : break;
203 :
204 : case LIMIT_WINDOWSTART:
205 3 : if (!ScanDirectionIsForward(direction))
206 0 : return NULL;
207 :
208 : /*
209 : * Advancing after having backed off window start: simply
210 : * re-return the last tuple fetched from the subplan.
211 : */
212 3 : slot = node->subSlot;
213 3 : node->lstate = LIMIT_INWINDOW;
214 : /* position does not change 'cause we didn't change it before */
215 3 : break;
216 :
217 : default:
218 0 : elog(ERROR, "impossible LIMIT state: %d",
219 : (int) node->lstate);
220 : slot = NULL; /* keep compiler quiet */
221 : break;
222 : }
223 :
224 : /* Return the current tuple */
225 637 : Assert(!TupIsNull(slot));
226 :
227 637 : return slot;
228 : }
229 :
230 : /*
231 : * Evaluate the limit/offset expressions --- done at startup or rescan.
232 : *
233 : * This is also a handy place to reset the current-position state info.
234 : */
235 : static void
236 290 : recompute_limits(LimitState *node)
237 : {
238 290 : ExprContext *econtext = node->ps.ps_ExprContext;
239 : Datum val;
240 : bool isNull;
241 :
242 290 : if (node->limitOffset)
243 : {
244 28 : val = ExecEvalExprSwitchContext(node->limitOffset,
245 : econtext,
246 : &isNull);
247 : /* Interpret NULL offset as no offset */
248 28 : if (isNull)
249 1 : node->offset = 0;
250 : else
251 : {
252 27 : node->offset = DatumGetInt64(val);
253 27 : if (node->offset < 0)
254 0 : ereport(ERROR,
255 : (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
256 : errmsg("OFFSET must not be negative")));
257 : }
258 : }
259 : else
260 : {
261 : /* No OFFSET supplied */
262 262 : node->offset = 0;
263 : }
264 :
265 290 : if (node->limitCount)
266 : {
267 286 : val = ExecEvalExprSwitchContext(node->limitCount,
268 : econtext,
269 : &isNull);
270 : /* Interpret NULL count as no count (LIMIT ALL) */
271 286 : if (isNull)
272 : {
273 1 : node->count = 0;
274 1 : node->noCount = true;
275 : }
276 : else
277 : {
278 285 : node->count = DatumGetInt64(val);
279 285 : if (node->count < 0)
280 0 : ereport(ERROR,
281 : (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
282 : errmsg("LIMIT must not be negative")));
283 285 : node->noCount = false;
284 : }
285 : }
286 : else
287 : {
288 : /* No COUNT supplied */
289 4 : node->count = 0;
290 4 : node->noCount = true;
291 : }
292 :
293 : /* Reset position to start-of-scan */
294 290 : node->position = 0;
295 290 : node->subSlot = NULL;
296 :
297 : /* Set state-machine state */
298 290 : node->lstate = LIMIT_RESCAN;
299 :
300 : /*
301 : * Notify child node about limit. Note: think not to "optimize" by
302 : * skipping ExecSetTupleBound if compute_tuples_needed returns < 0. We
303 : * must update the child node anyway, in case this is a rescan and the
304 : * previous time we got a different result.
305 : */
306 290 : ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node));
307 290 : }
308 :
309 : /*
310 : * Compute the maximum number of tuples needed to satisfy this Limit node.
311 : * Return a negative value if there is not a determinable limit.
312 : */
313 : static int64
314 290 : compute_tuples_needed(LimitState *node)
315 : {
316 290 : if (node->noCount)
317 5 : return -1;
318 : /* Note: if this overflows, we'll return a negative value, which is OK */
319 285 : return node->count + node->offset;
320 : }
321 :
322 : /* ----------------------------------------------------------------
323 : * ExecInitLimit
324 : *
325 : * This initializes the limit node state structures and
326 : * the node's subplan.
327 : * ----------------------------------------------------------------
328 : */
329 : LimitState *
330 260 : ExecInitLimit(Limit *node, EState *estate, int eflags)
331 : {
332 : LimitState *limitstate;
333 : Plan *outerPlan;
334 :
335 : /* check for unsupported flags */
336 260 : Assert(!(eflags & EXEC_FLAG_MARK));
337 :
338 : /*
339 : * create state structure
340 : */
341 260 : limitstate = makeNode(LimitState);
342 260 : limitstate->ps.plan = (Plan *) node;
343 260 : limitstate->ps.state = estate;
344 260 : limitstate->ps.ExecProcNode = ExecLimit;
345 :
346 260 : limitstate->lstate = LIMIT_INITIAL;
347 :
348 : /*
349 : * Miscellaneous initialization
350 : *
351 : * Limit nodes never call ExecQual or ExecProject, but they need an
352 : * exprcontext anyway to evaluate the limit/offset parameters in.
353 : */
354 260 : ExecAssignExprContext(estate, &limitstate->ps);
355 :
356 : /*
357 : * initialize child expressions
358 : */
359 260 : limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
360 : (PlanState *) limitstate);
361 260 : limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
362 : (PlanState *) limitstate);
363 :
364 : /*
365 : * Tuple table initialization (XXX not actually used...)
366 : */
367 260 : ExecInitResultTupleSlot(estate, &limitstate->ps);
368 :
369 : /*
370 : * then initialize outer plan
371 : */
372 260 : outerPlan = outerPlan(node);
373 260 : outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
374 :
375 : /*
376 : * limit nodes do no projections, so initialize projection info for this
377 : * node appropriately
378 : */
379 260 : ExecAssignResultTypeFromTL(&limitstate->ps);
380 260 : limitstate->ps.ps_ProjInfo = NULL;
381 :
382 260 : return limitstate;
383 : }
384 :
385 : /* ----------------------------------------------------------------
386 : * ExecEndLimit
387 : *
388 : * This shuts down the subplan and frees resources allocated
389 : * to this node.
390 : * ----------------------------------------------------------------
391 : */
392 : void
393 254 : ExecEndLimit(LimitState *node)
394 : {
395 254 : ExecFreeExprContext(&node->ps);
396 254 : ExecEndNode(outerPlanState(node));
397 254 : }
398 :
399 :
400 : void
401 110 : ExecReScanLimit(LimitState *node)
402 : {
403 : /*
404 : * Recompute limit/offset in case parameters changed, and reset the state
405 : * machine. We must do this before rescanning our child node, in case
406 : * it's a Sort that we are passing the parameters down to.
407 : */
408 110 : recompute_limits(node);
409 :
410 : /*
411 : * if chgParam of subnode is not null then plan will be re-scanned by
412 : * first ExecProcNode.
413 : */
414 110 : if (node->ps.lefttree->chgParam == NULL)
415 18 : ExecReScan(node->ps.lefttree);
416 110 : }
|