@@ -41,6 +41,7 @@ static TupleTableSlot * /* return: a tuple or NULL */
4141ExecLimit (PlanState * pstate )
4242{
4343 LimitState * node = castNode (LimitState , pstate );
44+ ExprContext * econtext = node -> ps .ps_ExprContext ;
4445 ScanDirection direction ;
4546 TupleTableSlot * slot ;
4647 PlanState * outerPlan ;
@@ -102,6 +103,16 @@ ExecLimit(PlanState *pstate)
102103 node -> lstate = LIMIT_EMPTY ;
103104 return NULL ;
104105 }
106+
107+ /*
108+ * Tuple at limit is needed for comparation in subsequent
109+ * execution to detect ties.
110+ */
111+ if (node -> limitOption == LIMIT_OPTION_WITH_TIES &&
112+ node -> position - node -> offset == node -> count - 1 )
113+ {
114+ ExecCopySlot (node -> last_slot , slot );
115+ }
105116 node -> subSlot = slot ;
106117 if (++ node -> position > node -> offset )
107118 break ;
@@ -125,10 +136,13 @@ ExecLimit(PlanState *pstate)
125136 if (ScanDirectionIsForward (direction ))
126137 {
127138 /*
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.
139+ * Forwards scan, so check for stepping off end of window. At
140+ * the end of the window, the behavior depends on whether WITH
141+ * TIES was specified: in that case, we need to change the
142+ * state machine to LIMIT_WINDOWTIES. If not (nothing was
143+ * specified, or ONLY was) return NULL without advancing the
144+ * subplan or the position variable but change the state
145+ * machine to record having done so
132146 *
133147 * Once at the end, ideally, we can shut down parallel
134148 * resources but that would destroy the parallel context which
@@ -139,27 +153,102 @@ ExecLimit(PlanState *pstate)
139153 if (!node -> noCount &&
140154 node -> position - node -> offset >= node -> count )
141155 {
142- node -> lstate = LIMIT_WINDOWEND ;
156+ if (node -> limitOption == LIMIT_OPTION_COUNT )
157+ {
158+ node -> lstate = LIMIT_WINDOWEND ;
159+ return NULL ;
160+ }
161+ else
162+ {
163+ node -> lstate = LIMIT_WINDOWEND_TIES ;
164+ /* fall-through */
165+ }
166+ }
167+ else
168+ {
169+ /*
170+ * Get next tuple from subplan, if any.
171+ */
172+ slot = ExecProcNode (outerPlan );
173+ if (TupIsNull (slot ))
174+ {
175+ node -> lstate = LIMIT_SUBPLANEOF ;
176+ return NULL ;
177+ }
178+
179+ /*
180+ * Tuple at limit is needed for comparation in subsequent
181+ * execution to detect ties.
182+ */
183+ if (node -> limitOption == LIMIT_OPTION_WITH_TIES &&
184+ node -> position - node -> offset == node -> count - 1 )
185+ {
186+ ExecCopySlot (node -> last_slot , slot );
187+ }
188+ node -> subSlot = slot ;
189+ node -> position ++ ;
190+ break ;
191+ }
192+ }
193+ else
194+ {
195+ /*
196+ * Backwards scan, so check for stepping off start of window.
197+ * As above, change only state-machine status if so.
198+ */
199+ if (node -> position <= node -> offset + 1 )
200+ {
201+ node -> lstate = LIMIT_WINDOWSTART ;
143202 return NULL ;
144203 }
145204
146205 /*
147- * Get next tuple from subplan, if any.
206+ * Get previous tuple from subplan; there should be one!
207+ */
208+ slot = ExecProcNode (outerPlan );
209+ if (TupIsNull (slot ))
210+ elog (ERROR , "LIMIT subplan failed to run backwards" );
211+ node -> subSlot = slot ;
212+ node -> position -- ;
213+ break ;
214+ }
215+
216+ case LIMIT_WINDOWEND_TIES :
217+ if (ScanDirectionIsForward (direction ))
218+ {
219+ /*
220+ * Advance the subplan until we find the first row with
221+ * different ORDER BY pathkeys.
148222 */
149223 slot = ExecProcNode (outerPlan );
150224 if (TupIsNull (slot ))
151225 {
152226 node -> lstate = LIMIT_SUBPLANEOF ;
153227 return NULL ;
154228 }
155- node -> subSlot = slot ;
156- node -> position ++ ;
229+
230+ /*
231+ * Test if the new tuple and the last tuple match. If so we
232+ * return the tuple.
233+ */
234+ econtext -> ecxt_innertuple = slot ;
235+ econtext -> ecxt_outertuple = node -> last_slot ;
236+ if (ExecQualAndReset (node -> eqfunction , econtext ))
237+ {
238+ node -> subSlot = slot ;
239+ node -> position ++ ;
240+ }
241+ else
242+ {
243+ node -> lstate = LIMIT_WINDOWEND ;
244+ return NULL ;
245+ }
157246 }
158247 else
159248 {
160249 /*
161250 * Backwards scan, so check for stepping off start of window.
162- * As above, change only state-machine status if so.
251+ * Change only state-machine status if so.
163252 */
164253 if (node -> position <= node -> offset + 1 )
165254 {
@@ -168,13 +257,15 @@ ExecLimit(PlanState *pstate)
168257 }
169258
170259 /*
171- * Get previous tuple from subplan; there should be one!
260+ * Get previous tuple from subplan; there should be one! And
261+ * change state-machine status.
172262 */
173263 slot = ExecProcNode (outerPlan );
174264 if (TupIsNull (slot ))
175265 elog (ERROR , "LIMIT subplan failed to run backwards" );
176266 node -> subSlot = slot ;
177267 node -> position -- ;
268+ node -> lstate = LIMIT_INWINDOW ;
178269 }
179270 break ;
180271
@@ -199,12 +290,28 @@ ExecLimit(PlanState *pstate)
199290 return NULL ;
200291
201292 /*
202- * Backing up from window end: simply re-return the last tuple
203- * fetched from the subplan.
293+ * We already past one position to detect ties so re-fetch
294+ * previous tuple; there should be one! Note previous tuple must
295+ * be in window.
204296 */
205- slot = node -> subSlot ;
206- node -> lstate = LIMIT_INWINDOW ;
207- /* position does not change 'cause we didn't advance it before */
297+ if (node -> limitOption == LIMIT_OPTION_WITH_TIES )
298+ {
299+ slot = ExecProcNode (outerPlan );
300+ if (TupIsNull (slot ))
301+ elog (ERROR , "LIMIT subplan failed to run backwards" );
302+ node -> subSlot = slot ;
303+ node -> lstate = LIMIT_INWINDOW ;
304+ }
305+ else
306+ {
307+ /*
308+ * Backing up from window end: simply re-return the last tuple
309+ * fetched from the subplan.
310+ */
311+ slot = node -> subSlot ;
312+ node -> lstate = LIMIT_INWINDOW ;
313+ /* position does not change 'cause we didn't advance it before */
314+ }
208315 break ;
209316
210317 case LIMIT_WINDOWSTART :
@@ -319,7 +426,7 @@ recompute_limits(LimitState *node)
319426static int64
320427compute_tuples_needed (LimitState * node )
321428{
322- if (node -> noCount )
429+ if (( node -> noCount ) || ( node -> limitOption == LIMIT_OPTION_WITH_TIES ) )
323430 return -1 ;
324431 /* Note: if this overflows, we'll return a negative value, which is OK */
325432 return node -> count + node -> offset ;
@@ -372,6 +479,7 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
372479 (PlanState * ) limitstate );
373480 limitstate -> limitCount = ExecInitExpr ((Expr * ) node -> limitCount ,
374481 (PlanState * ) limitstate );
482+ limitstate -> limitOption = node -> limitOption ;
375483
376484 /*
377485 * Initialize result type.
@@ -388,6 +496,26 @@ ExecInitLimit(Limit *node, EState *estate, int eflags)
388496 */
389497 limitstate -> ps .ps_ProjInfo = NULL ;
390498
499+ /*
500+ * Initialize the equality evaluation, to detect ties.
501+ */
502+ if (node -> limitOption == LIMIT_OPTION_WITH_TIES )
503+ {
504+ TupleDesc desc ;
505+ const TupleTableSlotOps * ops ;
506+
507+ desc = ExecGetResultType (outerPlanState (limitstate ));
508+ ops = ExecGetResultSlotOps (outerPlanState (limitstate ), NULL );
509+
510+ limitstate -> last_slot = ExecInitExtraTupleSlot (estate , desc , ops );
511+ limitstate -> eqfunction = execTuplesMatchPrepare (desc ,
512+ node -> uniqNumCols ,
513+ node -> uniqColIdx ,
514+ node -> uniqOperators ,
515+ node -> uniqCollations ,
516+ & limitstate -> ps );
517+ }
518+
391519 return limitstate ;
392520}
393521
0 commit comments