PostgreSQL Source Code  git master
nodeLimit.c File Reference
#include "postgres.h"
#include "executor/executor.h"
#include "executor/nodeLimit.h"
#include "miscadmin.h"
Include dependency graph for nodeLimit.c:

Go to the source code of this file.

Functions

static void recompute_limits (LimitState *node)
 
static int64 compute_tuples_needed (LimitState *node)
 
static TupleTableSlotExecLimit (PlanState *pstate)
 
LimitStateExecInitLimit (Limit *node, EState *estate, int eflags)
 
void ExecEndLimit (LimitState *node)
 
void ExecReScanLimit (LimitState *node)
 

Function Documentation

◆ compute_tuples_needed()

static int64 compute_tuples_needed ( LimitState node)
static

Definition at line 431 of file nodeLimit.c.

432 {
433  if ((node->noCount) || (node->limitOption == LIMIT_OPTION_WITH_TIES))
434  return -1;
435  /* Note: if this overflows, we'll return a negative value, which is OK */
436  return node->count + node->offset;
437 }
@ LIMIT_OPTION_WITH_TIES
Definition: nodes.h:431
LimitOption limitOption
Definition: execnodes.h:2839
bool noCount
Definition: execnodes.h:2842
int64 offset
Definition: execnodes.h:2840
int64 count
Definition: execnodes.h:2841

References LimitState::count, LIMIT_OPTION_WITH_TIES, LimitState::limitOption, LimitState::noCount, and LimitState::offset.

Referenced by recompute_limits().

◆ ExecEndLimit()

void ExecEndLimit ( LimitState node)

Definition at line 534 of file nodeLimit.c.

535 {
537 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
#define outerPlanState(node)
Definition: execnodes.h:1214

References ExecEndNode(), and outerPlanState.

Referenced by ExecEndNode().

◆ ExecInitLimit()

LimitState* ExecInitLimit ( Limit node,
EState estate,
int  eflags 
)

Definition at line 447 of file nodeLimit.c.

448 {
449  LimitState *limitstate;
450  Plan *outerPlan;
451 
452  /* check for unsupported flags */
453  Assert(!(eflags & EXEC_FLAG_MARK));
454 
455  /*
456  * create state structure
457  */
458  limitstate = makeNode(LimitState);
459  limitstate->ps.plan = (Plan *) node;
460  limitstate->ps.state = estate;
461  limitstate->ps.ExecProcNode = ExecLimit;
462 
463  limitstate->lstate = LIMIT_INITIAL;
464 
465  /*
466  * Miscellaneous initialization
467  *
468  * Limit nodes never call ExecQual or ExecProject, but they need an
469  * exprcontext anyway to evaluate the limit/offset parameters in.
470  */
471  ExecAssignExprContext(estate, &limitstate->ps);
472 
473  /*
474  * initialize outer plan
475  */
476  outerPlan = outerPlan(node);
477  outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
478 
479  /*
480  * initialize child expressions
481  */
482  limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
483  (PlanState *) limitstate);
484  limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
485  (PlanState *) limitstate);
486  limitstate->limitOption = node->limitOption;
487 
488  /*
489  * Initialize result type.
490  */
491  ExecInitResultTypeTL(&limitstate->ps);
492 
493  limitstate->ps.resultopsset = true;
494  limitstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(limitstate),
495  &limitstate->ps.resultopsfixed);
496 
497  /*
498  * limit nodes do no projections, so initialize projection info for this
499  * node appropriately
500  */
501  limitstate->ps.ps_ProjInfo = NULL;
502 
503  /*
504  * Initialize the equality evaluation, to detect ties.
505  */
506  if (node->limitOption == LIMIT_OPTION_WITH_TIES)
507  {
508  TupleDesc desc;
509  const TupleTableSlotOps *ops;
510 
511  desc = ExecGetResultType(outerPlanState(limitstate));
512  ops = ExecGetResultSlotOps(outerPlanState(limitstate), NULL);
513 
514  limitstate->last_slot = ExecInitExtraTupleSlot(estate, desc, ops);
515  limitstate->eqfunction = execTuplesMatchPrepare(desc,
516  node->uniqNumCols,
517  node->uniqColIdx,
518  node->uniqOperators,
519  node->uniqCollations,
520  &limitstate->ps);
521  }
522 
523  return limitstate;
524 }
#define Assert(condition)
Definition: c.h:858
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:134
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:58
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1842
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1918
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:493
const TupleTableSlotOps * ExecGetResultSlotOps(PlanState *planstate, bool *isfixed)
Definition: execUtils.c:502
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:483
@ LIMIT_INITIAL
Definition: execnodes.h:2824
#define EXEC_FLAG_MARK
Definition: executor.h:69
static TupleTableSlot * ExecLimit(PlanState *pstate)
Definition: nodeLimit.c:40
#define makeNode(_type_)
Definition: nodes.h:155
#define outerPlan(node)
Definition: plannodes.h:182
PlanState ps
Definition: execnodes.h:2836
ExprState * limitOffset
Definition: execnodes.h:2837
ExprState * limitCount
Definition: execnodes.h:2838
TupleTableSlot * last_slot
Definition: execnodes.h:2848
ExprState * eqfunction
Definition: execnodes.h:2846
LimitStateCond lstate
Definition: execnodes.h:2843
LimitOption limitOption
Definition: plannodes.h:1281
Node * limitCount
Definition: plannodes.h:1278
int uniqNumCols
Definition: plannodes.h:1284
Node * limitOffset
Definition: plannodes.h:1275
const TupleTableSlotOps * resultops
Definition: execnodes.h:1195
bool resultopsset
Definition: execnodes.h:1203
Plan * plan
Definition: execnodes.h:1118
EState * state
Definition: execnodes.h:1120
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1158
bool resultopsfixed
Definition: execnodes.h:1199
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:1124

