summaryrefslogtreecommitdiffstats
path: root/src/backend/executor/nodeNestloop.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/backend/executor/nodeNestloop.c
parentInitial commit. (diff)
downloadpostgresql-14-46651ce6fe013220ed397add242004d764fc0153.tar.xz
postgresql-14-46651ce6fe013220ed397add242004d764fc0153.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/backend/executor/nodeNestloop.c')
-rw-r--r--src/backend/executor/nodeNestloop.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
new file mode 100644
index 0000000..41e5eca
--- /dev/null
+++ b/src/backend/executor/nodeNestloop.c
@@ -0,0 +1,411 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNestloop.c
+ * routines to support nest-loop joins
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/executor/nodeNestloop.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ * ExecNestLoop - process a nestloop join of two plans
+ * ExecInitNestLoop - initialize the join
+ * ExecEndNestLoop - shut down the join
+ */
+
+#include "postgres.h"
+
+#include "executor/execdebug.h"
+#include "executor/nodeNestloop.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
+
+
+/* ----------------------------------------------------------------
+ * ExecNestLoop(node)
+ *
+ * old comments
+ * Returns the tuple joined from inner and outer tuples which
+ * satisfies the qualification clause.
+ *
+ * It scans the inner relation to join with current outer tuple.
+ *
+ * If none is found, next tuple from the outer relation is retrieved
+ * and the inner relation is scanned from the beginning again to join
+ * with the outer tuple.
+ *
+ * NULL is returned if all the remaining outer tuples are tried and
+ * all fail to join with the inner tuples.
+ *
+ * NULL is also returned if there is no tuple from inner relation.
+ *
+ * Conditions:
+ * -- outerTuple contains current tuple from outer relation and
+ * the right son(inner relation) maintains "cursor" at the tuple
+ * returned previously.
+ * This is achieved by maintaining a scan position on the outer
+ * relation.
+ *
+ * Initial States:
+ * -- the outer child and the inner child
+ * are prepared to return the first tuple.
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ExecNestLoop(PlanState *pstate)
+{
+ NestLoopState *node = castNode(NestLoopState, pstate);
+ NestLoop *nl;
+ PlanState *innerPlan;
+ PlanState *outerPlan;
+ TupleTableSlot *outerTupleSlot;
+ TupleTableSlot *innerTupleSlot;
+ ExprState *joinqual;
+ ExprState *otherqual;
+ ExprContext *econtext;
+ ListCell *lc;
+
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * get information from the node
+ */
+ ENL1_printf("getting info from node");
+
+ nl = (NestLoop *) node->js.ps.plan;
+ joinqual = node->js.joinqual;
+ otherqual = node->js.ps.qual;
+ outerPlan = outerPlanState(node);
+ innerPlan = innerPlanState(node);
+ econtext = node->js.ps.ps_ExprContext;
+
+ /*
+ * Reset per-tuple memory context to free any expression evaluation
+ * storage allocated in the previous tuple cycle.
+ */
+ ResetExprContext(econtext);
+
+ /*
+ * Ok, everything is setup for the join so now loop until we return a
+ * qualifying join tuple.
+ */
+ ENL1_printf("entering main loop");
+
+ for (;;)
+ {
+ /*
+ * If we don't have an outer tuple, get the next one and reset the
+ * inner scan.
+ */
+ if (node->nl_NeedNewOuter)
+ {
+ ENL1_printf("getting new outer tuple");
+ outerTupleSlot = ExecProcNode(outerPlan);
+
+ /*
+ * if there are no more outer tuples, then the join is complete..
+ */
+ if (TupIsNull(outerTupleSlot))
+ {
+ ENL1_printf("no outer tuple, ending join");
+ return NULL;
+ }
+
+ ENL1_printf("saving new outer tuple information");
+ econtext->ecxt_outertuple = outerTupleSlot;
+ node->nl_NeedNewOuter = false;
+ node->nl_MatchedOuter = false;
+
+ /*
+ * fetch the values of any outer Vars that must be passed to the
+ * inner scan, and store them in the appropriate PARAM_EXEC slots.
+ */
+ foreach(lc, nl->nestParams)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
+ int paramno = nlp->paramno;
+ ParamExecData *prm;
+
+ prm = &(econtext->ecxt_param_exec_vals[paramno]);
+ /* Param value should be an OUTER_VAR var */
+ Assert(IsA(nlp->paramval, Var));
+ Assert(nlp->paramval->varno == OUTER_VAR);
+ Assert(nlp->paramval->varattno > 0);
+ prm->value = slot_getattr(outerTupleSlot,
+ nlp->paramval->varattno,
+ &(prm->isnull));
+ /* Flag parameter value as changed */
+ innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
+ paramno);
+ }
+
+ /*
+ * now rescan the inner plan
+ */
+ ENL1_printf("rescanning inner plan");
+ ExecReScan(innerPlan);
+ }
+
+ /*
+ * we have an outerTuple, try to get the next inner tuple.
+ */
+ ENL1_printf("getting new inner tuple");
+
+ innerTupleSlot = ExecProcNode(innerPlan);
+ econtext->ecxt_innertuple = innerTupleSlot;
+
+ if (TupIsNull(innerTupleSlot))
+ {
+ ENL1_printf("no inner tuple, need new outer tuple");
+
+ node->nl_NeedNewOuter = true;
+
+ if (!node->nl_MatchedOuter &&
+ (node->js.jointype == JOIN_LEFT ||
+ node->js.jointype == JOIN_ANTI))
+ {
+ /*
+ * We are doing an outer join and there were no join matches
+ * for this outer tuple. Generate a fake join tuple with
+ * nulls for the inner tuple, and return it if it passes the
+ * non-join quals.
+ */
+ econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
+
+ ENL1_printf("testing qualification for outer-join tuple");
+
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
+ {
+ /*
+ * qualification was satisfied so we project and return
+ * the slot containing the result tuple using
+ * ExecProject().
+ */
+ ENL1_printf("qualification succeeded, projecting tuple");
+
+ return ExecProject(node->js.ps.ps_ProjInfo);
+ }
+ else
+ InstrCountFiltered2(node, 1);
+ }
+
+ /*
+ * Otherwise just return to top of loop for a new outer tuple.
+ */
+ continue;
+ }
+
+ /*
+ * at this point we have a new pair of inner and outer tuples so we
+ * test the inner and outer tuples to see if they satisfy the node's
+ * qualification.
+ *
+ * Only the joinquals determine MatchedOuter status, but all quals
+ * must pass to actually return the tuple.
+ */
+ ENL1_printf("testing qualification");
+
+ if (ExecQual(joinqual, econtext))
+ {
+ node->nl_MatchedOuter = true;
+
+ /* In an antijoin, we never return a matched tuple */
+ if (node->js.jointype == JOIN_ANTI)
+ {
+ node->nl_NeedNewOuter = true;
+ continue; /* return to top of loop */
+ }
+
+ /*
+ * If we only need to join to the first matching inner tuple, then
+ * consider returning this one, but after that continue with next
+ * outer tuple.
+ */
+ if (node->js.single_match)
+ node->nl_NeedNewOuter = true;
+
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
+ {
+ /*
+ * qualification was satisfied so we project and return the
+ * slot containing the result tuple using ExecProject().
+ */
+ ENL1_printf("qualification succeeded, projecting tuple");
+
+ return ExecProject(node->js.ps.ps_ProjInfo);
+ }
+ else
+ InstrCountFiltered2(node, 1);
+ }
+ else
+ InstrCountFiltered1(node, 1);
+
+ /*
+ * Tuple fails qual, so free per-tuple memory and try again.
+ */
+ ResetExprContext(econtext);
+
+ ENL1_printf("qualification failed, looping");
+ }
+}
+
+/* ----------------------------------------------------------------
+ * ExecInitNestLoop
+ * ----------------------------------------------------------------
+ */
+NestLoopState *
+ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
+{
+ NestLoopState *nlstate;
+
+ /* check for unsupported flags */
+ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+
+ NL1_printf("ExecInitNestLoop: %s\n",
+ "initializing node");
+
+ /*
+ * create state structure
+ */
+ nlstate = makeNode(NestLoopState);
+ nlstate->js.ps.plan = (Plan *) node;
+ nlstate->js.ps.state = estate;
+ nlstate->js.ps.ExecProcNode = ExecNestLoop;
+
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
+ */
+ ExecAssignExprContext(estate, &nlstate->js.ps);
+
+ /*
+ * initialize child nodes
+ *
+ * If we have no parameters to pass into the inner rel from the outer,
+ * tell the inner child that cheap rescans would be good. If we do have
+ * such parameters, then there is no point in REWIND support at all in the
+ * inner child, because it will always be rescanned with fresh parameter
+ * values.
+ */
+ outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate, eflags);
+ if (node->nestParams == NIL)
+ eflags |= EXEC_FLAG_REWIND;
+ else
+ eflags &= ~EXEC_FLAG_REWIND;
+ innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate, eflags);
+
+ /*
+ * Initialize result slot, type and projection.
+ */
+ ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
+ ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
+
+ /*
+ * initialize child expressions
+ */
+ nlstate->js.ps.qual =
+ ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
+ nlstate->js.jointype = node->join.jointype;
+ nlstate->js.joinqual =
+ ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
+
+ /*
+ * detect whether we need only consider the first matching inner tuple
+ */
+ nlstate->js.single_match = (node->join.inner_unique ||
+ node->join.jointype == JOIN_SEMI);
+
+ /* set up null tuples for outer joins, if needed */
+ switch (node->join.jointype)
+ {
+ case JOIN_INNER:
+ case JOIN_SEMI:
+ break;
+ case JOIN_LEFT:
+ case JOIN_ANTI:
+ nlstate->nl_NullInnerTupleSlot =
+ ExecInitNullTupleSlot(estate,
+ ExecGetResultType(innerPlanState(nlstate)),
+ &TTSOpsVirtual);
+ break;
+ default:
+ elog(ERROR, "unrecognized join type: %d",
+ (int) node->join.jointype);
+ }
+
+ /*
+ * finally, wipe the current outer tuple clean.
+ */
+ nlstate->nl_NeedNewOuter = true;
+ nlstate->nl_MatchedOuter = false;
+
+ NL1_printf("ExecInitNestLoop: %s\n",
+ "node initialized");
+
+ return nlstate;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndNestLoop
+ *
+ * closes down scans and frees allocated storage
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndNestLoop(NestLoopState *node)
+{
+ NL1_printf("ExecEndNestLoop: %s\n",
+ "ending node processing");
+
+ /*
+ * Free the exprcontext
+ */
+ ExecFreeExprContext(&node->js.ps);
+
+ /*
+ * clean out the tuple table
+ */
+ ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
+
+ /*
+ * close down subplans
+ */
+ ExecEndNode(outerPlanState(node));
+ ExecEndNode(innerPlanState(node));
+
+ NL1_printf("ExecEndNestLoop: %s\n",
+ "node processing ended");
+}
+
+/* ----------------------------------------------------------------
+ * ExecReScanNestLoop
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanNestLoop(NestLoopState *node)
+{
+ PlanState *outerPlan = outerPlanState(node);
+
+ /*
+ * If outerPlan->chgParam is not null then plan will be automatically
+ * re-scanned by first ExecProcNode.
+ */
+ if (outerPlan->chgParam == NULL)
+ ExecReScan(outerPlan);
+
+ /*
+ * innerPlan is re-scanned for each new outer tuple and MUST NOT be
+ * re-scanned from here or you'll get troubles from inner index scans when
+ * outer Vars are used as run-time keys...
+ */
+
+ node->nl_NeedNewOuter = true;
+ node->nl_MatchedOuter = false;
+}