相关 《Postgresql源码(61)查询执行——最外层Portal模块》 《Postgresql源码(62)查询执行——子模块ProcessUtility》 《Postgresql源码(63)查询执行——子模块Executor(1)》 《Postgresql源码(64)查询执行——子模块Executor(2)执行前的数据结构和执行过程》 《Postgresql源码(85)查询执行——表达式解析器分析(select 1 1如何执行)》
总结
- 表达式解析器执行可以简化为两步:
- ExecInitExpr:
- 准备ExprState结构记录执行需要的全部信息
- 记录Step数组,每一个为一个工作单元ExprEvalStep
- 每一个工作单元ExprEvalStep记录了该步的执行结果、内部有大union记录了该步骤执行需要的信息
- 每一个工作单元ExprEvalStep记录了自己在ExecInterpExpr函数中,需要跳转到什么位置处理自己
- ExecEvalExprSwitchContext
- 核心函数ExecInterpExpr:函数使用GOTO替代了递归遍历,所以会看到很多宏可读性较差
- 流程:每次拿出一个工作单元ExprEvalStep,goto到
ExprEvalStep->opcode
记录的位置进行处理 - 流程:处理后, 偏移到steps数组下一个位置,goto到
ExprEvalStep->opcode
记录的位置进行处理
- ExecInitExpr:
- 调试技巧备忘
- 进入ExecInterpExpr后,`p *state`可以查看当前执行的所有数据
- 其中
- `p state->steps_len`:要执行几步,最后一步为空,表示结束
- `p state->steps[0]`:可以打印0步的信息,但无法确认是什么类型的step
- `p/x state->steps[0]->opcode`:打印当前步骤的标记位置
- `0x7252bf`
- `p reverse_dispatch_table`:打印所有标记位置对应的op枚举型
- `{opcode = 0x7252bf <ExecInterpExpr 2275>, op = EEOP_FUNCEXPR_STRICT}`
- 用0x7252bf找到EEOP_FUNCEXPR_STRICT类型,在ExecInterpExpr中搜索即可找到处理分支xxx__walker函数
代码语言:txt复制- 功能:递归遍历表达式执行树
- 实现:函数内会递归进入各种各样的node类型,要截取函数感兴趣的node类型处理并返回
- 实现:如果没有发现感兴趣的节点,最后调用expression_tree_walker(…,xxx__walker,…)并把自己作为参数传入,expression_tree_walker在内部递归树时,新节点会递归进入`xxx__walker`处理。
ps. PG对表达式执行做了大量优化,文章最后摘录了优化设计思想,DFS到BFS的经典优化过程。
正文:
待分析SQL:select 1 1
- evaluate_expr:优化器入口,进入表达式解析器。
- CreateExecutorState
- fix_opfuncids
- ExecInitExpr
- ExecEvalExprSwitchContext
1 CreateExecutorState
输入:无
输出:EState
功能:构造通用estate结构用于后面调用执行器模块
2 fix_opfuncids
输入:FuncExpr
代码语言:javascript复制expr -> FuncExpr
{ xpr = {type = T_FuncExpr},
funcid = 177,
funcresulttype = 23,
funcretset = false,
funcvariadic = false,
funcformat = COERCE_EXPLICIT_CALL,
funccollid = 0,
inputcollid = 0,
args = 0x2a49548,
location = -1
}
args -> List
{ xpr = {type = T_Const},
consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4,
constvalue = 1, constisnull = false, constbyval = true, location = 7 }
{ xpr = {type = T_Const},
consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4,
constvalue = 1, constisnull = false, constbyval = true, location = 9 }
输出:无
功能:递归遍历所有节点,包括FuncExpr和FuncExpr->args链表上的所有元素。当前用例场景无操作返回。
- 对于其他操作符可能有配置,例如fix_opfuncids_walker->set_opfuncid会在pg_operator中取oprcode出来赋值。
- pg_operator的oprcode表示当前操作符的处理函数的oid,关联pg_proc。
fix_opfuncids
fix_opfuncids_walker
expression_tree_walker_impl 传入 FuncExpr
case T_FuncExpr:
LIST_WALK(expr->args)
expression_tree_walker_impl 【递归】传入 FuncExpr-->args
fix_opfuncids_walker 【递归】传入 FuncExpr-->args list的第一个元素
expression_tree_walker_impl
3 ExecInitExpr
输入:FuncExpr
输出:ExprState
功能:构造ExprState
数据结构
ExprState
代码语言:javascript复制typedef struct ExprState
{
NodeTag type;
uint8 flags; /* bitmask of EEO_FLAG_* bits, see above */
/*
* Storage for result value of a scalar expression, or for individual
* column results within expressions built by ExecBuildProjectionInfo().
*/
#define FIELDNO_EXPRSTATE_RESNULL 2
bool resnull;
#define FIELDNO_EXPRSTATE_RESVALUE 3
Datum resvalue;
/*
* If projecting a tuple result, this slot holds the result; else NULL.
*/
#define FIELDNO_EXPRSTATE_RESULTSLOT 4
TupleTableSlot *resultslot;
/*
* Instructions to compute expression's return value.
*/
struct ExprEvalStep *steps;
/*
* Function that actually evaluates the expression. This can be set to
* different values depending on the complexity of the expression.
*/
ExprStateEvalFunc evalfunc;
/* original expression tree, for debugging only */
Expr *expr;
/* private state for an evalfunc */
void *evalfunc_private;
/*
* XXX: following fields only needed during "compilation" (ExecInitExpr);
* could be thrown away afterwards.
*/
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
#define FIELDNO_EXPRSTATE_PARENT 11
struct PlanState *parent; /* parent PlanState node, if any */
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
Datum *innermost_caseval;
bool *innermost_casenull;
Datum *innermost_domainval;
bool *innermost_domainnull;
} ExprState;
ExprEvalStep
代码语言:javascript复制typedef struct ExprEvalStep
{
/*
* Instruction to be executed. During instruction preparation this is an
* enum ExprEvalOp, but later it can be changed to some other type, e.g. a
* pointer for computed goto (that's why it's an intptr_t).
*/
intptr_t opcode;
/* where to store the result of this step */
Datum *resvalue;
bool *resnull;
/*
* Inline data for the operation. Inline data is faster to access, but
* also bloats the size of all instructions. The union should be kept to
* no more than 40 bytes on 64-bit systems (so that the entire struct is
* no more than 64 bytes, a single cacheline on common systems).
*/
union
{
...
struct
{
/* constant's value */
Datum value;
bool isnull;
} constval;
...
} d;
} ExprEvalStep;
流程
ExecInitExpr
- ExecInitExprSlots
- ExecInitExprRec
- ExecInitFunc:构造函数结构放入scratch(ExprEvalStep)当前ExprEvalStep状态
代码语言:txt复制- ExprEvalPushStep:ExprState中申请16个位置用来放ExprEvalStep
- ExprEvalPushStep:推一个空的Step上去作为结尾
- ExecReadyExpr:准备一个编译完的表达式来执行
- ExecReadyInterpretedExpr
代码语言:txt复制 - ExecInitInterpreter:初始化表达式解析器,用三个空值调入ExecInterpExpr函数,用ExecInterpExpr函数内定义好的dispatch_table赋给全局变量dispatch_table。dispatch_table是一个指针数组在函数进入时初始化的,记录了所有ExecInterpExpr函数内的label地址(goto需要使用)。
代码语言:txt复制 - ExecInitInterpreter还需要遍历dispatch_table构造reverse_dispatch_table,把dispatch_table的裸数据做下包装
代码语言:txt复制- ExecReadyInterpretedExpr继续
代码语言:txt复制 - 第一次运行要给ExecInterpExprStillValid入口state->evalfunc = ExecInterpExprStillValid
- 把之前Step中的opcode直接用dispatch_table的地址做替换,便于直接跳转。
exprstate最终形态
代码语言:javascript复制{
type = T_ExprState,
flags = 6 '