first commit for openGauss server

This commit is contained in:
lishifu
2020-06-30 17:38:27 +08:00
parent bcb52078d5
commit 815a9771fb
7975 changed files with 9646516 additions and 61 deletions

View File

@ -0,0 +1,469 @@
/* -------------------------------------------------------------------------
*
* nodeNestloop.cpp
* routines to support nest-loop joins
*
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/nodeNestloop.cpp
*
* -------------------------------------------------------------------------
*
* INTERFACE ROUTINES
* ExecNestLoop - process a nestloop join of two plans
* ExecInitNestLoop - initialize the join
* ExecEndNestLoop - shut down the join
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "executor/execdebug.h"
#include "executor/nodeNestloop.h"
#include "executor/execStream.h"
#include "utils/memutils.h"
static void MaterialAll(PlanState* node)
{
if (IsA(node, MaterialState)) {
((MaterialState*)node)->materalAll = true;
/*
* Call ExecProcNode to material all the inner tuple first.
*/
ExecProcNode(node);
/* early free the left tree of Material. */
ExecEarlyFree(outerPlanState(node));
/* early deinit consumer in left tree of Material. */
ExecEarlyDeinitConsumer(node);
}
}
/* ----------------------------------------------------------------
* 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.
* ----------------------------------------------------------------
*/
TupleTableSlot* ExecNestLoop(NestLoopState* node)
{
TupleTableSlot* outer_tuple_slot = NULL;
TupleTableSlot* inner_tuple_slot = NULL;
ListCell* lc = NULL;
/*
* get information from the node
*/
ENL1_printf("getting info from node");
NestLoop* nl = (NestLoop*)node->js.ps.plan;
List* joinqual = node->js.joinqual;
List* otherqual = node->js.ps.qual;
PlanState* outer_plan = outerPlanState(node);
PlanState* inner_plan = innerPlanState(node);
ExprContext* econtext = node->js.ps.ps_ExprContext;
/*
* Check to see if we're still projecting out tuples from a previous join
* tuple (because there is a function-returning-set in the projection
* expressions). If so, try to project another one.
*/
if (node->js.ps.ps_TupFromTlist) {
ExprDoneCond is_done;
TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done);
if (is_done == ExprMultipleResult)
return result;
/* Done with that source tuple... */
node->js.ps.ps_TupFromTlist = false;
}
/*
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't happen
* until we're done projecting out tuples from a join tuple.
*/
ResetExprContext(econtext);
/*
* Ok, everything is setup for the join so now loop until we return a
* qualifying join tuple.
*/
ENL1_printf("entering main loop");
if (node->nl_MaterialAll) {
MaterialAll(inner_plan);
node->nl_MaterialAll = false;
}
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");
outer_tuple_slot = ExecProcNode(outer_plan);
/*
* if there are no more outer tuples, then the join is complete..
*/
if (TupIsNull(outer_tuple_slot)) {
ExecEarlyFree(inner_plan);
ExecEarlyFree(outer_plan);
EARLY_FREE_LOG(elog(LOG,
"Early Free: NestLoop is done "
"at node %d, memory used %d MB.",
(node->js.ps.plan)->plan_node_id,
getSessionMemoryUsageMB()));
ENL1_printf("no outer tuple, ending join");
return NULL;
}
ENL1_printf("saving new outer tuple information");
econtext->ecxt_outertuple = outer_tuple_slot;
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 = NULL;
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(outer_tuple_slot, nlp->paramval->varattno, &(prm->isnull));
/*
* the following two parameters are called when there exist
* join-operation with column table (see ExecEvalVecParamExec).
*/
prm->valueType = outer_tuple_slot->tts_tupleDescriptor->tdtypeid;
prm->isChanged = true;
/* Flag parameter value as changed */
inner_plan->chgParam = bms_add_member(inner_plan->chgParam, paramno);
}
/*
* now rescan the inner plan
*/
ENL1_printf("rescanning inner plan");
ExecReScan(inner_plan);
}
/*
* we have an outerTuple, try to get the next inner tuple.
*/
ENL1_printf("getting new inner tuple");
/*
* If inner plan is mergejoin, which does not cache data,
* but will early free the left and right tree's caching memory.
* When rescan left tree, may fail.
*/
bool orig_value = inner_plan->state->es_skip_early_free;
if (!IsA(inner_plan, MaterialState))
inner_plan->state->es_skip_early_free = true;
inner_tuple_slot = ExecProcNode(inner_plan);
inner_plan->state->es_skip_early_free = orig_value;
econtext->ecxt_innertuple = inner_tuple_slot;
if (TupIsNull(inner_tuple_slot)) {
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 ||
node->js.jointype == JOIN_LEFT_ANTI_FULL)) {
/*
* 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 == NIL || ExecQual(otherqual, econtext, false)) {
/*
* qualification was satisfied so we project and return
* the slot containing the result tuple using
* function ExecProject.
*/
ExprDoneCond is_done;
ENL1_printf("qualification succeeded, projecting tuple");
TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done);
if (is_done != ExprEndResult) {
node->js.ps.ps_TupFromTlist = (is_done == ExprMultipleResult);
return result;
}
} 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, false)) {
node->nl_MatchedOuter = true;
/* In an antijoin, we never return a matched tuple */
if (node->js.jointype == JOIN_ANTI || node->js.jointype == JOIN_LEFT_ANTI_FULL) {
node->nl_NeedNewOuter = true;
continue; /* return to top of loop */
}
/*
* In a semijoin, we'll consider returning the first match, but
* after that we're done with this outer tuple.
*/
if (node->js.jointype == JOIN_SEMI)
node->nl_NeedNewOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false)) {
/*
* qualification was satisfied so we project and return the
* slot containing the result tuple using ExecProject().
*/
ExprDoneCond is_done;
ENL1_printf("qualification succeeded, projecting tuple");
TupleTableSlot* result = ExecProject(node->js.ps.ps_ProjInfo, &is_done);
if (is_done != ExprEndResult) {
node->js.ps.ps_TupFromTlist = (is_done == ExprMultipleResult);
/*
* @hdfs
* Optimize plan by informational constraint.
*/
if (((NestLoop*)(node->js.ps.plan))->join.optimizable) {
node->nl_NeedNewOuter = true;
}
return result;
}
} 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)
{
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
NL1_printf("ExecInitNestLoop: %s\n", "initializing node");
/*
* create state structure
*/
NestLoopState* nlstate = makeNode(NestLoopState);
nlstate->js.ps.plan = (Plan*)node;
nlstate->js.ps.state = estate;
nlstate->nl_MaterialAll = node->materialAll;
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &nlstate->js.ps);
/*
* initialize child expressions
*/
nlstate->js.ps.targetlist = (List*)ExecInitExpr((Expr*)node->join.plan.targetlist, (PlanState*)nlstate);
nlstate->js.ps.qual = (List*)ExecInitExpr((Expr*)node->join.plan.qual, (PlanState*)nlstate);
nlstate->js.jointype = node->join.jointype;
nlstate->js.joinqual = (List*)ExecInitExpr((Expr*)node->join.joinqual, (PlanState*)nlstate);
Assert(node->join.nulleqqual == NIL);
/*
* 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);
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &nlstate->js.ps);
switch (node->join.jointype) {
case JOIN_INNER:
case JOIN_SEMI:
break;
case JOIN_LEFT:
case JOIN_ANTI:
case JOIN_LEFT_ANTI_FULL:
nlstate->nl_NullInnerTupleSlot = ExecInitNullTupleSlot(estate, ExecGetResultType(innerPlanState(nlstate)));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmodule(MOD_EXECUTOR),
errmsg("unrecognized join type: %d when initializing nestLoop", (int)node->join.jointype)));
}
/*
* initialize tuple type and projection info
*/
ExecAssignResultTypeFromTL(&nlstate->js.ps);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
/*
* finally, wipe the current outer tuple clean.
*/
nlstate->js.ps.ps_TupFromTlist = false;
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
*/
(void)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* outer_plan = outerPlanState(node);
PlanState* inner_plan = innerPlanState(node);
PlanState ps = node->js.ps;
/*
* If outer_plan->chgParam is not null then plan will be automatically
* re-scanned by first ExecProcNode.
*/
if (outer_plan->chgParam == NULL)
ExecReScan(outer_plan);
/*
* Under recursive-stream condition, need to rescan
* both outer_plan and inner_plan.
*/
if (IS_PGXC_DATANODE && EXEC_IN_RECURSIVE_MODE(ps.plan) && ((ps.state)->es_recursive_next_iteration)) {
ExecReScan(inner_plan);
node->nl_MaterialAll = ((NestLoop*)ps.plan)->materialAll;
}
/*
* inner_plan 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->js.ps.ps_TupFromTlist = false;
node->nl_NeedNewOuter = true;
node->nl_MatchedOuter = false;
}