引言
从本篇文章开始,我将分析几个控制算子的功能和作用。控制算子主要用于执行特殊流程,这类流程通常包含多个输入,例如Union操作,需要把多个子结果合并成一个。接下来介绍Result算子。
代码位置
src\gausskernel\runtime\executor\nodeResult.cpp
功能作用
Result算子用于处理只有一个结果(例如SELECT 3*2或者INSERT语句只包含values子句并不包含列名)或者where表达式中的结果是常量(如 SELECT * FROM t WHERE 2>1,过滤表达式”2>1”是常量)的流程。
ExecResult
算子执行的关键函数为ExecResult,源码如下:
//代码清单1
TupleTableSlot* ExecResult(ResultState* node)
{
TupleTableSlot* outer_tuple_slot = NULL;
TupleTableSlot* result_slot = NULL;
PlanState* outer_plan = NULL;
ExprDoneCond is_done;
ExprContext* econtext = node->ps.ps_ExprContext;
/*如果没有做过常量表达式检查,则执行if语句计算常量表达式*/
if (node->rs_checkqual) {
bool qualResult = ExecQual((List*)node->resconstantqual, econtext, false);
node->rs_checkqual = false;
if (!qualResult) {
node->rs_done = true;
u_sess->exec_cxt.exec_result_checkqual_fail = true;
return NULL;
}
}
/*检查是否需要投影*/
if (node->ps.ps_TupFromTlist) {
result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done);
if (is_done == ExprMultipleResult) {
return result_slot;
}
node->ps.ps_TupFromTlist = false;
}
/*重置常量表达式的内存上下文*/
ResetExprContext(econtext);
/*如果rs_done字段为0,则说明结点没有被执行过,进入循环执行*/
while (!node->rs_done) {
outer_plan = outerPlanState(node);
if (outer_plan != NULL) {
/*从外部计划中获取元组*/
outer_tuple_slot = ExecProcNode(outer_plan);
if (TupIsNull(outer_tuple_slot))
return NULL;
econtext->ecxt_outertuple = outer_tuple_slot;
if (node->ps.qual && !ExecQual(node->ps.qual, econtext, false))
continue;
} else {
/*如果没有外部计划,则直接读取target list中的元组*/
node->rs_done = true;
}
/*投影*/
result_slot = ExecProject(node->ps.ps_ProjInfo, &is_done);
if (is_done != ExprEndResult) {
node->ps.ps_TupFromTlist = (is_done == ExprMultipleResult);
return result_slot;
}
}
return NULL;
}
输入参数
ExecResult函数的输入参数是一个ResultState结构体指针,结构体定义如下:
typedef struct ResultState {
PlanState ps; /* its first field is NodeTag */
ExprState* resconstantqual;
bool rs_done; /* are we done? */
bool rs_checkqual; /* do we need to check the qual? */
} ResultState;
字段解释:
类型 | 命名 | 含义 |
---|---|---|
PlanState | ps | 父类 |
ExprState* | resconstantqual | 表达式节点 |
bool | rs_done | 是否完成了所有的输出 |
bool | rs_checkqual | 是否需要检查表达式 |
执行过程
可以画出执行流程图大致如下:
- 检查是否需要检查常量表达式,如果需要则计算表达式
- 表达式为假,返回NULL
- 表达式为真,继续执行后面的代码
- 检查是否需要执行投影操作,如果需要则调用ExecProject函数进行投影并返回元组。
- 接着进入一个循环,循环检查条件是node结点是否仍需被执行,如果需要则
- 从左子节点获取一个元组
- 对元组进行过滤,如果常量表达式为假,则continue,跳过本次循环
- 对元组进行投影并返回
小结
Result算子主要用来处理一些简单的控制过程,由于opengauss数据库没有投影算子和选择算子,Result算子一定程度上可以代替这两个算子。下一篇文章我将讲解BitmapOR和BitmapAnd算子。