ExecMakeFunctionResult函数和ExecMakeFunctionResultNoS函数是表达式计算的核心函数,主要作用是通过获取表达式的参数来计算出表达式结果。ExecMakeFunctionResultNoSets函数是ExecMakeFunctionResult函数的简化版,只能处理返回值是非集合情况。ExecMakeFunctionResult函数核心代码如下:
fcinfo = &fcache->fcinfo_data; /* 声明fcinfo */
InitFunctionCallInfoArgs(*fcinfo, list_length(fcache->args), 1); /*初始化fcinfo */
econtext->is_cursor = false;
foreach (arg, fcache->args) { /* 遍历获取参数值 */
ExprState* argstate = (ExprState*)lfirst(arg);
fcinfo->argTypes[i] = argstate->resultType;
fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], NULL);
if (fcache->func.fn_strict) /* 判断参数是否存在空值 */
……
result = FunctionCallInvoke(fcinfo); /* 计算表达式结果 */
return result;
ExecMakeFunctionResultNoSets函数的执行流程如下。
(1) 声明fcinfo来存储表达式需要的参数信息,通过InitFunctionCallInfoArgs函数初始化fcinfo中的字段。
(2) 遍历表达式中的参数args,通过ExecEvalExpr宏调用接口获取每一个参数的值,存储到“fcinfo->arg[i]”中。
(3) 根据func.fn_strict函数来判断是否需要检查参数空值情况。如果不需要检查,则通过“FunctionCalllv-oke”宏将参数传入表达式并计算出表达式的结果。否则进行判空处理,若存在空值则直接返回空,若不存在空值则通过FunctionCalllvoke宏计算表达式结果。
(4) 返回计算结果。
流程如图7-14所示。
图7-14 “ExecMakeFunctionResultNoSets”函数执行流程
ExecMakeFunctionResult函数的执行流程如图7-15所示。
(1) 判断funcResultStore是否存在,如果存在则从中获取结果返回(注:如果下文(3)中的模式是SFRM_Materialize,则会直接跳到此处)。
(2) 计算出参数值存入到fcinfo中。
(3) 把参数传入到表达式函数中计算表达式,首先判断参数args是否存在空,然后判断返回集合的函数的返回模式,SFRM_ValuePerCall模式是每次调用返回一个值,SFRM_Materialize模式是在Tuplestore中实例化的结果集。
(4) 根据不同的模式进行计算并返回结果。
图7-15 ExecMakeFunctionResult函数执行流程
ExecEvalFunc和ExecEvalOper这两个函数的功能类似。通过调用结果处理函数来获取结果。如果函数本身或者它的任何输入参数都可以返回一个集合,那么就会调ExecMakeFunctionResult函数来计算结果,否则调用ExecMakeFunctionResultNoSets函数来计算结果。核心代码如下:
init_fcache<false>(func->funcid,func->inputcollid,fcache, econtext->ecxt_per_query_memory, true); /* 初始化fcache */
if (fcache->func.fn_retset) { /* 判断返回结果类型 */
……
return ExecMakeFunctionResult<true, true, true>(fcache, econtext, isNull, isDone);
} else if (expression_returns_set((Node\*)func->args)) {
……
return ExecMakeFunctionResult<true, true, false>(fcache, econtext, isNull, isDone);
} else {
……
return ExecMakeFunctionResultNoSets<true, true>(fcache, econtext, isNull, isDone);
}
ExecEvalFunc函数的执行流程如下。
(1) 是通过init_fcache函数初始化FuncExprState节点,包括初始化参数、内存管理等等。
(2) 根据FuncExprState函数中的数据判断返回结果是否为set类型,并调用相应的函数计算结果。
ExecEvalFunc函数执行流程如图7-16所示。
图7-16 ExecEvalFunc函数执行流程
ExecQual函数的作用是检查slot结果是否满足表达式中的子表达式,如果子表达式为false,则返回false否则返回true,表示该结果符合预期,需要输出。核心代码如下:
foreach (l, qual) { /* 遍历qual中的子表达式并计算 */
expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
if (isNull) { /* 判断计算结果 */
if (resultForNull == false) {
result = false;
break;
}
} else {
if (!DatumGetBool(expr_value)) {
result = false;
……
return result; /* 返回结果是否满足表达式 */
ExecQual函数的主要执行流程如下。
(1) 遍历qual中的子表达式,根据ExecEvalExpr函数计算结果是否满足该子表达式,若满足则expr_value为1,否则为0。
(2) 判断结果是否为空,若为空,则根据resultForNull参数得到返回值信息。若不为空,则根据expr_value判断返回true或者false。
(3) 返回result。
ExecQual函数的执行流程如图7-17所示。
图7-17 ExecQual函数执行流程
ExecEvalOr函数的作用是计算通过or连接的bool表达式(布尔表达式,最终只有true(真)和false(假)两个取值),检查slot结果是否满足表达式中的or表达式。如果结果符合or表达式中的任何一个子表达式,则直接返回true,否则返回false。如果获取的结果为null,则记录isNull为true。核心代码如下:
foreach (clause, clauses) { /* 遍历子表达式 */
ExprState* clausestate = (ExprState*)lfirst(clause);
Datum clause_value;
clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); /* 执行表达式 */
/* 如果得到不空且ture的结果,直接返回结果 */
if (*isNull)
/* 记录存在空值 */
AnyNull = true;
else if (DatumGetBool(clause_value))
/* 一次结果为true就返回 */
return clause_value; /* 返回执行结果 */
}
*isNull = AnyNull;
return BoolGetDatum(false);
ExecEvalOr函数主要执行流程如下。
(1) 遍历子表达式clauses。
(2) 通过ExecEvalExpr函数来调用clause中的表达式计算函数,计算出结果。
(3) 对结果进行判断,or表达式中若有一个结果满足条件,就会跳出循环直接返回。
ExecEvalOr函数的执行流程如图7-18所示。
图7-18 ExecEvalOr函数执行流程
ExecTargetList函数的作用是根据给定的表达式上下文计算targetlist中的所有表达式,将计算结果存储到元组中。主要结构体代码如下:
typedef struct GenericExprState {
ExprState xprstate;
ExprState* arg; /*子节点的状态*/
} GenericExprState;
typedef struct TargetEntry {
Expr xpr;
Expr* expr; /*要