1/*-------------------------------------------------------------------------
4 * Routines to handle limiting of query results where appropriate
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeLimit.c
13 *-------------------------------------------------------------------------
17 * ExecLimit - extract a limited range of tuples
18 * ExecInitLimit - initialize node and subnodes..
19 * ExecEndLimit - shutdown node and subnodes
32/* ----------------------------------------------------------------
35 * This is a very simple node which just performs LIMIT/OFFSET
36 * filtering on the stream of tuples returned by a subplan.
37 * ----------------------------------------------------------------
51 * get information from the node
57 * The main logic is a simple state machine.
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.
76 * If backwards scan, just return NULL without changing state.
82 * Check for empty window; if so, treat like empty subplan.
91 * Fetch rows from subplan until we reach position > offset.
99 * The subplan returns too few tuples for us to produce
107 * Tuple at limit is needed for comparison in subsequent
108 * execution to detect ties.
121 * Okay, we have the first tuple of the window.
129 * The subplan is known to return no tuples (or not more than
130 * OFFSET tuples, in general). So we return no tuples.
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
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.
164 /* we'll fall through to the next case */
170 * Get next tuple from subplan, if any.
180 * If WITH TIES is active, and this is the last in-window
181 * tuple, save it to be used in subsequent WINDOWEND_TIES
197 * Backwards scan, so check for stepping off start of window.
198 * As above, only change state-machine status if so.
207 * Get previous tuple from subplan; there should be one!
211 elog(
ERROR,
"LIMIT subplan failed to run backwards");
224 * Advance the subplan until we find the first row with
225 * different ORDER BY pathkeys.
235 * Test if the new tuple and the last tuple match. If so we
254 * Backwards scan, so check for stepping off start of window.
255 * Change only state-machine status if so.
264 * Get previous tuple from subplan; there should be one! And
265 * change state-machine status.
269 elog(
ERROR,
"LIMIT subplan failed to run backwards");
281 * Backing up from subplan EOF, so re-fetch previous tuple; there
282 * should be one! Note previous tuple must be in window.
286 elog(
ERROR,
"LIMIT subplan failed to run backwards");
289 /* position does not change 'cause we didn't advance it before */
297 * We already past one position to detect ties so re-fetch
298 * previous tuple; there should be one! Note previous tuple must
305 elog(
ERROR,
"LIMIT subplan failed to run backwards");
312 * Backing up from window end: simply re-return the last tuple
313 * fetched from the subplan.
317 /* position does not change 'cause we didn't advance it before */
326 * Advancing after having backed off window start: simply
327 * re-return the last tuple fetched from the subplan.
331 /* position does not change 'cause we didn't change it before */
335 elog(
ERROR,
"impossible LIMIT state: %d",
337 slot = NULL;
/* keep compiler quiet */
341 /* Return the current tuple */
348 * Evaluate the limit/offset expressions --- done at startup or rescan.
350 * This is also a handy place to reset the current-position state info.
364 /* Interpret NULL offset as no offset */
372 (
errcode(ERRCODE_INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE),
373 errmsg(
"OFFSET must not be negative")));
378 /* No OFFSET supplied */
387 /* Interpret NULL count as no count (LIMIT ALL) */
398 (
errcode(ERRCODE_INVALID_ROW_COUNT_IN_LIMIT_CLAUSE),
399 errmsg(
"LIMIT must not be negative")));
405 /* No COUNT supplied */
410 /* Reset position to start-of-scan */
414 /* Set state-machine state */
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.
427 * Compute the maximum number of tuples needed to satisfy this Limit node.
428 * Return a negative value if there is not a determinable limit.
435 /* Note: if this overflows, we'll return a negative value, which is OK */
439/* ----------------------------------------------------------------
442 * This initializes the limit node state structures and
443 * the node's subplan.
444 * ----------------------------------------------------------------
452 /* check for unsupported flags */
456 * create state structure
466 * Miscellaneous initialization
468 * Limit nodes never call ExecQual or ExecProject, but they need an
469 * exprcontext anyway to evaluate the limit/offset parameters in.
474 * initialize outer plan
480 * initialize child expressions
489 * Initialize result type.
498 * limit nodes do no projections, so initialize projection info for this
504 * Initialize the equality evaluation, to detect ties.
519 node->uniqCollations,
526/* ----------------------------------------------------------------
529 * This shuts down the subplan and frees resources allocated
531 * ----------------------------------------------------------------
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.
553 * if chgParam of subnode is not null then plan will be re-scanned by
554 * first ExecProcNode.
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
void ExecReScan(PlanState *node)
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
ExprState * execTuplesMatchPrepare(TupleDesc desc, int numCols, const AttrNumber *keyColIdx, const Oid *eqOperators, const Oid *collations, PlanState *parent)
void ExecSetTupleBound(int64 tuples_needed, PlanState *child_node)
void ExecEndNode(PlanState *node)
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
void ExecInitResultTypeTL(PlanState *planstate)
TupleTableSlot * ExecInitExtraTupleSlot(EState *estate, TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
TupleDesc ExecGetResultType(PlanState *planstate)
void ExecAssignExprContext(EState *estate, PlanState *planstate)
const TupleTableSlotOps * ExecGetResultSlotOps(PlanState *planstate, bool *isfixed)
#define outerPlanState(node)
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
static TupleTableSlot * ExecProcNode(PlanState *node)
static Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull)
Assert(PointerIsAligned(start, uint64))
#define CHECK_FOR_INTERRUPTS()
void ExecReScanLimit(LimitState *node)
LimitState * ExecInitLimit(Limit *node, EState *estate, int eflags)
static int64 compute_tuples_needed(LimitState *node)
static void recompute_limits(LimitState *node)
void ExecEndLimit(LimitState *node)
static TupleTableSlot * ExecLimit(PlanState *pstate)
#define castNode(_type_, nodeptr)
static int64 DatumGetInt64(Datum X)
#define ScanDirectionIsForward(direction)
ScanDirection es_direction
TupleTableSlot * ecxt_innertuple
TupleTableSlot * ecxt_outertuple
TupleTableSlot * last_slot
const TupleTableSlotOps * resultops
ExprContext * ps_ExprContext
ProjectionInfo * ps_ProjInfo
ExecProcNodeMtd ExecProcNode
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)