版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
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);
/* ----------------------------------------------------------------
* YUSUR CODE START: start acceleration.
* ----------------------------------------------------------------
*/
if (DBLIB_ENABLED)
{
/*
* we execute join only when it's the first loop, so this number
* indicates the number of loop.
*/
static int dblib_nestloop_num = 0;//第几次调用呢?这里的static的意思是函数结束时候,这个变量保持原来的值,这样第二次第三次仍然可以使用这个变量原来的值
static TupleTableSlot *dblib_result_tupleslot = NULL;//这个就是一个指针呗,留着指向结果使用
/*
* We don't accelerate join including index scan.
*/
if (outerPlan->type != T_IndexScanState && innerPlan->type != T_IndexScanState)//没有索引的时候我们才加速哦
{
/*
* Iidentifying the result slots and 'join on' param slots.
* now we only support one column data per table.
* We new result Datum **, allocate them into dblib_result_tupleslot.
*/
static int *dblib_output_qual_index = NULL;//盛放一对一对的index couple哦,注意static的作用
static int dblib_output_index_col = 0;//有多少组呢?
static Datum *dblib_outer_output_datums = NULL;//输入的join的Datum数组,我们现在只支持一个join 条件
static bool *dblib_outer_output_tts_isnull = NULL;//这个数组是否有些值是空的呢
static Datum *dblib_inner_output_datums = NULL;//输入的join的Datum数组,我们现在只支持一个join 条件
static bool *dblib_inner_output_tts_isnull = NULL;//这个数组是否有些值是空的呢
/*result related to inner tuple. The dblib_result_attnum is the index in result tuple,
* and the dblib_inner_var_attnum is the index in inner tuple*/
static int *dblib_innerresult_attnum = NULL;//dblib_innerresult_attnum[]是一个数组,里面有DBLIB_ATTR_NUM(4)个int数字
static int *dblib_outerresult_attnum = NULL;//dblib_outerresult_attnum[]是一个数组,里面有DBLIB_ATTR_NUM(4)个int数字
static int *dblib_inner_var_attnum = NULL;//
static int *dblib_outer_var_attnum = NULL;//
static int dblib_resultattr_valid_num = 0, dblib_innerattr_valid_num = 0,
dblib_outerattr_valid_num = 0;//这个变量在结果元组中的index,在内层的index,在外层的index
static int dblib_outer_col = 0, dblib_inner_col = 0; //这个简单,就是内层和外层分别有多少个呢??
/*
* Identifying the input slots for KPU kernel. now we only support 1 join condition.
*/
TupleTableSlot *dblib_temp_tupleslot;//这个作为函数内的临时变量
Datum *dblib_input_inner = NULL;//这个作为函数内的临时变量
Datum *dblib_input_outer = NULL;//这个作为函数内的临时变量
int dblib_i = 0;
int dblib_inner_fetch_attnum, dblib_outer_fetch_attnum, dblib_inner_join_attnum,
dblib_outer_join_attnum;//小于这个的值都会被解析
unsigned int dblib_join_funcid;
/*
* If it's the first loop.
*/
if (dblib_nestloop_num == 0) {
/*
* Allocate necessary memory
*/
dblib_input_inner = (Datum *)malloc(DBLIB_INPUTSLOTS_NUM * sizeof(Datum));//把临时变量搞成数组
dblib_input_outer = (Datum *)malloc(DBLIB_INPUTSLOTS_NUM * sizeof(Datum));//把临时变量搞成数组
dblib_output_qual_index = (int *)malloc(2 * DBLIB_OUTPUTSLOTS_NUM * sizeof(int));//把临时变量搞成数组
dblib_innerresult_attnum = (int *)malloc(DBLIB_ATTR_NUM * sizeof(int));//只有四个数的数组
dblib_outerresult_attnum = (int *)malloc(DBLIB_ATTR_NUM * sizeof(int));//只有四个数的数组
dblib_inner_var_attnum = (int *)malloc(DBLIB_ATTR_NUM * sizeof(int));//只有四个数的数组
dblib_outer_var_attnum = (int *)malloc(DBLIB_ATTR_NUM * sizeof(int));//只有四个数的数组
/*
* extract join information from joinqual
*/
for (; dblib_i < joinqual->steps_len; dblib_i++)
{
switch ((joinqual->steps + dblib_i)->opcode)
{
case EEOP_INNER_FETCHSOME:
dblib_inner_fetch_attnum = (joinqual->steps + dblib_i)->d.fetch.last_var;//意思是从内层中取出那些个数的数据呢?
break;
case EEOP_OUTER_FETCHSOME:
dblib_outer_fetch_attnum = (joinqual->steps + dblib_i)->d.fetch.last_var;//意思是从外层中取出那些个数的数据呢?
break;
case EEOP_INNER_VAR:
dblib_inner_join_attnum = (joinqual->steps + dblib_i)->d.var.attnum;//连接的属性
break;
case EEOP_OUTER_VAR:
dblib_outer_join_attnum = (joinqual->steps + dblib_i)->d.var.attnum;//连接的属性
break;
case EEOP_FUNCEXPR_STRICT:
dblib_join_funcid = (joinqual->steps + dblib_i)->d.func.finfo->fn_oid;//连接运算
break;
default:
;
}
}
/*
* extract result information from ps_ProjInfo
*/
ExprState *dblib_pi_state = &(node->js.ps.ps_ProjInfo->pi_state);
for (dblib_i = 0; dblib_i < dblib_pi_state->steps_len; dblib_i++)
{
switch ((dblib_pi_state->steps + dblib_i)->opcode)
{
case EEOP_INNER_FETCHSOME:
if (dblib_inner_fetch_attnum < (dblib_pi_state->steps + dblib_i)->d.fetch.last_var)
dblib_inner_fetch_attnum = (dblib_pi_state->steps + dblib_i)->d.fetch.last_var;//哦,这下明白了
break;
case EEOP_OUTER_FETCHSOME:
if (dblib_outer_fetch_attnum < (dblib_pi_state->steps + dblib_i)->d.fetch.last_var)
dblib_outer_fetch_attnum = (dblib_pi_state->steps + dblib_i)->d.fetch.last_var;//哦,这下明白了
break;
case EEOP_ASSIGN_INNER_VAR:
dblib_innerresult_attnum[dblib_innerattr_valid_num] =
(dblib_pi_state->steps + dblib_i)->d.assign_var.resultnum;
dblib_inner_var_attnum[dblib_innerattr_valid_num] =
(dblib_pi_state->steps + dblib_i)->d.assign_var.attnum;
dblib_innerattr_valid_num++;
break;
case EEOP_ASSIGN_OUTER_VAR:
dblib_outerresult_attnum[dblib_outerattr_valid_num] =
(dblib_pi_state->steps + dblib_i)->d.assign_var.resultnum;
dblib_outer_var_attnum[dblib_outerattr_valid_num] =
(dblib_pi_state->steps + dblib_i)->d.assign_var.attnum;
dblib_outerattr_valid_num++;
break;
default:
;
}
}
/*
* initialize dblib_output_datums
*/
dblib_resultattr_valid_num = dblib_innerattr_valid_num + dblib_outerattr_valid_num;
if (dblib_resultattr_valid_num > 0) {
dblib_outer_output_datums = (Datum *)malloc(DBLIB_OUTPUTSLOTS_NUM *
dblib_outerattr_valid_num * sizeof(Datum));
dblib_outer_output_tts_isnull = (bool *)malloc(DBLIB_OUTPUTSLOTS_NUM *
dblib_outerattr_valid_num * sizeof(bool));
dblib_inner_output_datums = (Datum *)malloc(DBLIB_OUTPUTSLOTS_NUM *
dblib_innerattr_valid_num * sizeof(Datum));
dblib_inner_output_tts_isnull = (bool *)malloc(DBLIB_OUTPUTSLOTS_NUM *
dblib_innerattr_valid_num * sizeof(bool));
}
/*
* getting outer tuples and result slots related to outer tuples
*/
dblib_i = 0;
ENL1_printf("DBLIB getting outer tuples");
dblib_temp_tupleslot = ExecProcNode(outerPlan);
while (!(TupIsNull(dblib_temp_tupleslot)) && (dblib_i < DBLIB_INPUTSLOTS_NUM))
{
/* Assign outer slots inputs*/
//(joinqual->steps + 1)->d.fetch.last_var, joinqual->steps->d.var.attnum
slot_getsomeattrs(dblib_temp_tupleslot, dblib_outer_fetch_attnum);
/* Assign outer intputs*/
*(dblib_input_outer + dblib_i) = dblib_temp_tupleslot->tts_values[dblib_outer_join_attnum];
/* Assign output datum*/
for (int dblib_j = 0; dblib_j < dblib_outerattr_valid_num; dblib_j++)
{
int dblib_temp_index1 = dblib_i * dblib_outerattr_valid_num +
dblib_j;
int dblib_temp_index2 = dblib_outer_var_attnum[dblib_j];
dblib_outer_output_datums[dblib_temp_index1] =
dblib_temp_tupleslot->tts_values[dblib_temp_index2];
dblib_outer_output_tts_isnull[dblib_temp_index1] =
dblib_temp_tupleslot->tts_isnull[dblib_temp_index2];
}
dblib_temp_tupleslot = ExecProcNode(outerPlan);
dblib_i++;
}
dblib_outer_col = dblib_i;
/*
* getting inner tuples and result slots related to inner tuples
*/
dblib_i = 0;
ENL1_printf("DBLIB getting inner tuples");
dblib_temp_tupleslot = ExecProcNode(innerPlan);
while (!(TupIsNull(dblib_temp_tupleslot)) && (dblib_i < DBLIB_INPUTSLOTS_NUM))
{
/* Assign outer slots inputs */
//(joinqual->steps + 1)->d.fetch.last_var, joinqual->steps->d.var.attnum
slot_getsomeattrs(dblib_temp_tupleslot, dblib_inner_fetch_attnum);
/* Assign inner inputs*/
*(dblib_input_inner + dblib_i) = dblib_temp_tupleslot->tts_values[dblib_inner_join_attnum];
/* Assign output datum*/
for (int dblib_j = 0; dblib_j < dblib_innerattr_valid_num; dblib_j++)
{
int dblib_temp_index1 = dblib_i * dblib_innerattr_valid_num +
dblib_j;
int dblib_temp_index2 = dblib_inner_var_attnum[dblib_j];
dblib_inner_output_datums[dblib_temp_index1] =
dblib_temp_tupleslot->tts_values[dblib_temp_index2];
dblib_inner_output_tts_isnull[dblib_temp_index1] =
dblib_temp_tupleslot->tts_isnull[dblib_temp_index2];
}
dblib_temp_tupleslot = ExecProcNode(innerPlan);
dblib_i++;
}
dblib_inner_col = dblib_i;
/*
* execute join and calculate result slots
*/
dblib_output_index_col = DblibNestLoopJoin(dblib_output_qual_index, dblib_input_outer,
dblib_input_inner, dblib_outer_col, dblib_inner_col, dblib_join_funcid);
/*
* Mark the end position of qual_index
*/
dblib_output_qual_index[dblib_output_index_col * 2] = -1;
dblib_output_qual_index[dblib_output_index_col * 2 + 1] = -1;
/*
* We need to use ExecProject to initialize some status of node,
* otherwise, it's forbidden to return dblib_result_tupleslot;
*/
outerTupleSlot = ExecProcNode(outerPlan);
innerTupleSlot = ExecProcNode(innerPlan);
while (TupIsNull(innerTupleSlot) || TupIsNull(outerTupleSlot))
{
ENL1_printf("rescanning inner plan");
ExecReScan(outerPlan);
outerTupleSlot = ExecProcNode(outerPlan);
ExecReScan(innerPlan);
innerTupleSlot = ExecProcNode(innerPlan);
}
econtext->ecxt_outertuple = outerTupleSlot;
econtext->ecxt_innertuple = innerTupleSlot;
dblib_result_tupleslot = ExecProject(node->js.ps.ps_ProjInfo);
}
/*
* return one tuple each time
*/
for (dblib_i = 0; dblib_i < dblib_output_index_col; dblib_i++)
{
int dblib_temp_i = dblib_i * 2;
int dblib_outer_valid_index = dblib_output_qual_index[dblib_temp_i];
int dblib_inner_valid_index = dblib_output_qual_index[dblib_temp_i + 1];
if (dblib_outer_valid_index > -1 &&
dblib_inner_valid_index > -1)
{
node->nl_MatchedOuter = true;
node->nl_NeedNewOuter = true;
dblib_result_tupleslot = node->js.ps.ps_ProjInfo->pi_state.resultslot;
ExecClearTuple(dblib_result_tupleslot);
dblib_result_tupleslot->tts_isempty = false;
dblib_result_tupleslot->tts_nvalid =
dblib_result_tupleslot->tts_tupleDescriptor->natts;
/*
* Assign outer output datums
*/
for (int dblib_j = 0; dblib_j < dblib_outerattr_valid_num; dblib_j++)
{
int dblib_temp_index1 = dblib_outerresult_attnum[dblib_j];
int dblib_temp_index2 = dblib_outer_valid_index *
dblib_outerattr_valid_num + dblib_j;
dblib_result_tupleslot->tts_values[dblib_temp_index1] =
dblib_outer_output_datums[dblib_temp_index2];
dblib_result_tupleslot->tts_isnull[dblib_temp_index1] =
dblib_outer_output_tts_isnull[dblib_temp_index2];
}
/*
* Assign inner output datums
*/
for (int dblib_j = 0; dblib_j < dblib_innerattr_valid_num; dblib_j++)
{
int dblib_temp_index1 = dblib_innerresult_attnum[dblib_j];
int dblib_temp_index2 = dblib_inner_valid_index *
dblib_innerattr_valid_num + dblib_j;
dblib_result_tupleslot->tts_values[dblib_temp_index1] =
dblib_inner_output_datums[dblib_temp_index2];
dblib_result_tupleslot->tts_isnull[dblib_temp_index1] =
dblib_inner_output_tts_isnull[dblib_temp_index2];
}
dblib_output_qual_index[dblib_temp_i] = -1;
dblib_output_qual_index[dblib_temp_i + 1] = -1;
dblib_nestloop_num++;
return dblib_result_tupleslot;
}
}
dblib_nestloop_num = 0;
dblib_resultattr_valid_num = 0;
dblib_innerattr_valid_num = 0;
dblib_outerattr_valid_num = 0;
dblib_output_index_col = 0;
dblib_outer_col = 0;
dblib_inner_col = 0;
free(dblib_input_inner);
free(dblib_input_outer);
free(dblib_innerresult_attnum);
free(dblib_outerresult_attnum);
free(dblib_inner_var_attnum);
free(dblib_outer_var_attnum);
free(dblib_outer_output_datums);
free(dblib_outer_output_tts_isnull);
free(dblib_inner_output_datums);
free(dblib_inner_output_tts_isnull);
free(dblib_output_qual_index);
dblib_innerresult_attnum = NULL;
dblib_outerresult_attnum = NULL;
dblib_inner_var_attnum = NULL;
dblib_outer_var_attnum = NULL;
dblib_outer_output_datums = NULL;
dblib_outer_output_tts_isnull = NULL;
dblib_inner_output_datums = NULL;
dblib_inner_output_tts_isnull = NULL;
dblib_output_qual_index = NULL;
/* If all valid result slots are returned,
* return NULL.
*/
return NULL;
}
}
/* ----------------------------------------------------------------
* YUSUR CODE END
* ----------------------------------------------------------------
*/
/*
* 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");
}
}