References Assert, LimitState::eqfunction, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecGetResultSlotOps(), ExecGetResultType(), ExecInitExpr(), ExecInitExtraTupleSlot(), ExecInitNode(), ExecInitResultTypeTL(), ExecLimit(), PlanState::ExecProcNode, execTuplesMatchPrepare(), LimitState::last_slot, LIMIT_INITIAL, LIMIT_OPTION_WITH_TIES, LimitState::limitCount, Limit::limitCount, LimitState::limitOffset, Limit::limitOffset, LimitState::limitOption, Limit::limitOption, LimitState::lstate, makeNode, outerPlan, outerPlanState, PlanState::plan, LimitState::ps, PlanState::ps_ProjInfo, PlanState::resultops, PlanState::resultopsfixed, PlanState::resultopsset, PlanState::state, and Limit::uniqNumCols.

Referenced by ExecInitNode().

◆ ExecLimit()

static TupleTableSlot* ExecLimit ( PlanState pstate)
static

Definition at line 40 of file nodeLimit.c.

41 {
42  LimitState *node = castNode(LimitState, pstate);
43  ExprContext *econtext = node->ps.ps_ExprContext;
44  ScanDirection direction;
45  TupleTableSlot *slot;
47 
49 
50  /*
51  * get information from the node
52  */
53  direction = node->ps.state->es_direction;
54  outerPlan = outerPlanState(node);
55 
56  /*
57  * The main logic is a simple state machine.
58  */
59  switch (node->lstate)
60  {
61  case LIMIT_INITIAL:
62 
63  /*
64  * First call for this node, so compute limit/offset. (We can't do
65  * this any earlier, because parameters from upper nodes will not
66  * be set during ExecInitLimit.) This also sets position = 0 and
67  * changes the state to LIMIT_RESCAN.
68  */
69  recompute_limits(node);
70 
71  /* FALL THRU */
72 
73  case LIMIT_RESCAN:
74 
75  /*
76  * If backwards scan, just return NULL without changing state.
77  */
78  if (!ScanDirectionIsForward(direction))
79  return NULL;
80 
81  /*
82  * Check for empty window; if so, treat like empty subplan.
83  */
84  if (node->count <= 0 && !node->noCount)
85  {
86  node->lstate = LIMIT_EMPTY;
87  return NULL;
88  }
89 
90  /*
91  * Fetch rows from subplan until we reach position > offset.
92  */
93  for (;;)
94  {
95  slot = ExecProcNode(outerPlan);
96  if (TupIsNull(slot))
97  {
98  /*
99  * The subplan returns too few tuples for us to produce
100  * any output at all.
101  */
102  node->lstate = LIMIT_EMPTY;
103  return NULL;
104  }
105 
106  /*
107  * Tuple at limit is needed for comparison in subsequent
108  * execution to detect ties.
109  */
110  if (node->limitOption == LIMIT_OPTION_WITH_TIES &&
111  node->position - node->offset == node->count - 1)
112  {
113  ExecCopySlot(node->last_slot, slot);
114  }
115  node->subSlot = slot;
116  if (++node->position > node->offset)
117  break;
118  }
119 
120  /*
121  * Okay, we have the first tuple of the window.
122  */
123  node->lstate = LIMIT_INWINDOW;
124  break;
125 
126  case LIMIT_EMPTY:
127 
128  /*
129  * The subplan is known to return no tuples (or not more than
130  * OFFSET tuples, in general). So we return no tuples.
131  */
132  return NULL;
133 
134  case LIMIT_INWINDOW:
135  if (ScanDirectionIsForward(direction))
136  {
137  /*
138  * Forwards scan, so check for stepping off end of window. At
139  * the end of the window, the behavior depends on whether WITH
140  * TIES was specified: if so, we need to change the state
141  * machine to WINDOWEND_TIES, and fall through to the code for
142  * that case. If not (nothing was specified, or ONLY was)
143  * return NULL without advancing the subplan or the position
144  * variable, but change the state machine to record having
145  * done so.
146  *
147  * Once at the end, ideally, we would shut down parallel
148  * resources; but that would destroy the parallel context
149  * which might be required for rescans. To do that, we'll
150  * need to find a way to pass down more information about
151  * whether rescans are possible.
152  */
153  if (!node->noCount &&
154  node->position - node->offset >= node->count)
155  {
156  if (node->limitOption == LIMIT_OPTION_COUNT)
157  {
158  node->lstate = LIMIT_WINDOWEND;
159  return NULL;
160  }
161  else
162  {
164  /* we'll fall through to the next case */
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  * If WITH TIES is active, and this is the last in-window
181  * tuple, save it to be used in subsequent WINDOWEND_TIES
182  * processing.
183  */
184  if (node->limitOption == LIMIT_OPTION_WITH_TIES &&
185  node->position - node->offset == node->count - 1)
186  {
187  ExecCopySlot(node->last_slot, slot);
188  }
189  node->subSlot = slot;
190  node->position++;
191  break;
192  }
193  }
194  else
195  {
196  /*
197  * Backwards scan, so check for stepping off start of window.
198  * As above, only change state-machine status if so.
199  */
200  if (node->position <= node->offset + 1)
201  {
202  node->lstate = LIMIT_WINDOWSTART;
203  return NULL;
204  }
205 
206  /*
207  * Get previous tuple from subplan; there should be one!
208  */
209  slot = ExecProcNode(outerPlan);
210  if (TupIsNull(slot))
211  elog(ERROR, "LIMIT subplan failed to run backwards");
212  node->subSlot = slot;
213  node->position--;
214  break;
215  }
216 
218  /* FALL THRU */
219 
221  if (ScanDirectionIsForward(direction))
222  {
223  /*
224  * Advance the subplan until we find the first row with
225  * different ORDER BY pathkeys.
226  */
227  slot = ExecProcNode(outerPlan);
228  if (TupIsNull(slot))
229  {
230  node->lstate = LIMIT_SUBPLANEOF;
231  return NULL;
232  }
233 
234  /*
235  * Test if the new tuple and the last tuple match. If so we
236  * return the tuple.
237  */
238  econtext->ecxt_innertuple = slot;
239  econtext->ecxt_outertuple = node->last_slot;
240  if (ExecQualAndReset(node->eqfunction, econtext))
241  {
242  node->subSlot = slot;
243  node->position++;
244  }
245  else
246  {
247  node->lstate = LIMIT_WINDOWEND;
248  return NULL;
249  }
250  }
251  else
252  {
253  /*
254  * Backwards scan, so check for stepping off start of window.
255  * Change only state-machine status if so.
256  */
257  if (node->position <= node->offset + 1)
258  {
259  node->lstate = LIMIT_WINDOWSTART;
260  return NULL;
261  }
262 
263  /*
264  * Get previous tuple from subplan; there should be one! And
265  * change state-machine status.
266  */
267  slot = ExecProcNode(outerPlan);
268  if (TupIsNull(slot))
269  elog(ERROR, "LIMIT subplan failed to run backwards");
270  node->subSlot = slot;
271  node->position--;
272  node->lstate = LIMIT_INWINDOW;
273  }
274  break;
275 
276  case LIMIT_SUBPLANEOF:
277  if (ScanDirectionIsForward(direction))
278  return NULL;
279 
280  /*
281  * Backing up from subplan EOF, so re-fetch previous tuple; there
282  * should be one! Note previous tuple must be in window.
283  */
284  slot = ExecProcNode(outerPlan);
285  if (TupIsNull(slot))
286  elog(ERROR, "LIMIT subplan failed to run backwards");
287  node->subSlot = slot;
288  node->lstate = LIMIT_INWINDOW;
289  /* position does not change 'cause we didn't advance it before */
290  break;
291 
292  case LIMIT_WINDOWEND:
293  if (ScanDirectionIsForward(direction))
294  return NULL;
295 
296  /*
297  * We already past one position to detect ties so re-fetch
298  * previous tuple; there should be one! Note previous tuple must
299  * be in window.
300  */
301  if (node->limitOption == LIMIT_OPTION_WITH_TIES)
302  {
303  slot = ExecProcNode(outerPlan);
304  if (TupIsNull(slot))
305  elog(ERROR, "LIMIT subplan failed to run backwards");
306  node->subSlot = slot;
307  node->lstate = LIMIT_INWINDOW;
308  }
309  else
310  {
311  /*
312  * Backing up from window end: simply re-return the last tuple
313  * fetched from the subplan.
314  */
315  slot = node->subSlot;
316  node->lstate = LIMIT_INWINDOW;
317  /* position does not change 'cause we didn't advance it before */
318  }
319  break;
320 
321  case LIMIT_WINDOWSTART:
322  if (!ScanDirectionIsForward(direction))
323  return NULL;
324 
325  /*
326  * Advancing after having backed off window start: simply
327  * re-return the last tuple fetched from the subplan.
328  */
329  slot = node->subSlot;
330  node->lstate = LIMIT_INWINDOW;
331  /* position does not change 'cause we didn't change it before */
332  break;
333 
334  default:
335  elog(ERROR, "impossible LIMIT state: %d",
336  (int) node->lstate);
337  slot = NULL; /* keep compiler quiet */
338  break;
339  }
340 
341  /* Return the current tuple */
342  Assert(!TupIsNull(slot));
343 
344  return slot;
345 }
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
@ LIMIT_WINDOWEND_TIES
Definition: execnodes.h:2828
@ LIMIT_WINDOWEND
Definition: execnodes.h:2830
@ LIMIT_INWINDOW
Definition: execnodes.h:2827
@ LIMIT_SUBPLANEOF
Definition: execnodes.h:2829
@ LIMIT_WINDOWSTART
Definition: execnodes.h:2831
@ LIMIT_EMPTY
Definition: execnodes.h:2826
@ LIMIT_RESCAN
Definition: execnodes.h:2825
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:440
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:269
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
static void recompute_limits(LimitState *node)
Definition: nodeLimit.c:353
@ LIMIT_OPTION_COUNT
Definition: nodes.h:430
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define ScanDirectionIsForward(direction)
Definition: sdir.h:64
ScanDirection
Definition: sdir.h:25
ScanDirection es_direction
Definition: execnodes.h:623
TupleTableSlot * ecxt_innertuple
Definition: execnodes.h:257
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:259
int64 position
Definition: execnodes.h:2844
TupleTableSlot * subSlot
Definition: execnodes.h:2845
ExprContext * ps_ExprContext
Definition: execnodes.h:1157
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:509
#define TupIsNull(slot)
Definition: tuptable.h:306

References Assert, castNode, CHECK_FOR_INTERRUPTS, LimitState::count, ExprContext::ecxt_innertuple, ExprContext::ecxt_outertuple, elog, LimitState::eqfunction, ERROR, EState::es_direction, ExecCopySlot(), ExecProcNode(), ExecQualAndReset(), LimitState::last_slot, LIMIT_EMPTY, LIMIT_INITIAL, LIMIT_INWINDOW, LIMIT_OPTION_COUNT, LIMIT_OPTION_WITH_TIES, LIMIT_RESCAN, LIMIT_SUBPLANEOF, LIMIT_WINDOWEND, LIMIT_WINDOWEND_TIES, LIMIT_WINDOWSTART, LimitState::limitOption, LimitState::lstate, LimitState::noCount, LimitState::offset, outerPlan, outerPlanState, LimitState::position, LimitState::ps, PlanState::ps_ExprContext, recompute_limits(), ScanDirectionIsForward, PlanState::state, LimitState::subSlot, and TupIsNull.

Referenced by ExecInitLimit().

◆ ExecReScanLimit()

void ExecReScanLimit ( LimitState node)

Definition at line 541 of file nodeLimit.c.

542 {
544 
545  /*
546  * Recompute limit/offset in case parameters changed, and reset the state
547  * machine. We must do this before rescanning our child node, in case
548  * it's a Sort that we are passing the parameters down to.
549  */
550  recompute_limits(node);
551 
552  /*
553  * if chgParam of subnode is not null then plan will be re-scanned by
554  * first ExecProcNode.
555  */
556  if (outerPlan->chgParam == NULL)
558 }
void ExecReScan(PlanState *node)
Definition: execAmi.c:76

References ExecReScan(), outerPlan, outerPlanState, and recompute_limits().

Referenced by ExecReScan().

◆ recompute_limits()

static void recompute_limits ( LimitState node)
static

Definition at line 353 of file nodeLimit.c.

354 {
355  ExprContext *econtext = node->ps.ps_ExprContext;
356  Datum val;
357  bool isNull;
358 
359  if (node->limitOffset)
360  {
362  econtext,
363  &isNull);
364  /* Interpret NULL offset as no offset */
365  if (isNull)
366  node->offset = 0;
367  else
368  {
369  node->offset = DatumGetInt64(val);
370  if (node->offset < 0)
371  ereport(ERROR,
372  (errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
373  errmsg("OFFSET must not be negative")));
374  }
375  }
376  else
377  {
378  /* No OFFSET supplied */
379  node->offset = 0;
380  }
381 
382  if (node->limitCount)
383  {
385  econtext,
386  &isNull);
387  /* Interpret NULL count as no count (LIMIT ALL) */
388  if (isNull)
389  {
390  node->count = 0;
391  node->noCount = true;
392  }
393  else
394  {
395  node->count = DatumGetInt64(val);
396  if (node->count < 0)
397  ereport(ERROR,
398  (errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
399  errmsg("LIMIT must not be negative")));
400  node->noCount = false;
401  }
402  }
403  else
404  {
405  /* No COUNT supplied */
406  node->count = 0;
407  node->noCount = true;
408  }
409 
410  /* Reset position to start-of-scan */
411  node->position = 0;
412  node->subSlot = NULL;
413 
414  /* Set state-machine state */
415  node->lstate = LIMIT_RESCAN;
416 
417  /*
418  * Notify child node about limit. Note: think not to "optimize" by
419  * skipping ExecSetTupleBound if compute_tuples_needed returns < 0. We
420  * must update the child node anyway, in case this is a rescan and the
421  * previous time we got a different result.
422  */
424 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
void ExecSetTupleBound(int64 tuples_needed, PlanState *child_node)
Definition: execProcnode.c:843
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:348
long val
Definition: informix.c:670
static int64 compute_tuples_needed(LimitState *node)
Definition: nodeLimit.c:431
static int64 DatumGetInt64(Datum X)
Definition: postgres.h:385
uintptr_t Datum
Definition: postgres.h:64

References compute_tuples_needed(), LimitState::count, DatumGetInt64(), ereport, errcode(), errmsg(), ERROR, ExecEvalExprSwitchContext(), ExecSetTupleBound(), LIMIT_RESCAN, LimitState::limitCount, LimitState::limitOffset, LimitState::lstate, LimitState::noCount, LimitState::offset, outerPlanState, LimitState::position, LimitState::ps, PlanState::ps_ExprContext, LimitState::subSlot, and val.

Referenced by ExecLimit(), and ExecReScanLimit().