PostgreSQL Source Code  git master
nodeLimit.c File Reference
#include "postgres.h"
#include "executor/executor.h"
#include "executor/nodeLimit.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.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 432 of file nodeLimit.c.

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

Referenced by recompute_limits().

433 {
434  if ((node->noCount) || (node->limitOption == LIMIT_OPTION_WITH_TIES))
435  return -1;
436  /* Note: if this overflows, we'll return a negative value, which is OK */
437  return node->count + node->offset;
438 }
bool noCount
Definition: execnodes.h:2478
LimitOption limitOption
Definition: execnodes.h:2475
int64 count
Definition: execnodes.h:2477
int64 offset
Definition: execnodes.h:2476

◆ ExecEndLimit()

void ExecEndLimit ( LimitState node)

Definition at line 535 of file nodeLimit.c.

References ExecEndNode(), ExecFreeExprContext(), outerPlanState, and LimitState::ps.

Referenced by ExecEndNode().

536 {
537  ExecFreeExprContext(&node->ps);
539 }
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:543
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:655
#define outerPlanState(node)
Definition: execnodes.h:1039
PlanState ps
Definition: execnodes.h:2472

◆ ExecInitLimit()

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

Definition at line 448 of file nodeLimit.c.

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

Referenced by ExecInitNode().

449 {
450  LimitState *limitstate;
451  Plan *outerPlan;
452 
453  /* check for unsupported flags */
454  Assert(!(eflags & EXEC_FLAG_MARK));
455 
456  /*
457  * create state structure
458  */
459  limitstate = makeNode(LimitState);
460  limitstate->ps.plan = (Plan *) node;
461  limitstate->ps.state = estate;
462  limitstate->ps.ExecProcNode = ExecLimit;
463 
464  limitstate->lstate = LIMIT_INITIAL;
465 
466  /*
467  * Miscellaneous initialization
468  *
469  * Limit nodes never call ExecQual or ExecProject, but they need an
470  * exprcontext anyway to evaluate the limit/offset parameters in.
471  */
472  ExecAssignExprContext(estate, &limitstate->ps);
473 
474  /*
475  * initialize outer plan
476  */
477  outerPlan = outerPlan(node);
478  outerPlanState(limitstate) = ExecInitNode(outerPlan, estate, eflags);
479 
480  /*
481  * initialize child expressions
482  */
483  limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
484  (PlanState *) limitstate);
485  limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
486  (PlanState *) limitstate);
487  limitstate->limitOption = node->limitOption;
488 
489  /*
490  * Initialize result type.
491  */
492  ExecInitResultTypeTL(&limitstate->ps);
493 
494  limitstate->ps.resultopsset = true;
495  limitstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(limitstate),
496  &limitstate->ps.resultopsfixed);
497 
498  /*
499  * limit nodes do no projections, so initialize projection info for this
500  * node appropriately
501  */
502  limitstate->ps.ps_ProjInfo = NULL;
503 
504  /*
505  * Initialize the equality evaluation, to detect ties.
506  */
507  if (node->limitOption == LIMIT_OPTION_WITH_TIES)
508  {
509  TupleDesc desc;
510  const TupleTableSlotOps *ops;
511 
512  desc = ExecGetResultType(outerPlanState(limitstate));
513  ops = ExecGetResultSlotOps(outerPlanState(limitstate), NULL);
514 
515  limitstate->last_slot = ExecInitExtraTupleSlot(estate, desc, ops);
516  limitstate->eqfunction = execTuplesMatchPrepare(desc,
517  node->uniqNumCols,
518  node->uniqColIdx,
519  node->uniqOperators,
520  node->uniqCollations,
521  &limitstate->ps);
522  }
523 
524  return limitstate;
525 }
static TupleTableSlot * ExecLimit(PlanState *pstate)
Definition: nodeLimit.c:41
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1801
Oid * uniqOperators
Definition: plannodes.h:988
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:985
const TupleTableSlotOps * ExecGetResultSlotOps(PlanState *planstate, bool *isfixed)
Definition: execUtils.c:504
EState * state
Definition: execnodes.h:947
LimitStateCond lstate
Definition: execnodes.h:2479
LimitOption limitOption
Definition: execnodes.h:2475
ExprState * limitCount
Definition: execnodes.h:2474
Node * limitOffset
Definition: plannodes.h:983
ExprState * eqfunction
Definition: execnodes.h:2482
const TupleTableSlotOps * resultops
Definition: execnodes.h:1020
Oid * uniqCollations
Definition: plannodes.h:989
LimitOption limitOption
Definition: plannodes.h:985
Node * limitCount
Definition: plannodes.h:984
#define outerPlanState(node)
Definition: execnodes.h:1039
void ExecInitResultTypeTL(PlanState *planstate)
Definition: execTuples.c:1725
ExprState * limitOffset
Definition: execnodes.h:2473
bool resultopsset
Definition: execnodes.h:1028
#define outerPlan(node)
Definition: plannodes.h:172
int uniqNumCols
Definition: plannodes.h:986
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:951
TupleTableSlot * last_slot
Definition: execnodes.h:2484
Plan * plan
Definition: execnodes.h:945
#define makeNode(_type_)
Definition: nodes.h:577
#define Assert(condition)
Definition: c.h:738
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
Definition: execGrouping.c:59
#define EXEC_FLAG_MARK
Definition: executor.h:59
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:485
TupleDesc ExecGetResultType(PlanState *planstate)
Definition: execUtils.c:495
bool resultopsfixed
Definition: execnodes.h:1024
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:123
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:139
PlanState ps
Definition: execnodes.h:2472
AttrNumber * uniqColIdx
Definition: plannodes.h:987

◆ ExecLimit()

static TupleTableSlot* ExecLimit ( PlanState pstate)
static

Definition at line 41 of file nodeLimit.c.

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().

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

◆ ExecReScanLimit()

void ExecReScanLimit ( LimitState node)

Definition at line 543 of file nodeLimit.c.

References PlanState::chgParam, ExecReScan(), PlanState::lefttree, LimitState::ps, and recompute_limits().

Referenced by ExecReScan().

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 (node->ps.lefttree->chgParam == NULL)
557  ExecReScan(node->ps.lefttree);
558 }
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
struct PlanState * lefttree
Definition: execnodes.h:967
Bitmapset * chgParam
Definition: execnodes.h:977
static void recompute_limits(LimitState *node)
Definition: nodeLimit.c:354
PlanState ps
Definition: execnodes.h:2472

◆ recompute_limits()

static void recompute_limits ( LimitState node)
static

Definition at line 354 of file nodeLimit.c.

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().

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