1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * nodeLimit.c |
4 | * Routines to handle limiting of query results where appropriate |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 | ExecLimit(PlanState *pstate) |
42 | { |
43 | LimitState *node = castNode(LimitState, pstate); |
44 | ScanDirection direction; |
45 | TupleTableSlot *slot; |
46 | PlanState *outerPlan; |
47 | |
48 | CHECK_FOR_INTERRUPTS(); |
49 | |
50 | /* |
51 | * get information from the node |
52 | */ |
53 | direction = node->ps.state->es_direction; |
54 | outerPlan = outerPlanState(node); |
55 | |
56 | /* |
57 | * The main logic is a simple state machine. |
58 | */ |
59 | 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 | 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 | if (!ScanDirectionIsForward(direction)) |
79 | return NULL; |
80 | |
81 | /* |
82 | * Check for empty window; if so, treat like empty subplan. |
83 | */ |
84 | if (node->count <= 0 && !node->noCount) |
85 | { |
86 | node->lstate = LIMIT_EMPTY; |
87 | return NULL; |
88 | } |
89 | |
90 | /* |
91 | * Fetch rows from subplan until we reach position > offset. |
92 | */ |
93 | for (;;) |
94 | { |
95 | slot = ExecProcNode(outerPlan); |
96 | if (TupIsNull(slot)) |
97 | { |
98 | /* |
99 | * The subplan returns too few tuples for us to produce |
100 | * any output at all. |
101 | */ |
102 | node->lstate = LIMIT_EMPTY; |
103 | return NULL; |
104 | } |
105 | node->subSlot = slot; |
106 | if (++node->position > node->offset) |
107 | break; |
108 | } |
109 | |
110 | /* |
111 | * Okay, we have the first tuple of the window. |
112 | */ |
113 | node->lstate = LIMIT_INWINDOW; |
114 | 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 | return NULL; |
123 | |
124 | case LIMIT_INWINDOW: |
125 | 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 | if (!node->noCount && |
134 | node->position - node->offset >= node->count) |
135 | { |
136 | node->lstate = LIMIT_WINDOWEND; |
137 | |
138 | /* |
139 | * If we know we won't need to back up, we can release |
140 | * resources at this point. |
141 | */ |
142 | if (!(node->ps.state->es_top_eflags & EXEC_FLAG_BACKWARD)) |
143 | (void) ExecShutdownNode(outerPlan); |
144 | |
145 | return NULL; |
146 | } |
147 | |
148 | /* |
149 | * Get next tuple from subplan, if any. |
150 | */ |
151 | slot = ExecProcNode(outerPlan); |
152 | if (TupIsNull(slot)) |
153 | { |
154 | node->lstate = LIMIT_SUBPLANEOF; |
155 | return NULL; |
156 | } |
157 | node->subSlot = slot; |
158 | node->position++; |
159 | } |
160 | else |
161 | { |
162 | /* |
163 | * Backwards scan, so check for stepping off start of window. |
164 | * As above, change only state-machine status if so. |
165 | */ |
166 | if (node->position <= node->offset + 1) |
167 | { |
168 | node->lstate = LIMIT_WINDOWSTART; |
169 | return NULL; |
170 | } |
171 | |
172 | /* |
173 | * Get previous tuple from subplan; there should be one! |
174 | */ |
175 | slot = ExecProcNode(outerPlan); |
176 | if (TupIsNull(slot)) |
177 | elog(ERROR, "LIMIT subplan failed to run backwards" ); |
178 | node->subSlot = slot; |
179 | node->position--; |
180 | } |
181 | break; |
182 | |
183 | case LIMIT_SUBPLANEOF: |
184 | if (ScanDirectionIsForward(direction)) |
185 | return NULL; |
186 | |
187 | /* |
188 | * Backing up from subplan EOF, so re-fetch previous tuple; there |
189 | * should be one! Note previous tuple must be in window. |
190 | */ |
191 | slot = ExecProcNode(outerPlan); |
192 | if (TupIsNull(slot)) |
193 | elog(ERROR, "LIMIT subplan failed to run backwards" ); |
194 | node->subSlot = slot; |
195 | node->lstate = LIMIT_INWINDOW; |
196 | /* position does not change 'cause we didn't advance it before */ |
197 | break; |
198 | |
199 | case LIMIT_WINDOWEND: |
200 | if (ScanDirectionIsForward(direction)) |
201 | return NULL; |
202 | |
203 | /* |
204 | * Backing up from window end: simply re-return the last tuple |
205 | * fetched from the subplan. |
206 | */ |
207 | slot = node->subSlot; |
208 | node->lstate = LIMIT_INWINDOW; |
209 | /* position does not change 'cause we didn't advance it before */ |
210 | break; |
211 | |
212 | case LIMIT_WINDOWSTART: |
213 | if (!ScanDirectionIsForward(direction)) |
214 | return NULL; |
215 | |
216 | /* |
217 | * Advancing after having backed off window start: simply |
218 | * re-return the last tuple fetched from the subplan. |
219 | */ |
220 | slot = node->subSlot; |
221 | node->lstate = LIMIT_INWINDOW; |
222 | /* position does not change 'cause we didn't change it before */ |
223 | break; |
224 | |
225 | default: |
226 | elog(ERROR, "impossible LIMIT state: %d" , |
227 | (int) node->lstate); |
228 | slot = NULL; /* keep compiler quiet */ |
229 | break; |
230 | } |
231 | |
232 | /* Return the current tuple */ |
233 | Assert(!TupIsNull(slot)); |
234 | |
235 | return slot; |
236 | } |
237 | |
238 | /* |
239 | * Evaluate the limit/offset expressions --- done at startup or rescan. |
240 | * |
241 | * This is also a handy place to reset the current-position state info. |
242 | */ |
243 | static void |
244 | recompute_limits(LimitState *node) |
245 | { |
246 | ExprContext *econtext = node->ps.ps_ExprContext; |
247 | Datum val; |
248 | bool isNull; |
249 | |
250 | if (node->limitOffset) |
251 | { |
252 | val = ExecEvalExprSwitchContext(node->limitOffset, |
253 | econtext, |
254 | &isNull); |
255 | /* Interpret NULL offset as no offset */ |
256 | if (isNull) |
257 | node->offset = 0; |
258 | else |
259 | { |
260 | node->offset = DatumGetInt64(val); |
261 | if (node->offset < 0) |
262 | ereport(ERROR, |
263 | (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE), |
264 | errmsg("OFFSET must not be negative" ))); |
265 | } |
266 | } |
267 | else |
268 | { |
269 | /* No OFFSET supplied */ |
270 | node->offset = 0; |
271 | } |
272 | |
273 | if (node->limitCount) |
274 | { |
275 | val = ExecEvalExprSwitchContext(node->limitCount, |
276 | econtext, |
277 | &isNull); |
278 | /* Interpret NULL count as no count (LIMIT ALL) */ |
279 | if (isNull) |
280 | { |
281 | node->count = 0; |
282 | node->noCount = true; |
283 | } |
284 | else |
285 | { |
286 | node->count = DatumGetInt64(val); |
287 | if (node->count < 0) |
288 | ereport(ERROR, |
289 | (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE), |
290 | errmsg("LIMIT must not be negative" ))); |
291 | node->noCount = false; |
292 | } |
293 | } |
294 | else |
295 | { |
296 | /* No COUNT supplied */ |
297 | node->count = 0; |
298 | node->noCount = true; |
299 | } |
300 | |
301 | /* Reset position to start-of-scan */ |
302 | node->position = 0; |
303 | node->subSlot = NULL; |
304 | |
305 | /* Set state-machine state */ |
306 | node->lstate = LIMIT_RESCAN; |
307 | |
308 | /* |
309 | * Notify child node about limit. Note: think not to "optimize" by |
310 | * skipping ExecSetTupleBound if compute_tuples_needed returns < 0. We |
311 | * must update the child node anyway, in case this is a rescan and the |
312 | * previous time we got a different result. |
313 | */ |
314 | ExecSetTupleBound(compute_tuples_needed(node), outerPlanState(node)); |
315 | } |
316 | |
317 | /* |
318 | * Compute the maximum number of tuples needed to satisfy this Limit node. |
319 | * Return a negative value if there is not a determinable limit. |
320 | */ |
321 | static int64 |
322 | compute_tuples_needed(LimitState *node) |
323 | { |
324 | if (node->noCount) |
325 | return -1; |
326 | /* Note: if this overflows, we'll return a negative value, which is OK */ |
327 | return node->count + node->offset; |
328 | } |
329 | |
330 | /* ---------------------------------------------------------------- |
331 | * ExecInitLimit |
332 | * |
333 | * This initializes the limit node state structures and |
334 | * the node's subplan. |
335 | * ---------------------------------------------------------------- |
336 | */ |
337 | LimitState * |
338 | ExecInitLimit(Limit *node, EState *estate, int eflags) |
339 | { |
340 | LimitState *limitstate; |
341 | Plan *outerPlan; |
342 | |
343 | /* check for unsupported flags */ |
344 | Assert(!(eflags & EXEC_FLAG_MARK)); |
345 | |
346 | /* |
347 | * create state structure |
348 | */ |
349 | limitstate = makeNode(LimitState); |
350 | limitstate->ps.plan = (Plan *) node; |
351 | limitstate->ps.state = estate; |
352 | limitstate->ps.ExecProcNode = ExecLimit; |
353 | |
354 | limitstate->lstate = LIMIT_INITIAL; |
355 | |
356 | /* |
357 | * Miscellaneous initialization |
358 | * |
359 | * Limit nodes never call ExecQual or ExecProject, but they need an |
360 | * exprcontext anyway to evaluate the limit/offset parameters in. |
361 | */ |
362 | ExecAssignExprContext(estate, &limitstate->ps); |
363 | |
364 | /* |
365 | * initialize outer plan |
366 | */ |
367 | outerPlan = outerPlan(node); |
368 | outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags); |
369 | |
370 | /* |
371 | * initialize child expressions |
372 | */ |
373 | limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset, |
374 | (PlanState *) limitstate); |
375 | limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount, |
376 | (PlanState *) limitstate); |
377 | |
378 | /* |
379 | * Initialize result type. |
380 | */ |
381 | ExecInitResultTypeTL(&limitstate->ps); |
382 | |
383 | limitstate->ps.resultopsset = true; |
384 | limitstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(limitstate), |
385 | &limitstate->ps.resultopsfixed); |
386 | |
387 | /* |
388 | * limit nodes do no projections, so initialize projection info for this |
389 | * node appropriately |
390 | */ |
391 | limitstate->ps.ps_ProjInfo = NULL; |
392 | |
393 | return limitstate; |
394 | } |
395 | |
396 | /* ---------------------------------------------------------------- |
397 | * ExecEndLimit |
398 | * |
399 | * This shuts down the subplan and frees resources allocated |
400 | * to this node. |
401 | * ---------------------------------------------------------------- |
402 | */ |
403 | void |
404 | ExecEndLimit(LimitState *node) |
405 | { |
406 | ExecFreeExprContext(&node->ps); |
407 | ExecEndNode(outerPlanState(node)); |
408 | } |
409 | |
410 | |
411 | void |
412 | ExecReScanLimit(LimitState *node) |
413 | { |
414 | /* |
415 | * Recompute limit/offset in case parameters changed, and reset the state |
416 | * machine. We must do this before rescanning our child node, in case |
417 | * it's a Sort that we are passing the parameters down to. |
418 | */ |
419 | recompute_limits(node); |
420 | |
421 | /* |
422 | * if chgParam of subnode is not null then plan will be re-scanned by |
423 | * first ExecProcNode. |
424 | */ |
425 | if (node->ps.lefttree->chgParam == NULL) |
426 | ExecReScan(node->ps.lefttree); |
427 | } |
428 | |