diff options
Diffstat (limited to 'src/backend/executor/nodeLimit.c')
-rw-r--r-- | src/backend/executor/nodeLimit.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c new file mode 100644 index 00000000000..c7bc666c2f9 --- /dev/null +++ b/src/backend/executor/nodeLimit.c @@ -0,0 +1,324 @@ +/*------------------------------------------------------------------------- + * + * nodeLimit.c + * Routines to handle limiting of query results where appropriate + * + * Portions Copyright (c) 1996-2000, PostgreSQL, Inc + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.1 2000/10/26 21:35:15 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * ExecLimit - extract a limited range of tuples + * ExecInitLimit - initialize node and subnodes.. + * ExecEndLimit - shutdown node and subnodes + */ + +#include "postgres.h" + +#include "executor/executor.h" +#include "executor/nodeLimit.h" + +static void recompute_limits(Limit *node); + + +/* ---------------------------------------------------------------- + * ExecLimit + * + * This is a very simple node which just performs LIMIT/OFFSET + * filtering on the stream of tuples returned by a subplan. + * ---------------------------------------------------------------- + */ +TupleTableSlot * /* return: a tuple or NULL */ +ExecLimit(Limit *node) +{ + LimitState *limitstate; + ScanDirection direction; + TupleTableSlot *resultTupleSlot; + TupleTableSlot *slot; + Plan *outerPlan; + long netlimit; + + /* ---------------- + * get information from the node + * ---------------- + */ + limitstate = node->limitstate; + direction = node->plan.state->es_direction; + outerPlan = outerPlan((Plan *) node); + resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot; + + /* ---------------- + * If first call for this scan, compute limit/offset. + * (We can't do this any earlier, because parameters from upper nodes + * may not be set until now.) + * ---------------- + */ + if (! limitstate->parmsSet) + recompute_limits(node); + netlimit = limitstate->offset + limitstate->count; + + /* ---------------- + * now loop, returning only desired tuples. + * ---------------- + */ + for (;;) + { + /*---------------- + * If we have reached the subplan EOF or the limit, just quit. + * + * NOTE: when scanning forwards, we must fetch one tuple beyond the + * COUNT limit before we can return NULL, else the subplan won't be + * properly positioned to start going backwards. Hence test here + * is for position > netlimit not position >= netlimit. + * + * Similarly, when scanning backwards, we must re-fetch the last + * tuple in the offset region before we can return NULL. Otherwise + * we won't be correctly aligned to start going forward again. So, + * although you might think we can quit when position = offset + 1, + * we have to fetch a subplan tuple first, and then exit when + * position = offset. + *---------------- + */ + if (ScanDirectionIsForward(direction)) + { + if (limitstate->atEnd) + return NULL; + if (! limitstate->noCount && limitstate->position > netlimit) + return NULL; + } + else + { + if (limitstate->position <= limitstate->offset) + return NULL; + } + /* ---------------- + * fetch a tuple from the outer subplan + * ---------------- + */ + slot = ExecProcNode(outerPlan, (Plan *) node); + if (TupIsNull(slot)) + { + /* + * We are at start or end of the subplan. Update local state + * appropriately, but always return NULL. + */ + if (ScanDirectionIsForward(direction)) + { + Assert(! limitstate->atEnd); + /* must bump position to stay in sync for backwards fetch */ + limitstate->position++; + limitstate->atEnd = true; + } + else + { + limitstate->position = 0; + limitstate->atEnd = false; + } + return NULL; + } + /* + * We got the next subplan tuple successfully, so adjust state. + */ + if (ScanDirectionIsForward(direction)) + limitstate->position++; + else + { + limitstate->position--; + Assert(limitstate->position > 0); + } + limitstate->atEnd = false; + + /* ---------------- + * Now, is this a tuple we want? If not, loop around to fetch + * another tuple from the subplan. + * ---------------- + */ + if (limitstate->position > limitstate->offset && + (limitstate->noCount || limitstate->position <= netlimit)) + break; + } + + ExecStoreTuple(slot->val, + resultTupleSlot, + InvalidBuffer, + false); /* tuple does not belong to slot */ + + return resultTupleSlot; +} + +/* + * Evaluate the limit/offset expressions --- done at start of each scan. + * + * This is also a handy place to reset the current-position state info. + */ +static void +recompute_limits(Limit *node) +{ + LimitState *limitstate = node->limitstate; + ExprContext *econtext = limitstate->cstate.cs_ExprContext; + bool isNull; + + if (node->limitOffset) + { + limitstate->offset = DatumGetInt32(ExecEvalExpr(node->limitOffset, + econtext, + &isNull, + NULL)); + /* Interpret NULL offset as no offset */ + if (isNull) + limitstate->offset = 0; + else if (limitstate->offset < 0) + limitstate->offset = 0; + } + else + { + /* No OFFSET supplied */ + limitstate->offset = 0; + } + + if (node->limitCount) + { + limitstate->count = DatumGetInt32(ExecEvalExpr(node->limitCount, + econtext, + &isNull, + NULL)); + /* Interpret NULL count as no count */ + if (isNull) + limitstate->noCount = true; + else + { + /* Currently, LIMIT 0 is specified as meaning no limit. + * I think this is pretty bogus, but ... + */ + if (limitstate->count <= 0) + limitstate->noCount = true; + } + } + else + { + /* No COUNT supplied */ + limitstate->count = 0; + limitstate->noCount = true; + } + + /* Reset position data to start-of-scan */ + limitstate->position = 0; + limitstate->atEnd = false; + + /* Set flag that params are computed */ + limitstate->parmsSet = true; +} + +/* ---------------------------------------------------------------- + * ExecInitLimit + * + * This initializes the limit node state structures and + * the node's subplan. + * ---------------------------------------------------------------- + */ +bool /* return: initialization status */ +ExecInitLimit(Limit *node, EState *estate, Plan *parent) +{ + LimitState *limitstate; + Plan *outerPlan; + + /* ---------------- + * assign execution state to node + * ---------------- + */ + node->plan.state = estate; + + /* ---------------- + * create new LimitState for node + * ---------------- + */ + limitstate = makeNode(LimitState); + node->limitstate = limitstate; + limitstate->parmsSet = false; + + /* ---------------- + * Miscellaneous initialization + * + * Limit nodes never call ExecQual or ExecProject, but they need + * an exprcontext anyway to evaluate the limit/offset parameters in. + * ---------------- + */ + ExecAssignExprContext(estate, &limitstate->cstate); + +#define LIMIT_NSLOTS 1 + /* ------------ + * Tuple table initialization + * ------------ + */ + ExecInitResultTupleSlot(estate, &limitstate->cstate); + + /* ---------------- + * then initialize outer plan + * ---------------- + */ + outerPlan = outerPlan((Plan *) node); + ExecInitNode(outerPlan, estate, (Plan *) node); + + /* ---------------- + * limit nodes do no projections, so initialize + * projection info for this node appropriately + * ---------------- + */ + ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate); + limitstate->cstate.cs_ProjInfo = NULL; + + return TRUE; +} + +int +ExecCountSlotsLimit(Limit *node) +{ + return ExecCountSlotsNode(outerPlan(node)) + + ExecCountSlotsNode(innerPlan(node)) + + LIMIT_NSLOTS; +} + +/* ---------------------------------------------------------------- + * ExecEndLimit + * + * This shuts down the subplan and frees resources allocated + * to this node. + * ---------------------------------------------------------------- + */ +void +ExecEndLimit(Limit *node) +{ + LimitState *limitstate = node->limitstate; + + ExecFreeExprContext(&limitstate->cstate); + + ExecEndNode(outerPlan((Plan *) node), (Plan *) node); + + /* clean up tuple table */ + ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot); +} + + +void +ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent) +{ + LimitState *limitstate = node->limitstate; + + ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot); + + /* force recalculation of limit expressions on first call */ + limitstate->parmsSet = false; + + /* + * if chgParam of subnode is not null then plan will be re-scanned by + * first ExecProcNode. + */ + if (((Plan *) node)->lefttree->chgParam == NULL) + ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); +} |