88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.10 2002/06/20 20:29:28 momjian Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.11 2002/11/22 22:10:01 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -42,7 +42,6 @@ ExecLimit(Limit *node)
4242 TupleTableSlot * resultTupleSlot ;
4343 TupleTableSlot * slot ;
4444 Plan * outerPlan ;
45- long netlimit ;
4645
4746 /*
4847 * get information from the node
@@ -53,93 +52,160 @@ ExecLimit(Limit *node)
5352 resultTupleSlot = limitstate -> cstate .cs_ResultTupleSlot ;
5453
5554 /*
56- * If first call for this scan, compute limit/offset. (We can't do
57- * this any earlier, because parameters from upper nodes may not be
58- * set until now.)
55+ * The main logic is a simple state machine.
5956 */
60- if (!limitstate -> parmsSet )
61- recompute_limits (node );
62- netlimit = limitstate -> offset + limitstate -> count ;
63-
64- /*
65- * now loop, returning only desired tuples.
66- */
67- for (;;)
57+ switch (limitstate -> lstate )
6858 {
69- /*
70- * If we have reached the subplan EOF or the limit, just quit.
71- *
72- * NOTE: when scanning forwards, we must fetch one tuple beyond the
73- * COUNT limit before we can return NULL, else the subplan won't
74- * be properly positioned to start going backwards. Hence test
75- * here is for position > netlimit not position >= netlimit.
76- *
77- * Similarly, when scanning backwards, we must re-fetch the last
78- * tuple in the offset region before we can return NULL. Otherwise
79- * we won't be correctly aligned to start going forward again. So,
80- * although you might think we can quit when position equals
81- * offset + 1, we have to fetch a subplan tuple first, and then
82- * exit when position = offset.
83- */
84- if (ScanDirectionIsForward (direction ))
85- {
86- if (limitstate -> atEnd )
87- return NULL ;
88- if (!limitstate -> noCount && limitstate -> position > netlimit )
59+ case LIMIT_INITIAL :
60+ /*
61+ * If backwards scan, just return NULL without changing state.
62+ */
63+ if (!ScanDirectionIsForward (direction ))
8964 return NULL ;
90- }
91- else
92- {
93- if (limitstate -> position <= limitstate -> offset )
65+ /*
66+ * First call for this scan, so compute limit/offset. (We can't do
67+ * this any earlier, because parameters from upper nodes may not
68+ * be set until now.) This also sets position = 0.
69+ */
70+ recompute_limits (node );
71+ /*
72+ * Check for empty window; if so, treat like empty subplan.
73+ */
74+ if (limitstate -> count <= 0 && !limitstate -> noCount )
75+ {
76+ limitstate -> lstate = LIMIT_EMPTY ;
9477 return NULL ;
95- }
96-
97- /*
98- * fetch a tuple from the outer subplan
99- */
100- slot = ExecProcNode (outerPlan , (Plan * ) node );
101- if (TupIsNull (slot ))
102- {
78+ }
10379 /*
104- * We are at start or end of the subplan. Update local state
105- * appropriately, but always return NULL.
80+ * Fetch rows from subplan until we reach position > offset.
10681 */
82+ for (;;)
83+ {
84+ slot = ExecProcNode (outerPlan , (Plan * ) node );
85+ if (TupIsNull (slot ))
86+ {
87+ /*
88+ * The subplan returns too few tuples for us to produce
89+ * any output at all.
90+ */
91+ limitstate -> lstate = LIMIT_EMPTY ;
92+ return NULL ;
93+ }
94+ limitstate -> subSlot = slot ;
95+ if (++ limitstate -> position > limitstate -> offset )
96+ break ;
97+ }
98+ /*
99+ * Okay, we have the first tuple of the window.
100+ */
101+ limitstate -> lstate = LIMIT_INWINDOW ;
102+ break ;
103+
104+ case LIMIT_EMPTY :
105+ /*
106+ * The subplan is known to return no tuples (or not more than
107+ * OFFSET tuples, in general). So we return no tuples.
108+ */
109+ return NULL ;
110+
111+ case LIMIT_INWINDOW :
107112 if (ScanDirectionIsForward (direction ))
108113 {
109- Assert (!limitstate -> atEnd );
110- /* must bump position to stay in sync for backwards fetch */
114+ /*
115+ * Forwards scan, so check for stepping off end of window.
116+ * If we are at the end of the window, return NULL without
117+ * advancing the subplan or the position variable; but
118+ * change the state machine state to record having done so.
119+ */
120+ if (!limitstate -> noCount &&
121+ limitstate -> position >= limitstate -> offset + limitstate -> count )
122+ {
123+ limitstate -> lstate = LIMIT_WINDOWEND ;
124+ return NULL ;
125+ }
126+ /*
127+ * Get next tuple from subplan, if any.
128+ */
129+ slot = ExecProcNode (outerPlan , (Plan * ) node );
130+ if (TupIsNull (slot ))
131+ {
132+ limitstate -> lstate = LIMIT_SUBPLANEOF ;
133+ return NULL ;
134+ }
135+ limitstate -> subSlot = slot ;
111136 limitstate -> position ++ ;
112- limitstate -> atEnd = true;
113137 }
114138 else
115139 {
116- limitstate -> position = 0 ;
117- limitstate -> atEnd = false;
140+ /*
141+ * Backwards scan, so check for stepping off start of window.
142+ * As above, change only state-machine status if so.
143+ */
144+ if (limitstate -> position <= limitstate -> offset + 1 )
145+ {
146+ limitstate -> lstate = LIMIT_WINDOWSTART ;
147+ return NULL ;
148+ }
149+ /*
150+ * Get previous tuple from subplan; there should be one!
151+ */
152+ slot = ExecProcNode (outerPlan , (Plan * ) node );
153+ if (TupIsNull (slot ))
154+ elog (ERROR , "ExecLimit: subplan failed to run backwards" );
155+ limitstate -> subSlot = slot ;
156+ limitstate -> position -- ;
118157 }
119- return NULL ;
120- }
121-
122- /*
123- * We got the next subplan tuple successfully, so adjust state.
124- */
125- if (ScanDirectionIsForward (direction ))
126- limitstate -> position ++ ;
127- else
128- {
129- limitstate -> position -- ;
130- Assert (limitstate -> position > 0 );
131- }
132- limitstate -> atEnd = false;
133-
134- /*
135- * Now, is this a tuple we want? If not, loop around to fetch
136- * another tuple from the subplan.
137- */
138- if (limitstate -> position > limitstate -> offset &&
139- (limitstate -> noCount || limitstate -> position <= netlimit ))
158+ break ;
159+
160+ case LIMIT_SUBPLANEOF :
161+ if (ScanDirectionIsForward (direction ))
162+ return NULL ;
163+ /*
164+ * Backing up from subplan EOF, so re-fetch previous tuple;
165+ * there should be one! Note previous tuple must be in window.
166+ */
167+ slot = ExecProcNode (outerPlan , (Plan * ) node );
168+ if (TupIsNull (slot ))
169+ elog (ERROR , "ExecLimit: subplan failed to run backwards" );
170+ limitstate -> subSlot = slot ;
171+ limitstate -> lstate = LIMIT_INWINDOW ;
172+ /* position does not change 'cause we didn't advance it before */
173+ break ;
174+
175+ case LIMIT_WINDOWEND :
176+ if (ScanDirectionIsForward (direction ))
177+ return NULL ;
178+ /*
179+ * Backing up from window end: simply re-return the last
180+ * tuple fetched from the subplan.
181+ */
182+ slot = limitstate -> subSlot ;
183+ limitstate -> lstate = LIMIT_INWINDOW ;
184+ /* position does not change 'cause we didn't advance it before */
185+ break ;
186+
187+ case LIMIT_WINDOWSTART :
188+ if (!ScanDirectionIsForward (direction ))
189+ return NULL ;
190+ /*
191+ * Advancing after having backed off window start: simply
192+ * re-return the last tuple fetched from the subplan.
193+ */
194+ slot = limitstate -> subSlot ;
195+ limitstate -> lstate = LIMIT_INWINDOW ;
196+ /* position does not change 'cause we didn't change it before */
197+ break ;
198+
199+ default :
200+ elog (ERROR , "ExecLimit: impossible state %d" ,
201+ (int ) limitstate -> lstate );
202+ slot = NULL ; /* keep compiler quiet */
140203 break ;
141204 }
142205
206+ /* Return the current tuple */
207+ Assert (!TupIsNull (slot ));
208+
143209 ExecStoreTuple (slot -> val ,
144210 resultTupleSlot ,
145211 InvalidBuffer ,
@@ -181,6 +247,7 @@ recompute_limits(Limit *node)
181247
182248 if (node -> limitCount )
183249 {
250+ limitstate -> noCount = false;
184251 limitstate -> count =
185252 DatumGetInt32 (ExecEvalExprSwitchContext (node -> limitCount ,
186253 econtext ,
@@ -199,12 +266,9 @@ recompute_limits(Limit *node)
199266 limitstate -> noCount = true;
200267 }
201268
202- /* Reset position data to start-of-scan */
269+ /* Reset position to start-of-scan */
203270 limitstate -> position = 0 ;
204- limitstate -> atEnd = false;
205-
206- /* Set flag that params are computed */
207- limitstate -> parmsSet = true;
271+ limitstate -> subSlot = NULL ;
208272}
209273
210274/* ----------------------------------------------------------------
@@ -230,7 +294,7 @@ ExecInitLimit(Limit *node, EState *estate, Plan *parent)
230294 */
231295 limitstate = makeNode (LimitState );
232296 node -> limitstate = limitstate ;
233- limitstate -> parmsSet = false ;
297+ limitstate -> lstate = LIMIT_INITIAL ;
234298
235299 /*
236300 * Miscellaneous initialization
@@ -297,10 +361,10 @@ ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
297361{
298362 LimitState * limitstate = node -> limitstate ;
299363
300- ExecClearTuple (limitstate -> cstate .cs_ResultTupleSlot );
364+ /* resetting lstate will force offset/limit recalculation */
365+ limitstate -> lstate = LIMIT_INITIAL ;
301366
302- /* force recalculation of limit expressions on first call */
303- limitstate -> parmsSet = false;
367+ ExecClearTuple (limitstate -> cstate .cs_ResultTupleSlot );
304368
305369 /*
306370 * if chgParam of subnode is not null then plan will be re-scanned by
0 commit comments