]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeLimit.c
Code review for bigint-LIMIT patch. Fix missed planner dependency,
[postgresql] / src / backend / executor / nodeLimit.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeLimit.c
4  *        Routines to handle limiting of query results where appropriate
5  *
6  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/executor/nodeLimit.c,v 1.27 2006/07/26 19:31:50 tgl Exp $
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
27 static void recompute_limits(LimitState *node);
28
29
30 /* ----------------------------------------------------------------
31  *              ExecLimit
32  *
33  *              This is a very simple node which just performs LIMIT/OFFSET
34  *              filtering on the stream of tuples returned by a subplan.
35  * ----------------------------------------------------------------
36  */
37 TupleTableSlot *                                /* return: a tuple or NULL */
38 ExecLimit(LimitState *node)
39 {
40         ScanDirection direction;
41         TupleTableSlot *slot;
42         PlanState  *outerPlan;
43
44         /*
45          * get information from the node
46          */
47         direction = node->ps.state->es_direction;
48         outerPlan = outerPlanState(node);
49
50         /*
51          * The main logic is a simple state machine.
52          */
53         switch (node->lstate)
54         {
55                 case LIMIT_INITIAL:
56
57                         /*
58                          * If backwards scan, just return NULL without changing state.
59                          */
60                         if (!ScanDirectionIsForward(direction))
61                                 return NULL;
62
63                         /*
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.
67                          */
68                         recompute_limits(node);
69
70                         /*
71                          * Check for empty window; if so, treat like empty subplan.
72                          */
73                         if (node->count <= 0 && !node->noCount)
74                         {
75                                 node->lstate = LIMIT_EMPTY;
76                                 return NULL;
77                         }
78
79                         /*
80                          * Fetch rows from subplan until we reach position > offset.
81                          */
82                         for (;;)
83                         {
84                                 slot = ExecProcNode(outerPlan);
85                                 if (TupIsNull(slot))
86                                 {
87                                         /*
88                                          * The subplan returns too few tuples for us to produce
89                                          * any output at all.
90                                          */
91                                         node->lstate = LIMIT_EMPTY;
92                                         return NULL;
93                                 }
94                                 node->subSlot = slot;
95                                 if (++node->position > node->offset)
96                                         break;
97                         }
98
99                         /*
100                          * Okay, we have the first tuple of the window.
101                          */
102                         node->lstate = LIMIT_INWINDOW;
103                         break;
104
105                 case LIMIT_EMPTY:
106
107                         /*
108                          * The subplan is known to return no tuples (or not more than
109                          * OFFSET tuples, in general).  So we return no tuples.
110                          */
111                         return NULL;
112
113                 case LIMIT_INWINDOW:
114                         if (ScanDirectionIsForward(direction))
115                         {
116                                 /*
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.
121                                  */
122                                 if (!node->noCount &&
123                                         node->position >= node->offset + node->count)
124                                 {
125                                         node->lstate = LIMIT_WINDOWEND;
126                                         return NULL;
127                                 }
128
129                                 /*
130                                  * Get next tuple from subplan, if any.
131                                  */
132                                 slot = ExecProcNode(outerPlan);
133                                 if (TupIsNull(slot))
134                                 {
135                                         node->lstate = LIMIT_SUBPLANEOF;
136                                         return NULL;
137                                 }
138                                 node->subSlot = slot;
139                                 node->position++;
140                         }
141                         else
142                         {
143                                 /*
144                                  * Backwards scan, so check for stepping off start of window.
145                                  * As above, change only state-machine status if so.
146                                  */
147                                 if (node->position <= node->offset + 1)
148                                 {
149                                         node->lstate = LIMIT_WINDOWSTART;
150                                         return NULL;
151                                 }
152
153                                 /*
154                                  * Get previous tuple from subplan; there should be one!
155                                  */
156                                 slot = ExecProcNode(outerPlan);
157                                 if (TupIsNull(slot))
158                                         elog(ERROR, "LIMIT subplan failed to run backwards");
159                                 node->subSlot = slot;
160                                 node->position--;
161                         }
162                         break;
163
164                 case LIMIT_SUBPLANEOF:
165                         if (ScanDirectionIsForward(direction))
166                                 return NULL;
167
168                         /*
169                          * Backing up from subplan EOF, so re-fetch previous tuple; there
170                          * should be one!  Note previous tuple must be in window.
171                          */
172                         slot = ExecProcNode(outerPlan);
173                         if (TupIsNull(slot))
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 */
178                         break;
179
180                 case LIMIT_WINDOWEND:
181                         if (ScanDirectionIsForward(direction))
182                                 return NULL;
183
184                         /*
185                          * Backing up from window end: simply re-return the last tuple
186                          * fetched from the subplan.
187                          */
188                         slot = node->subSlot;
189                         node->lstate = LIMIT_INWINDOW;
190                         /* position does not change 'cause we didn't advance it before */
191                         break;
192
193                 case LIMIT_WINDOWSTART:
194                         if (!ScanDirectionIsForward(direction))
195                                 return NULL;
196
197                         /*
198                          * Advancing after having backed off window start: simply
199                          * re-return the last tuple fetched from the subplan.
200                          */
201                         slot = node->subSlot;
202                         node->lstate = LIMIT_INWINDOW;
203                         /* position does not change 'cause we didn't change it before */
204                         break;
205
206                 default:
207                         elog(ERROR, "impossible LIMIT state: %d",
208                                  (int) node->lstate);
209                         slot = NULL;            /* keep compiler quiet */
210                         break;
211         }
212
213         /* Return the current tuple */
214         Assert(!TupIsNull(slot));
215
216         return slot;
217 }
218
219 /*
220  * Evaluate the limit/offset expressions --- done at start of each scan.
221  *
222  * This is also a handy place to reset the current-position state info.
223  */
224 static void
225 recompute_limits(LimitState *node)
226 {
227         ExprContext *econtext = node->ps.ps_ExprContext;
228         bool            isNull;
229
230         if (node->limitOffset)
231         {
232                 node->offset =
233                         DatumGetInt64(ExecEvalExprSwitchContext(node->limitOffset,
234                                                                                                         econtext,
235                                                                                                         &isNull,
236                                                                                                         NULL));
237                 /* Interpret NULL offset as no offset */
238                 if (isNull)
239                         node->offset = 0;
240                 else if (node->offset < 0)
241                         node->offset = 0;
242         }
243         else
244         {
245                 /* No OFFSET supplied */
246                 node->offset = 0;
247         }
248
249         if (node->limitCount)
250         {
251                 node->noCount = false;
252                 node->count =
253                         DatumGetInt64(ExecEvalExprSwitchContext(node->limitCount,
254                                                                                                         econtext,
255                                                                                                         &isNull,
256                                                                                                         NULL));
257                 /* Interpret NULL count as no count (LIMIT ALL) */
258                 if (isNull)
259                         node->noCount = true;
260                 else if (node->count < 0)
261                         node->count = 0;
262         }
263         else
264         {
265                 /* No COUNT supplied */
266                 node->count = 0;
267                 node->noCount = true;
268         }
269
270         /* Reset position to start-of-scan */
271         node->position = 0;
272         node->subSlot = NULL;
273 }
274
275 /* ----------------------------------------------------------------
276  *              ExecInitLimit
277  *
278  *              This initializes the limit node state structures and
279  *              the node's subplan.
280  * ----------------------------------------------------------------
281  */
282 LimitState *
283 ExecInitLimit(Limit *node, EState *estate, int eflags)
284 {
285         LimitState *limitstate;
286         Plan       *outerPlan;
287
288         /* check for unsupported flags */
289         Assert(!(eflags & EXEC_FLAG_MARK));
290
291         /*
292          * create state structure
293          */
294         limitstate = makeNode(LimitState);
295         limitstate->ps.plan = (Plan *) node;
296         limitstate->ps.state = estate;
297
298         limitstate->lstate = LIMIT_INITIAL;
299
300         /*
301          * Miscellaneous initialization
302          *
303          * Limit nodes never call ExecQual or ExecProject, but they need an
304          * exprcontext anyway to evaluate the limit/offset parameters in.
305          */
306         ExecAssignExprContext(estate, &limitstate->ps);
307
308         /*
309          * initialize child expressions
310          */
311         limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
312                                                                                    (PlanState *) limitstate);
313         limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
314                                                                                   (PlanState *) limitstate);
315
316 #define LIMIT_NSLOTS 1
317
318         /*
319          * Tuple table initialization (XXX not actually used...)
320          */
321         ExecInitResultTupleSlot(estate, &limitstate->ps);
322
323         /*
324          * then initialize outer plan
325          */
326         outerPlan = outerPlan(node);
327         outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
328
329         /*
330          * limit nodes do no projections, so initialize projection info for this
331          * node appropriately
332          */
333         ExecAssignResultTypeFromTL(&limitstate->ps);
334         limitstate->ps.ps_ProjInfo = NULL;
335
336         return limitstate;
337 }
338
339 int
340 ExecCountSlotsLimit(Limit *node)
341 {
342         return ExecCountSlotsNode(outerPlan(node)) +
343                 ExecCountSlotsNode(innerPlan(node)) +
344                 LIMIT_NSLOTS;
345 }
346
347 /* ----------------------------------------------------------------
348  *              ExecEndLimit
349  *
350  *              This shuts down the subplan and frees resources allocated
351  *              to this node.
352  * ----------------------------------------------------------------
353  */
354 void
355 ExecEndLimit(LimitState *node)
356 {
357         ExecFreeExprContext(&node->ps);
358         ExecEndNode(outerPlanState(node));
359 }
360
361
362 void
363 ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
364 {
365         /* resetting lstate will force offset/limit recalculation */
366         node->lstate = LIMIT_INITIAL;
367
368         /*
369          * if chgParam of subnode is not null then plan will be re-scanned by
370          * first ExecProcNode.
371          */
372         if (((PlanState *) node)->lefttree->chgParam == NULL)
373                 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
374 }