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