引言
这篇文章我将继续介绍表达式计算的相关内容,主要介绍ExecEvalFunc函数、ExecMakeFunctionResultNoSets函数和ExecTargetList函数。
函数所在源文件为src\gausskernel\runtime\executor\execQual.cpp
.
函数分析
执行阶段
ExecEvalFunc
函数源码
//代码清单1
static Datum ExecEvalFunc(FuncExprState* fcache, ExprContext* econtext, bool* isNull, ExprDoneCond* isDone)
{
/* This is called only the first time through */
FuncExpr* func = (FuncExpr*)fcache->xprstate.expr;
Oid target_type = InvalidOid;
Oid source_type = InvalidOid;
/* Initialize function lookup info */
init_fcache<false>(func->funcid, func->inputcollid, fcache, econtext->ecxt_per_query_memory, true);
bool has_refcursor = func_has_refcursor_args(func->funcid, &fcache->fcinfo_data);
int cursor_return_number = fcache->fcinfo_data.refcursor_data.return_number;
if (func->funcformat == COERCE_EXPLICIT_CAST || func->funcformat == COERCE_IMPLICIT_CAST) {
target_type = func->funcresulttype;
source_type = fcache->fcinfo_data.argTypes[0];
HeapTuple proc_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(func->funcid), 0, 0, 0);
if (HeapTupleIsValid(proc_tuple)) {
Form_pg_proc proc_struct = (Form_pg_proc)GETSTRUCT(proc_tuple);
source_type = proc_struct->proargtypes.values[0];
ReleaseSysCache(proc_tuple);
}
HeapTuple cast_tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(source_type),
ObjectIdGetDatum(target_type));
if (HeapTupleIsValid(cast_tuple)) {
Relation cast_rel = heap_open(CastRelationId, AccessShareLock);
int castowner_Anum = Anum_pg_cast_castowner;
if (castowner_Anum <= (int)HeapTupleHeaderGetNatts(cast_tuple->t_data, cast_rel->rd_att)) {
bool isnull = true;
Datum datum = fastgetattr(cast_tuple, Anum_pg_cast_castowner, cast_rel->rd_att, &isnull);
if (!isnull) {
u_sess->exec_cxt.cast_owner = DatumGetObjectId(datum);
} else {
u_sess->exec_cxt.cast_owner = InvalidCastOwnerId;
}
}
heap_close(cast_rel, AccessShareLock);
ReleaseSysCache(cast_tuple);
}
}
/*
* We need to invoke ExecMakeFunctionResult if either the function itself
* or any of its input expressions can return a set. Otherwise, invoke
* ExecMakeFunctionResultNoSets. In either case, change the evalfunc
* pointer to go directly there on subsequent uses.
*/
if (fcache->func.fn_retset) {
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, true>;
return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, true>;
return ExecMakeFunctionResult<true, false, true>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, true>;
return ExecMakeFunctionResult<false, true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, true>;
return ExecMakeFunctionResult<false, false, true>(fcache, econtext, isNull, isDone);
}
}
} else if (expression_returns_set((Node*)func->args)) {
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, true, false>;
return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<true, false, false>;
return ExecMakeFunctionResult<true, false, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, true, false>;
return ExecMakeFunctionResult<false, true, false>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResult<false, false, false>;
return ExecMakeFunctionResult<false, false, false>(fcache, econtext, isNull, isDone);
}
}
} else {
if (has_refcursor) {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, true>;
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<true, false>;
return ExecMakeFunctionResultNoSets<true, false>(fcache, econtext, isNull, isDone);
}
} else {
if (cursor_return_number > 0) {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, true>;
return ExecMakeFunctionResultNoSets<false, true>(fcache, econtext, isNull, isDone);
} else {
fcache->xprstate.evalfunc = (ExprStateEvalFunc)ExecMakeFunctionResultNoSets<false, false>;
return ExecMakeFunctionResultNoSets<false, false>(fcache, econtext, isNull, isDone);
}
}
}
}
执行过程
函数的执行过程较为简单,下面简要介绍:
- 首先初始化fcache,包括初始化参数,内存上下文管理等,并且在系统缓存里读取元组并更新相关信息。
- 根据表达式结点的信息,调用ExecMakeFunctionResult或ExecMakeFunctionResultNoSets函数。
函数执行过程可以用一个流程图来简单表示:
ExecEvalOper函数与ExecEvalFunc函数功能类似,这里不再展开叙述。
ExecMakeFunctionResultNoSets和ExecMakeFunctionResult
ExecMakeFunctionResultNoSets和ExecMakeFunctionResult函数是表达式计算中的核心函数,两个函数不同的地方在于一个用于集合,一个用于单值,单值可以看做是集合的特殊情况。由于ExecMakeFunctionResultNoSets和ExecMakeFunctionResult函数类似,为了不冗余,这里只介绍前者的作用。
- 遍历表达式参数,调用ExecEvalExpr宏函数获取参数值。
- 判断参数是否为空,若为空则返回空,否则调用FunctionCallInvoke宏函数计算表达式结果。
ExecTargetList
返回值
函数的返回值为bool类型的值,当返回true时代表表达式还未执行完,返回false时代表表达式全部执行完。
执行流程
ExecTargetList函数的作用是对传入参数tartgetlist里面的每个表达式计算并将结果存储到元组中。
主要的执行流程如下:
- 遍历targetlist列表中的每个表达式,并使用ExecEvalExpr宏函数计算每个表达式的结果,把结果储存在values数组中。
- 判断每个表达式是否返回集合,如果返回单值则另isDone为ExprEndResult;如果返回集合则判断表达式产生的此集合是否已经完备,若不完备则再次调用ExecEvalExpr宏函数直到完备为止。
小结
这几篇文章我简要解析了表达式计算的几个步骤,表达式计算和执行算子有着密不可分的联系,算子执行过程中需要表达式的值进行元组过滤,以减少内存开销。总的来说,执行算子离不开表达式,表达式服务于执行算子。