8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
@@ -42,7 +42,6 @@ ExecLimit(Limit *node)
42
42
TupleTableSlot * resultTupleSlot ;
43
43
TupleTableSlot * slot ;
44
44
Plan * outerPlan ;
45
- long netlimit ;
46
45
47
46
/*
48
47
* get information from the node
@@ -53,93 +52,160 @@ ExecLimit(Limit *node)
53
52
resultTupleSlot = limitstate -> cstate .cs_ResultTupleSlot ;
54
53
55
54
/*
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.
59
56
*/
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 )
68
58
{
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 ))
89
64
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 ;
94
77
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
+ }
103
79
/*
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.
106
81
*/
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 :
107
112
if (ScanDirectionIsForward (direction ))
108
113
{
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 ;
111
136
limitstate -> position ++ ;
112
- limitstate -> atEnd = true;
113
137
}
114
138
else
115
139
{
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 -- ;
118
157
}
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 */
140
203
break ;
141
204
}
142
205
206
+ /* Return the current tuple */
207
+ Assert (!TupIsNull (slot ));
208
+
143
209
ExecStoreTuple (slot -> val ,
144
210
resultTupleSlot ,
145
211
InvalidBuffer ,
@@ -181,6 +247,7 @@ recompute_limits(Limit *node)
181
247
182
248
if (node -> limitCount )
183
249
{
250
+ limitstate -> noCount = false;
184
251
limitstate -> count =
185
252
DatumGetInt32 (ExecEvalExprSwitchContext (node -> limitCount ,
186
253
econtext ,
@@ -199,12 +266,9 @@ recompute_limits(Limit *node)
199
266
limitstate -> noCount = true;
200
267
}
201
268
202
- /* Reset position data to start-of-scan */
269
+ /* Reset position to start-of-scan */
203
270
limitstate -> position = 0 ;
204
- limitstate -> atEnd = false;
205
-
206
- /* Set flag that params are computed */
207
- limitstate -> parmsSet = true;
271
+ limitstate -> subSlot = NULL ;
208
272
}
209
273
210
274
/* ----------------------------------------------------------------
@@ -230,7 +294,7 @@ ExecInitLimit(Limit *node, EState *estate, Plan *parent)
230
294
*/
231
295
limitstate = makeNode (LimitState );
232
296
node -> limitstate = limitstate ;
233
- limitstate -> parmsSet = false ;
297
+ limitstate -> lstate = LIMIT_INITIAL ;
234
298
235
299
/*
236
300
* Miscellaneous initialization
@@ -297,10 +361,10 @@ ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
297
361
{
298
362
LimitState * limitstate = node -> limitstate ;
299
363
300
- ExecClearTuple (limitstate -> cstate .cs_ResultTupleSlot );
364
+ /* resetting lstate will force offset/limit recalculation */
365
+ limitstate -> lstate = LIMIT_INITIAL ;
301
366
302
- /* force recalculation of limit expressions on first call */
303
- limitstate -> parmsSet = false;
367
+ ExecClearTuple (limitstate -> cstate .cs_ResultTupleSlot );
304
368
305
369
/*
306
370
* if chgParam of subnode is not null then plan will be re-scanned by
0 commit comments