aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeLimit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeLimit.c')
-rw-r--r--src/backend/executor/nodeLimit.c324
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);
+}