怎么使用PostgreSQL中ExecInitExprRec函数
发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,本篇内容主要讲解"怎么使用PostgreSQL中ExecInitExprRec函数",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用PostgreSQ
千家信息网最后更新 2025年01月24日怎么使用PostgreSQL中ExecInitExprRec函数
本篇内容主要讲解"怎么使用PostgreSQL中ExecInitExprRec函数",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"怎么使用PostgreSQL中ExecInitExprRec函数"吧!
EEO_XXX宏定义
opcode分发器宏定义
/* * Macros for opcode dispatch. * opcode分发器宏定义 * * EEO_SWITCH - just hides the switch if not in use. * EEO_SWITCH - 如未使用,则隐藏switch * * EEO_CASE - labels the implementation of named expression step type. * EEO_CASE - 标签化已命名的表达式步骤类型的实现 * * EEO_DISPATCH - jump to the implementation of the step type for 'op'. * EEO_DISPATCH - 跳到'op'指定的步骤类型的实现 * * EEO_OPCODE - compute opcode required by used expression evaluation method. * - 通过请求的表达式解析方法计算opcode * * EEO_NEXT - increment 'op' and jump to correct next step type. * - 'op'++并跳转到下一个步骤类型 * * EEO_JUMP - jump to the specified step number within the current expression. * EEO_JUMP - 在当前表达式中跳转到指定的步骤编号 */#if defined(EEO_USE_COMPUTED_GOTO)//--------------- 定义了EEO_USE_COMPUTED_GOTO/* struct for jump target -> opcode lookup table *///跳转target -> opcode搜索表结构体typedef struct ExprEvalOpLookup{ const void *opcode; ExprEvalOp op;} ExprEvalOpLookup;/* to make dispatch_table accessible outside ExecInterpExpr() */static const void **dispatch_table = NULL;/* jump target -> opcode lookup table */static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST];#define EEO_SWITCH()#define EEO_CASE(name) CASE_##name:#define EEO_DISPATCH() goto *((void *) op->opcode)#define EEO_OPCODE(opcode) ((intptr_t) dispatch_table[opcode])#else /* !EEO_USE_COMPUTED_GOTO *///--------------- 没有定义EEO_USE_COMPUTED_GOTO#define EEO_SWITCH() starteval: switch ((ExprEvalOp) op->opcode)#define EEO_CASE(name) case name:#define EEO_DISPATCH() goto starteval#define EEO_OPCODE(opcode) (opcode)#endif /* EEO_USE_COMPUTED_GOTO */#define EEO_NEXT() \ do { \ op++; \ EEO_DISPATCH(); \ } while (0)#define EEO_JUMP(stepno) \ do { \ op = &state->steps[stepno]; \ EEO_DISPATCH(); \ } while (0)
ExprState
解析表达式中运行期状态节点
/* Bits in ExprState->flags (see also execExpr.h for private flag bits): *//* expression is for use with ExecQual() */#define EEO_FLAG_IS_QUAL (1 << 0)typedef struct ExprState{ //节点tag Node tag; //EEO_FLAG_IS_QUAL 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(). * 存储scalar expression表达式 * 和通过ExecBuildProjectionInfo()函数创建的expressions单列的结果. */#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. * 如果投影元组结果,该slot存储结果,或者为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 */ //原始的表达式树,仅用于debugging Expr *expr; /* private state for an evalfunc */ //evalfunc的私有状态 void *evalfunc_private; /* * XXX: following fields only needed during "compilation" (ExecInitExpr); * could be thrown away afterwards. * XXX: 接下来的字段在"compilation" (ExecInitExpr)期间需要,之后可被"扔掉". */ //当前的步数 int steps_len; /* number of steps currently */ //steps数组已分配的长度 int steps_alloc; /* allocated length of steps array */ //父PlanState节点(如存在) struct PlanState *parent; /* parent PlanState node, if any */ //用于编译PARAM_EXTERN节点 ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */ // Datum *innermost_caseval; bool *innermost_casenull; Datum *innermost_domainval; bool *innermost_domainnull;} ExprState;
ExprEvalStep
表达式解析步骤结构体
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). * 待执行指令. * 在指令准备期间这是枚举型的ExprEvalOp, * 但后续会被改变为某些其他类型,比如用于goto的指针,因此被定义为intprt_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). * 操作的内联数据. * 内联数据用于更快的访问,但同时会导致指令的盘膨胀. * 联合体在64-bit系统上应保持在40字节范围内 * (因此整个结构体不应大于64字节,普通系统上的单个缓存线大小) */ union { /* for EEOP_INNER/OUTER/SCAN_FETCHSOME */ //用于EEOP_INNER/OUTER/SCAN_FETCHSOME struct { /* attribute number up to which to fetch (inclusive) */ //获取到的属性编号 int last_var; TupleDesc known_desc; } fetch; /* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */ struct { /* attnum is attr number - 1 for regular VAR ... */ //attnum是常规VAR的attr number - 1 /* but it's just the normal (negative) attr number for SYSVAR */ //对于SYSVAR,该值是常规的attr number int attnum; Oid vartype; /* type OID of variable */ } var; /* for EEOP_WHOLEROW */ struct { Var *var; /* original Var node in plan tree */ bool first; /* first time through, need to initialize? */ bool slow; /* need runtime check for nulls? */ TupleDesc tupdesc; /* descriptor for resulting tuples */ JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */ } wholerow; /* for EEOP_ASSIGN_*_VAR */ struct { /* target index in ExprState->resultslot->tts_values/nulls */ int resultnum; /* source attribute number - 1 */ int attnum; } assign_var; /* for EEOP_ASSIGN_TMP[_MAKE_RO] */ struct { /* target index in ExprState->resultslot->tts_values/nulls */ int resultnum; } assign_tmp; /* for EEOP_CONST */ struct { /* constant's value */ Datum value; bool isnull; } constval; /* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */ //对于EEOP_FUNCEXPR_* / NULLIF / DISTINCT struct { //函数的检索数据 FmgrInfo *finfo; /* function's lookup data */ //参数信息等 FunctionCallInfo fcinfo_data; /* arguments etc */ /* faster to access without additional indirection: */ //无需额外的指向,更快速的访问 PGFunction fn_addr; /* actual call address */ int nargs; /* number of arguments */ } func; /* for EEOP_BOOL_*_STEP */ struct { bool *anynull; /* track if any input was NULL */ int jumpdone; /* jump here if result determined */ } boolexpr; /* for EEOP_QUAL */ struct { int jumpdone; /* jump here on false or null */ } qualexpr; /* for EEOP_JUMP[_CONDITION] */ struct { int jumpdone; /* target instruction's index */ } jump; /* for EEOP_NULLTEST_ROWIS[NOT]NULL */ struct { /* cached tupdesc pointer - filled at runtime */ TupleDesc argdesc; } nulltest_row; /* for EEOP_PARAM_EXEC/EXTERN */ struct { int paramid; /* numeric ID for parameter */ Oid paramtype; /* OID of parameter's datatype */ } param; /* for EEOP_PARAM_CALLBACK */ struct { ExecEvalSubroutine paramfunc; /* add-on evaluation subroutine */ void *paramarg; /* private data for same */ int paramid; /* numeric ID for parameter */ Oid paramtype; /* OID of parameter's datatype */ } cparam; /* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */ struct { Datum *value; /* value to return */ bool *isnull; } casetest; /* for EEOP_MAKE_READONLY */ struct { Datum *value; /* value to coerce to read-only */ bool *isnull; } make_readonly; /* for EEOP_IOCOERCE */ struct { /* lookup and call info for source type's output function */ FmgrInfo *finfo_out; FunctionCallInfo fcinfo_data_out; /* lookup and call info for result type's input function */ FmgrInfo *finfo_in; FunctionCallInfo fcinfo_data_in; } iocoerce; /* for EEOP_SQLVALUEFUNCTION */ struct { SQLValueFunction *svf; } sqlvaluefunction; /* for EEOP_NEXTVALUEEXPR */ //EEOP_NEXTVALUEEXPR struct { Oid seqid; Oid seqtypid; } nextvalueexpr; /* for EEOP_ARRAYEXPR */ struct { Datum *elemvalues; /* element values get stored here */ bool *elemnulls; int nelems; /* length of the above arrays */ Oid elemtype; /* array element type */ int16 elemlength; /* typlen of the array element type */ bool elembyval; /* is the element type pass-by-value? */ char elemalign; /* typalign of the element type */ bool multidims; /* is array expression multi-D? */ } arrayexpr; /* for EEOP_ARRAYCOERCE */ struct { ExprState *elemexprstate; /* null if no per-element work */ Oid resultelemtype; /* element type of result array */ struct ArrayMapState *amstate; /* workspace for array_map */ } arraycoerce; /* for EEOP_ROW */ struct { TupleDesc tupdesc; /* descriptor for result tuples */ /* workspace for the values constituting the row: */ Datum *elemvalues; bool *elemnulls; } row; /* for EEOP_ROWCOMPARE_STEP */ struct { /* lookup and call data for column comparison function */ FmgrInfo *finfo; FunctionCallInfo fcinfo_data; PGFunction fn_addr; /* target for comparison resulting in NULL */ int jumpnull; /* target for comparison yielding inequality */ int jumpdone; } rowcompare_step; /* for EEOP_ROWCOMPARE_FINAL */ struct { RowCompareType rctype; } rowcompare_final; /* for EEOP_MINMAX */ struct { /* workspace for argument values */ Datum *values; bool *nulls; int nelems; /* is it GREATEST or LEAST? */ MinMaxOp op; /* lookup and call data for comparison function */ FmgrInfo *finfo; FunctionCallInfo fcinfo_data; } minmax; /* for EEOP_FIELDSELECT */ struct { AttrNumber fieldnum; /* field number to extract */ Oid resulttype; /* field's type */ /* cached tupdesc pointer - filled at runtime */ TupleDesc argdesc; } fieldselect; /* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */ struct { /* original expression node */ FieldStore *fstore; /* cached tupdesc pointer - filled at runtime */ /* note that a DEFORM and FORM pair share the same tupdesc */ TupleDesc *argdesc; /* workspace for column values */ Datum *values; bool *nulls; int ncolumns; } fieldstore; /* for EEOP_ARRAYREF_SUBSCRIPT */ struct { /* too big to have inline */ struct ArrayRefState *state; int off; /* 0-based index of this subscript */ bool isupper; /* is it upper or lower subscript? */ int jumpdone; /* jump here on null */ } arrayref_subscript; /* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */ struct { /* too big to have inline */ struct ArrayRefState *state; } arrayref; /* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */ struct { /* name of constraint */ char *constraintname; /* where the result of a CHECK constraint will be stored */ Datum *checkvalue; bool *checknull; /* OID of domain type */ Oid resulttype; } domaincheck; /* for EEOP_CONVERT_ROWTYPE */ struct { ConvertRowtypeExpr *convert; /* original expression */ /* these three fields are filled at runtime: */ TupleDesc indesc; /* tupdesc for input type */ TupleDesc outdesc; /* tupdesc for output type */ TupleConversionMap *map; /* column mapping */ bool initialized; /* initialized for current types? */ } convert_rowtype; /* for EEOP_SCALARARRAYOP */ struct { /* element_type/typlen/typbyval/typalign are filled at runtime */ Oid element_type; /* InvalidOid if not yet filled */ bool useOr; /* use OR or AND semantics? */ int16 typlen; /* array element type storage info */ bool typbyval; char typalign; FmgrInfo *finfo; /* function's lookup data */ FunctionCallInfo fcinfo_data; /* arguments etc */ /* faster to access without additional indirection: */ PGFunction fn_addr; /* actual call address */ } scalararrayop; /* for EEOP_XMLEXPR */ struct { XmlExpr *xexpr; /* original expression node */ /* workspace for evaluating named args, if any */ Datum *named_argvalue; bool *named_argnull; /* workspace for evaluating unnamed args, if any */ Datum *argvalue; bool *argnull; } xmlexpr; /* for EEOP_AGGREF */ struct { /* out-of-line state, modified by nodeAgg.c */ AggrefExprState *astate; } aggref; /* for EEOP_GROUPING_FUNC */ struct { AggState *parent; /* parent Agg */ List *clauses; /* integer list of column numbers */ } grouping_func; /* for EEOP_WINDOW_FUNC */ struct { /* out-of-line state, modified by nodeWindowFunc.c */ WindowFuncExprState *wfstate; } window_func; /* for EEOP_SUBPLAN */ struct { /* out-of-line state, created by nodeSubplan.c */ SubPlanState *sstate; } subplan; /* for EEOP_ALTERNATIVE_SUBPLAN */ struct { /* out-of-line state, created by nodeSubplan.c */ AlternativeSubPlanState *asstate; } alternative_subplan; /* for EEOP_AGG_*DESERIALIZE */ struct { AggState *aggstate; FunctionCallInfo fcinfo_data; int jumpnull; } agg_deserialize; /* for EEOP_AGG_STRICT_INPUT_CHECK */ struct { bool *nulls; int nargs; int jumpnull; } agg_strict_input_check; /* for EEOP_AGG_INIT_TRANS */ struct { AggState *aggstate; AggStatePerTrans pertrans; ExprContext *aggcontext; int setno; int transno; int setoff; int jumpnull; } agg_init_trans; /* for EEOP_AGG_STRICT_TRANS_CHECK */ struct { AggState *aggstate; int setno; int transno; int setoff; int jumpnull; } agg_strict_trans_check; /* for EEOP_AGG_{PLAIN,ORDERED}_TRANS* */ struct { AggState *aggstate; AggStatePerTrans pertrans; ExprContext *aggcontext; int setno; int transno; int setoff; } agg_trans; } d;} ExprEvalStep;
ExprEvalOp
ExprEvalSteps的鉴频器,定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用.
/* * Discriminator for ExprEvalSteps. * ExprEvalSteps的鉴频器 * * Identifies the operation to be executed and which member in the * ExprEvalStep->d union is valid. * 定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用. * * The order of entries needs to be kept in sync with the dispatch_table[] * array in execExprInterp.c:ExecInterpExpr(). * 条目的排序需要与execExprInterp.c:ExecInterpExpr()中dispatch_table[]数组的元素保持一致 */typedef enum ExprEvalOp{ /* entire expression has been evaluated completely, return */ //整个表达式已被解析,返回 EEOP_DONE, /* apply slot_getsomeattrs on corresponding tuple slot */ //在相应的元组slot上应用了slot_getsomeattrs方法 EEOP_INNER_FETCHSOME, EEOP_OUTER_FETCHSOME, EEOP_SCAN_FETCHSOME, /* compute non-system Var value */ //计算非系统Var变量值 EEOP_INNER_VAR, EEOP_OUTER_VAR, EEOP_SCAN_VAR, /* compute system Var value */ //计算系统Var变量值 EEOP_INNER_SYSVAR, EEOP_OUTER_SYSVAR, EEOP_SCAN_SYSVAR, /* compute wholerow Var */ //计算整行Var EEOP_WHOLEROW, /* * Compute non-system Var value, assign it into ExprState's resultslot. * These are not used if a CheckVarSlotCompatibility() check would be * needed. * 计算非系统Var值,分配到ExprState's的resultslot字段中. * 如果CheckVarSlotCompatibility()需要时,这些都不需要. */ EEOP_ASSIGN_INNER_VAR, EEOP_ASSIGN_OUTER_VAR, EEOP_ASSIGN_SCAN_VAR, /* assign ExprState's resvalue/resnull to a column of its resultslot */ //分配ExprState's resvalue/resnull到该列的resultslot中 EEOP_ASSIGN_TMP, /* ditto, applying MakeExpandedObjectReadOnly() */ //同上,应用MakeExpandedObjectReadOnly() EEOP_ASSIGN_TMP_MAKE_RO, /* evaluate Const value */ //解析常量值 EEOP_CONST, /* * Evaluate function call (including OpExprs etc). For speed, we * distinguish in the opcode whether the function is strict and/or * requires usage stats tracking. * 解析函数调用(包括OpExprs等等). * 出于性能的考虑,需要区分opcode是strict函数还是非strict函数,以及是否需要统计跟踪. */ EEOP_FUNCEXPR, EEOP_FUNCEXPR_STRICT, EEOP_FUNCEXPR_FUSAGE, EEOP_FUNCEXPR_STRICT_FUSAGE, /* * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST * subexpressions are special-cased for performance. Since AND always has * at least two subexpressions, FIRST and LAST never apply to the same * subexpression. * 解析布尔AND表达式,每一个子表达式一个步骤. * FIRST/LAST子表达式是性能上的特例. * 由于AND通常至少有两个子表达式,FIRST和LAST永远都不会应用在同一个子表达式上. */ EEOP_BOOL_AND_STEP_FIRST, EEOP_BOOL_AND_STEP, EEOP_BOOL_AND_STEP_LAST, /* similarly for boolean OR expression */ //与布尔OR表达式类似 EEOP_BOOL_OR_STEP_FIRST, EEOP_BOOL_OR_STEP, EEOP_BOOL_OR_STEP_LAST, /* evaluate boolean NOT expression */ //解析布尔NOT表达式 EEOP_BOOL_NOT_STEP, /* simplified version of BOOL_AND_STEP for use by ExecQual() */ //用于ExecQual()中的BOOL_AND_STEP简化版本 EEOP_QUAL, /* unconditional jump to another step */ //无条件跳转到另外一个步骤 EEOP_JUMP, /* conditional jumps based on current result value */ //基于当前结果值的条件跳转 EEOP_JUMP_IF_NULL, EEOP_JUMP_IF_NOT_NULL, EEOP_JUMP_IF_NOT_TRUE, /* perform NULL tests for scalar values */ //为scalar值执行NULL测试 EEOP_NULLTEST_ISNULL, EEOP_NULLTEST_ISNOTNULL, /* perform NULL tests for row values */ //为行值执行NULL测试 EEOP_NULLTEST_ROWISNULL, EEOP_NULLTEST_ROWISNOTNULL, /* evaluate a BooleanTest expression */ //解析BooleanTest表达式 EEOP_BOOLTEST_IS_TRUE, EEOP_BOOLTEST_IS_NOT_TRUE, EEOP_BOOLTEST_IS_FALSE, EEOP_BOOLTEST_IS_NOT_FALSE, /* evaluate PARAM_EXEC/EXTERN parameters */ //解析PARAM_EXEC/EXTERN参数 EEOP_PARAM_EXEC, EEOP_PARAM_EXTERN, EEOP_PARAM_CALLBACK, /* return CaseTestExpr value */ //返回CaseTestExpr值 EEOP_CASE_TESTVAL, /* apply MakeExpandedObjectReadOnly() to target value */ //对目标值应用MakeExpandedObjectReadOnly() EEOP_MAKE_READONLY, /* evaluate assorted special-purpose expression types */ //解析各种特殊用途的表达式类型 EEOP_IOCOERCE, EEOP_DISTINCT, EEOP_NOT_DISTINCT, EEOP_NULLIF, EEOP_SQLVALUEFUNCTION, EEOP_CURRENTOFEXPR, EEOP_NEXTVALUEEXPR, EEOP_ARRAYEXPR, EEOP_ARRAYCOERCE, EEOP_ROW, /* * Compare two individual elements of each of two compared ROW() * expressions. Skip to ROWCOMPARE_FINAL if elements are not equal. * 给出两个需要对比的ROW()表达式,两两比较行中的元素. * 如果元素不相等,则跳转到ROWCOMPARE_FINAL */ EEOP_ROWCOMPARE_STEP, /* evaluate boolean value based on previous ROWCOMPARE_STEP operations */ //基于上一步的ROWCOMPARE_STEP操作解析布尔值 EEOP_ROWCOMPARE_FINAL, /* evaluate GREATEST() or LEAST() */ //解析GREATEST()和LEAST() EEOP_MINMAX, /* evaluate FieldSelect expression */ //解析FieldSelect表达式 EEOP_FIELDSELECT, /* * Deform tuple before evaluating new values for individual fields in a * FieldStore expression. * 在解析FieldStore表达式中的独立列新值前重构元组 */ EEOP_FIELDSTORE_DEFORM, /* * Form the new tuple for a FieldStore expression. Individual fields will * have been evaluated into columns of the tuple deformed by the preceding * DEFORM step. * 为FieldStore表达式构成新元组. * 单独的字段会解析到元组的列中(行已被上一个步骤EEOP_FIELDSTORE_DEFORM析构) */ EEOP_FIELDSTORE_FORM, /* Process an array subscript; short-circuit expression to NULL if NULL */ //处理数组子脚本.如为NULL则短路表达式为NULL EEOP_ARRAYREF_SUBSCRIPT, /* * Compute old array element/slice when an ArrayRef assignment expression * contains ArrayRef/FieldStore subexpressions. Value is accessed using * the CaseTest mechanism. * 在ArrayRef分配表达式包含ArrayRef/FieldStore子表达式时计算旧的数组元素/片. * 通过CaseTest机制访问Value */ EEOP_ARRAYREF_OLD, /* compute new value for ArrayRef assignment expression */ //为ArrayRef分配118 EEOP_ARRAYREF_ASSIGN, /* compute element/slice for ArrayRef fetch expression */ //为ArrayRef提取表达式计算element/slice EEOP_ARRAYREF_FETCH, /* evaluate value for CoerceToDomainValue */ //为CoerceToDomainValue解析值 EEOP_DOMAIN_TESTVAL, /* evaluate a domain's NOT NULL constraint */ //解析域 NOT NULL 约束 EEOP_DOMAIN_NOTNULL, /* evaluate a single domain CHECK constraint */ //解析单个域CHECK约束 EEOP_DOMAIN_CHECK, /* evaluate assorted special-purpose expression types */ //解析特殊目的的表达式类型 EEOP_CONVERT_ROWTYPE, EEOP_SCALARARRAYOP, EEOP_XMLEXPR, EEOP_AGGREF, EEOP_GROUPING_FUNC, EEOP_WINDOW_FUNC, EEOP_SUBPLAN, EEOP_ALTERNATIVE_SUBPLAN, /* aggregation related nodes */ //聚合相关节点 EEOP_AGG_STRICT_DESERIALIZE, EEOP_AGG_DESERIALIZE, EEOP_AGG_STRICT_INPUT_CHECK, EEOP_AGG_INIT_TRANS, EEOP_AGG_STRICT_TRANS_CHECK, EEOP_AGG_PLAIN_TRANS_BYVAL, EEOP_AGG_PLAIN_TRANS, EEOP_AGG_ORDERED_TRANS_DATUM, EEOP_AGG_ORDERED_TRANS_TUPLE, /* non-existent operation, used e.g. to check array lengths */ //不存在的操作,比如用于检测数组长度 EEOP_LAST} ExprEvalOp;
二、源码解读
ExecInitExprRec函数,把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中.
其主要逻辑是根据节点类型,执行相应的处理逻辑,比如节点类型为OpExpr,则其逻辑为:
case T_OpExpr://操作符表达式 { OpExpr *op = (OpExpr *) node; ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid, state); ExprEvalPushStep(state, &scratch); break; }
其他节点类型类似,代码虽然很长,但逻辑清晰简单.
/* * Append the steps necessary for the evaluation of node to ExprState->steps, * possibly recursing into sub-expressions of node. * 把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中. * * node - expression to evaluate * state - ExprState to whose ->steps to append the necessary operations * resv / resnull - where to store the result of the node into * node - 待解析的表达式 * state - 步骤追加到该ExprState ->steps中 * resv / resnull - 节点结果存储的位置 */static voidExecInitExprRec(Expr *node, ExprState *state, Datum *resv, bool *resnull){ ExprEvalStep scratch = {0}; /* Guard against stack overflow due to overly complex expressions */ //避免出现堆栈溢出 check_stack_depth(); /* Step's output location is always what the caller gave us */ //步骤的输出位置往往是调用者提供给我们的 Assert(resv != NULL && resnull != NULL); scratch.resvalue = resv; scratch.resnull = resnull; /* cases should be ordered as they are in enum NodeTag */ //CASE的顺序与NodeTag枚举类型中的顺序一样 switch (nodeTag(node)) { case T_Var://VAR { Var *variable = (Var *) node; if (variable->varattno == InvalidAttrNumber) { /* whole-row Var */ ExecInitWholeRowVar(&scratch, variable, state); } else if (variable->varattno <= 0) { /* system column */ scratch.d.var.attnum = variable->varattno; scratch.d.var.vartype = variable->vartype; switch (variable->varno) { case INNER_VAR: scratch.opcode = EEOP_INNER_SYSVAR; break; case OUTER_VAR: scratch.opcode = EEOP_OUTER_SYSVAR; break; /* INDEX_VAR is handled by default case */ default: scratch.opcode = EEOP_SCAN_SYSVAR; break; } } else { /* regular user column */ scratch.d.var.attnum = variable->varattno - 1; scratch.d.var.vartype = variable->vartype; switch (variable->varno) { case INNER_VAR: scratch.opcode = EEOP_INNER_VAR; break; case OUTER_VAR: scratch.opcode = EEOP_OUTER_VAR; break; /* INDEX_VAR is handled by default case */ default: scratch.opcode = EEOP_SCAN_VAR; break; } } ExprEvalPushStep(state, &scratch); break; } case T_Const://常量 { Const *con = (Const *) node; scratch.opcode = EEOP_CONST; scratch.d.constval.value = con->constvalue; scratch.d.constval.isnull = con->constisnull; ExprEvalPushStep(state, &scratch); break; } case T_Param://参数 { Param *param = (Param *) node; ParamListInfo params; switch (param->paramkind) { case PARAM_EXEC: scratch.opcode = EEOP_PARAM_EXEC; scratch.d.param.paramid = param->paramid; scratch.d.param.paramtype = param->paramtype; ExprEvalPushStep(state, &scratch); break; case PARAM_EXTERN: /* * If we have a relevant ParamCompileHook, use it; * otherwise compile a standard EEOP_PARAM_EXTERN * step. ext_params, if supplied, takes precedence * over info from the parent node's EState (if any). */ if (state->ext_params) params = state->ext_params; else if (state->parent && state->parent->state) params = state->parent->state->es_param_list_info; else params = NULL; if (params && params->paramCompile) { params->paramCompile(params, param, state, resv, resnull); } else { scratch.opcode = EEOP_PARAM_EXTERN; scratch.d.param.paramid = param->paramid; scratch.d.param.paramtype = param->paramtype; ExprEvalPushStep(state, &scratch); } break; default: elog(ERROR, "unrecognized paramkind: %d", (int) param->paramkind); break; } break; } case T_Aggref://聚集 { Aggref *aggref = (Aggref *) node; AggrefExprState *astate = makeNode(AggrefExprState); scratch.opcode = EEOP_AGGREF; scratch.d.aggref.astate = astate; astate->aggref = aggref; if (state->parent && IsA(state->parent, AggState)) { AggState *aggstate = (AggState *) state->parent; aggstate->aggs = lcons(astate, aggstate->aggs); aggstate->numaggs++; } else { /* planner messed up */ elog(ERROR, "Aggref found in non-Agg plan node"); } ExprEvalPushStep(state, &scratch); break; } case T_GroupingFunc: { GroupingFunc *grp_node = (GroupingFunc *) node; Agg *agg; if (!state->parent || !IsA(state->parent, AggState) || !IsA(state->parent->plan, Agg)) elog(ERROR, "GroupingFunc found in non-Agg plan node"); scratch.opcode = EEOP_GROUPING_FUNC; scratch.d.grouping_func.parent = (AggState *) state->parent; agg = (Agg *) (state->parent->plan); if (agg->groupingSets) scratch.d.grouping_func.clauses = grp_node->cols; else scratch.d.grouping_func.clauses = NIL; ExprEvalPushStep(state, &scratch); break; } case T_WindowFunc: { WindowFunc *wfunc = (WindowFunc *) node; WindowFuncExprState *wfstate = makeNode(WindowFuncExprState); wfstate->wfunc = wfunc; if (state->parent && IsA(state->parent, WindowAggState)) { WindowAggState *winstate = (WindowAggState *) state->parent; int nfuncs; winstate->funcs = lcons(wfstate, winstate->funcs); nfuncs = ++winstate->numfuncs; if (wfunc->winagg) winstate->numaggs++; /* for now initialize agg using old style expressions */ wfstate->args = ExecInitExprList(wfunc->args, state->parent); wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter, state->parent); /* * Complain if the windowfunc's arguments contain any * windowfuncs; nested window functions are semantically * nonsensical. (This should have been caught earlier, * but we defend against it here anyway.) */ if (nfuncs != winstate->numfuncs) ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); } else { /* planner messed up */ elog(ERROR, "WindowFunc found in non-WindowAgg plan node"); } scratch.opcode = EEOP_WINDOW_FUNC; scratch.d.window_func.wfstate = wfstate; ExprEvalPushStep(state, &scratch); break; } case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; ExecInitArrayRef(&scratch, aref, state, resv, resnull); break; } case T_FuncExpr://函数表达式 { FuncExpr *func = (FuncExpr *) node; ExecInitFunc(&scratch, node, func->args, func->funcid, func->inputcollid, state); ExprEvalPushStep(state, &scratch); break; } case T_OpExpr://操作符表达式 { OpExpr *op = (OpExpr *) node; ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid, state); ExprEvalPushStep(state, &scratch); break; } case T_DistinctExpr: { DistinctExpr *op = (DistinctExpr *) node; ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid, state); /* * Change opcode of call instruction to EEOP_DISTINCT. * * XXX: historically we've not called the function usage * pgstat infrastructure - that seems inconsistent given that * we do so for normal function *and* operator evaluation. If * we decided to do that here, we'd probably want separate * opcodes for FUSAGE or not. */ scratch.opcode = EEOP_DISTINCT; ExprEvalPushStep(state, &scratch); break; } case T_NullIfExpr: { NullIfExpr *op = (NullIfExpr *) node; ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid, state); /* * Change opcode of call instruction to EEOP_NULLIF. * * XXX: historically we've not called the function usage * pgstat infrastructure - that seems inconsistent given that * we do so for normal function *and* operator evaluation. If * we decided to do that here, we'd probably want separate * opcodes for FUSAGE or not. */ scratch.opcode = EEOP_NULLIF; ExprEvalPushStep(state, &scratch); break; } case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; Expr *scalararg; Expr *arrayarg; FmgrInfo *finfo; FunctionCallInfo fcinfo; AclResult aclresult; Assert(list_length(opexpr->args) == 2); scalararg = (Expr *) linitial(opexpr->args); arrayarg = (Expr *) lsecond(opexpr->args); /* Check permission to call function */ aclresult = pg_proc_aclcheck(opexpr->opfuncid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(opexpr->opfuncid)); InvokeFunctionExecuteHook(opexpr->opfuncid); /* Set up the primary fmgr lookup information */ finfo = palloc0(sizeof(FmgrInfo)); fcinfo = palloc0(sizeof(FunctionCallInfoData)); fmgr_info(opexpr->opfuncid, finfo); fmgr_info_set_expr((Node *) node, finfo); InitFunctionCallInfoData(*fcinfo, finfo, 2, opexpr->inputcollid, NULL, NULL); /* Evaluate scalar directly into left function argument */ ExecInitExprRec(scalararg, state, &fcinfo->arg[0], &fcinfo->argnull[0]); /* * Evaluate array argument into our return value. There's no * danger in that, because the return value is guaranteed to * be overwritten by EEOP_SCALARARRAYOP, and will not be * passed to any other expression. */ ExecInitExprRec(arrayarg, state, resv, resnull); /* And perform the operation */ scratch.opcode = EEOP_SCALARARRAYOP; scratch.d.scalararrayop.element_type = InvalidOid; scratch.d.scalararrayop.useOr = opexpr->useOr; scratch.d.scalararrayop.finfo = finfo; scratch.d.scalararrayop.fcinfo_data = fcinfo; scratch.d.scalararrayop.fn_addr = finfo->fn_addr; ExprEvalPushStep(state, &scratch); break; } case T_BoolExpr: { BoolExpr *boolexpr = (BoolExpr *) node; int nargs = list_length(boolexpr->args); List *adjust_jumps = NIL; int off; ListCell *lc; /* allocate scratch memory used by all steps of AND/OR */ if (boolexpr->boolop != NOT_EXPR) scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool)); /* * For each argument evaluate the argument itself, then * perform the bool operation's appropriate handling. * * We can evaluate each argument into our result area, since * the short-circuiting logic means we only need to remember * previous NULL values. * * AND/OR is split into separate STEP_FIRST (one) / STEP (zero * or more) / STEP_LAST (one) steps, as each of those has to * perform different work. The FIRST/LAST split is valid * because AND/OR have at least two arguments. */ off = 0; foreach(lc, boolexpr->args) { Expr *arg = (Expr *) lfirst(lc); /* Evaluate argument into our output variable */ ExecInitExprRec(arg, state, resv, resnull); /* Perform the appropriate step type */ switch (boolexpr->boolop) { case AND_EXPR: Assert(nargs >= 2); if (off == 0) scratch.opcode = EEOP_BOOL_AND_STEP_FIRST; else if (off + 1 == nargs) scratch.opcode = EEOP_BOOL_AND_STEP_LAST; else scratch.opcode = EEOP_BOOL_AND_STEP; break; case OR_EXPR: Assert(nargs >= 2); if (off == 0) scratch.opcode = EEOP_BOOL_OR_STEP_FIRST; else if (off + 1 == nargs) scratch.opcode = EEOP_BOOL_OR_STEP_LAST; else scratch.opcode = EEOP_BOOL_OR_STEP; break; case NOT_EXPR: Assert(nargs == 1); scratch.opcode = EEOP_BOOL_NOT_STEP; break; default: elog(ERROR, "unrecognized boolop: %d", (int) boolexpr->boolop); break; } scratch.d.boolexpr.jumpdone = -1; ExprEvalPushStep(state, &scratch); adjust_jumps = lappend_int(adjust_jumps, state->steps_len - 1); off++; } /* adjust jump targets */ foreach(lc, adjust_jumps) { ExprEvalStep *as = &state->steps[lfirst_int(lc)]; Assert(as->d.boolexpr.jumpdone == -1); as->d.boolexpr.jumpdone = state->steps_len; } break; } case T_SubPlan: { SubPlan *subplan = (SubPlan *) node; SubPlanState *sstate; if (!state->parent) elog(ERROR, "SubPlan found with no parent plan"); sstate = ExecInitSubPlan(subplan, state->parent); /* add SubPlanState nodes to state->parent->subPlan */ state->parent->subPlan = lappend(state->parent->subPlan, sstate); scratch.opcode = EEOP_SUBPLAN; scratch.d.subplan.sstate = sstate; ExprEvalPushStep(state, &scratch); break; } case T_AlternativeSubPlan: { AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; AlternativeSubPlanState *asstate; if (!state->parent) elog(ERROR, "AlternativeSubPlan found with no parent plan"); asstate = ExecInitAlternativeSubPlan(asplan, state->parent); scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN; scratch.d.alternative_subplan.asstate = asstate; ExprEvalPushStep(state, &scratch); break; } case T_FieldSelect: { FieldSelect *fselect = (FieldSelect *) node; /* evaluate row/record argument into result area */ ExecInitExprRec(fselect->arg, state, resv, resnull); /* and extract field */ scratch.opcode = EEOP_FIELDSELECT; scratch.d.fieldselect.fieldnum = fselect->fieldnum; scratch.d.fieldselect.resulttype = fselect->resulttype; scratch.d.fieldselect.argdesc = NULL; ExprEvalPushStep(state, &scratch); break; } case T_FieldStore: { FieldStore *fstore = (FieldStore *) node; TupleDesc tupDesc; TupleDesc *descp; Datum *values; bool *nulls; int ncolumns; ListCell *l1, *l2; /* find out the number of columns in the composite type */ tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1); ncolumns = tupDesc->natts; DecrTupleDescRefCount(tupDesc); /* create workspace for column values */ values = (Datum *) palloc(sizeof(Datum) * ncolumns); nulls = (bool *) palloc(sizeof(bool) * ncolumns); /* create workspace for runtime tupdesc cache */ descp = (TupleDesc *) palloc(sizeof(TupleDesc)); *descp = NULL; /* emit code to evaluate the composite input value */ ExecInitExprRec(fstore->arg, state, resv, resnull); /* next, deform the input tuple into our workspace */ scratch.opcode = EEOP_FIELDSTORE_DEFORM; scratch.d.fieldstore.fstore = fstore; scratch.d.fieldstore.argdesc = descp; scratch.d.fieldstore.values = values; scratch.d.fieldstore.nulls = nulls; scratch.d.fieldstore.ncolumns = ncolumns; ExprEvalPushStep(state, &scratch); /* evaluate new field values, store in workspace columns */ forboth(l1, fstore->newvals, l2, fstore->fieldnums) { Expr *e = (Expr *) lfirst(l1); AttrNumber fieldnum = lfirst_int(l2); Datum *save_innermost_caseval; bool *save_innermost_casenull; if (fieldnum <= 0 || fieldnum > ncolumns) elog(ERROR, "field number %d is out of range in FieldStore", fieldnum); /* * Use the CaseTestExpr mechanism to pass down the old * value of the field being replaced; this is needed in * case the newval is itself a FieldStore or ArrayRef that * has to obtain and modify the old value. It's safe to * reuse the CASE mechanism because there cannot be a CASE * between here and where the value would be needed, and a * field assignment can't be within a CASE either. (So * saving and restoring innermost_caseval is just * paranoia, but let's do it anyway.) * * Another non-obvious point is that it's safe to use the * field's values[]/nulls[] entries as both the caseval * source and the result address for this subexpression. * That's okay only because (1) both FieldStore and * ArrayRef evaluate their arg or refexpr inputs first, * and (2) any such CaseTestExpr is directly the arg or * refexpr input. So any read of the caseval will occur * before there's a chance to overwrite it. Also, if * multiple entries in the newvals/fieldnums lists target * the same field, they'll effectively be applied * left-to-right which is what we want. */ save_innermost_caseval = state->innermost_caseval; save_innermost_casenull = state->innermost_casenull; state->innermost_caseval = &values[fieldnum - 1]; state->innermost_casenull = &nulls[fieldnum - 1]; ExecInitExprRec(e, state, &values[fieldnum - 1], &nulls[fieldnum - 1]); state->innermost_caseval = save_innermost_caseval; state->innermost_casenull = save_innermost_casenull; } /* finally, form result tuple */ scratch.opcode = EEOP_FIELDSTORE_FORM; scratch.d.fieldstore.fstore = fstore; scratch.d.fieldstore.argdesc = descp; scratch.d.fieldstore.values = values; scratch.d.fieldstore.nulls = nulls; scratch.d.fieldstore.ncolumns = ncolumns; ExprEvalPushStep(state, &scratch); break; } case T_RelabelType: { /* relabel doesn't need to do anything at runtime */ RelabelType *relabel = (RelabelType *) node; ExecInitExprRec(relabel->arg, state, resv, resnull); break; } case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; Oid iofunc; bool typisvarlena; Oid typioparam; FunctionCallInfo fcinfo_in; /* evaluate argument into step's result area */ ExecInitExprRec(iocoerce->arg, state, resv, resnull); /* * Prepare both output and input function calls, to be * evaluated inside a single evaluation step for speed - this * can be a very common operation. * * We don't check permissions here as a type's input/output * function are assumed to be executable by everyone. */ scratch.opcode = EEOP_IOCOERCE; /* lookup the source type's output function */ scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo)); scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData)); getTypeOutputInfo(exprType((Node *) iocoerce->arg), &iofunc, &typisvarlena); fmgr_info(iofunc, scratch.d.iocoerce.finfo_out); fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out); InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out, scratch.d.iocoerce.finfo_out, 1, InvalidOid, NULL, NULL); /* lookup the result type's input function */ scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo)); scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData)); getTypeInputInfo(iocoerce->resulttype, &iofunc, &typioparam); fmgr_info(iofunc, scratch.d.iocoerce.finfo_in); fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in); InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in, scratch.d.iocoerce.finfo_in, 3, InvalidOid, NULL, NULL); /* * We can preload the second and third arguments for the input * function, since they're constants. */ fcinfo_in = scratch.d.iocoerce.fcinfo_data_in; fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam); fcinfo_in->argnull[1] = false; fcinfo_in->arg[2] = Int32GetDatum(-1); fcinfo_in->argnull[2] = false; ExprEvalPushStep(state, &scratch); break; } case T_ArrayCoerceExpr: { ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; Oid resultelemtype; ExprState *elemstate; /* evaluate argument into step's result area */ ExecInitExprRec(acoerce->arg, state, resv, resnull); resultelemtype = get_element_type(acoerce->resulttype); if (!OidIsValid(resultelemtype)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("target type is not an array"))); /* * Construct a sub-expression for the per-element expression; * but don't ready it until after we check it for triviality. * We assume it hasn't any Var references, but does have a * CaseTestExpr representing the source array element values. */ elemstate = makeNode(ExprState); elemstate->expr = acoerce->elemexpr; elemstate->parent = state->parent; elemstate->ext_params = state->ext_params; elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum)); elemstate->innermost_casenull = (bool *) palloc(sizeof(bool)); ExecInitExprRec(acoerce->elemexpr, elemstate, &elemstate->resvalue, &elemstate->resnull); if (elemstate->steps_len == 1 && elemstate->steps[0].opcode == EEOP_CASE_TESTVAL) { /* Trivial, so we need no per-element work at runtime */ elemstate = NULL; } else { /* Not trivial, so append a DONE step */ scratch.opcode = EEOP_DONE; ExprEvalPushStep(elemstate, &scratch); /* and ready the subexpression */ ExecReadyExpr(elemstate); } scratch.opcode = EEOP_ARRAYCOERCE; scratch.d.arraycoerce.elemexprstate = elemstate; scratch.d.arraycoerce.resultelemtype = resultelemtype; if (elemstate) { /* Set up workspace for array_map */ scratch.d.arraycoerce.amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState)); } else { /* Don't need workspace if there's no subexpression */ scratch.d.arraycoerce.amstate = NULL; } ExprEvalPushStep(state, &scratch); break; } case T_ConvertRowtypeExpr: { ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; /* evaluate argument into step's result area */ ExecInitExprRec(convert->arg, state, resv, resnull); /* and push conversion step */ scratch.opcode = EEOP_CONVERT_ROWTYPE; scratch.d.convert_rowtype.convert = convert; scratch.d.convert_rowtype.indesc = NULL; scratch.d.convert_rowtype.outdesc = NULL; scratch.d.convert_rowtype.map = NULL; scratch.d.convert_rowtype.initialized = false; ExprEvalPushStep(state, &scratch); break; } /* note that CaseWhen expressions are handled within this block */ case T_CaseExpr: { CaseExpr *caseExpr = (CaseExpr *) node; List *adjust_jumps = NIL; Datum *caseval = NULL; bool *casenull = NULL; ListCell *lc; /* * If there's a test expression, we have to evaluate it and * save the value where the CaseTestExpr placeholders can find * it. */ if (caseExpr->arg != NULL) { /* Evaluate testexpr into caseval/casenull workspace */ caseval = palloc(sizeof(Datum)); casenull = palloc(sizeof(bool)); ExecInitExprRec(caseExpr->arg, state, caseval, casenull); /* * Since value might be read multiple times, force to R/O * - but only if it could be an expanded datum. */ if (get_typlen(exprType((Node *) caseExpr->arg)) == -1) { /* change caseval in-place */ scratch.opcode = EEOP_MAKE_READONLY; scratch.resvalue = caseval; scratch.resnull = casenull; scratch.d.make_readonly.value = caseval; scratch.d.make_readonly.isnull = casenull; ExprEvalPushStep(state, &scratch); /* restore normal settings of scratch fields */ scratch.resvalue = resv; scratch.resnull = resnull; } } /* * Prepare to evaluate each of the WHEN clauses in turn; as * soon as one is true we return the value of the * corresponding THEN clause. If none are true then we return * the value of the ELSE clause, or NULL if there is none. */ foreach(lc, caseExpr->args) { CaseWhen *when = (CaseWhen *) lfirst(lc); Datum *save_innermost_caseval; bool *save_innermost_casenull; int whenstep; /* * Make testexpr result available to CaseTestExpr nodes * within the condition. We must save and restore prior * setting of innermost_caseval fields, in case this node * is itself within a larger CASE. * * If there's no test expression, we don't actually need * to save and restore these fields; but it's less code to * just do so unconditionally. */ save_innermost_caseval = state->innermost_caseval; save_innermost_casenull = state->innermost_casenull; state->innermost_caseval = caseval; state->innermost_casenull = casenull; /* evaluate condition into CASE's result variables */ ExecInitExprRec(when->expr, state, resv, resnull); state->innermost_caseval = save_innermost_caseval; state->innermost_casenull = save_innermost_casenull; /* If WHEN result isn't true, jump to next CASE arm */ scratch.opcode = EEOP_JUMP_IF_NOT_TRUE; scratch.d.jump.jumpdone = -1; /* computed later */ ExprEvalPushStep(state, &scratch); whenstep = state->steps_len - 1; /* * If WHEN result is true, evaluate THEN result, storing * it into the CASE's result variables. */ ExecInitExprRec(when->result, state, resv, resnull); /* Emit JUMP step to jump to end of CASE's code */ scratch.opcode = EEOP_JUMP; scratch.d.jump.jumpdone = -1; /* computed later */ ExprEvalPushStep(state, &scratch); /* * Don't know address for that jump yet, compute once the * whole CASE expression is built. */ adjust_jumps = lappend_int(adjust_jumps, state->steps_len - 1); /* * But we can set WHEN test's jump target now, to make it * jump to the next WHEN subexpression or the ELSE. */ state->steps[whenstep].d.jump.jumpdone = state->steps_len; } /* transformCaseExpr always adds a default */ Assert(caseExpr->defresult); /* evaluate ELSE expr into CASE's result variables */ ExecInitExprRec(caseExpr->defresult, state, resv, resnull); /* adjust jump targets */ foreach(lc, adjust_jumps) { ExprEvalStep *as = &state->steps[lfirst_int(lc)]; Assert(as->opcode == EEOP_JUMP); Assert(as->d.jump.jumpdone == -1); as->d.jump.jumpdone = state->steps_len; } break; } case T_CaseTestExpr: { /* * Read from location identified by innermost_caseval. Note * that innermost_caseval could be NULL, if this node isn't * actually within a CaseExpr, ArrayCoerceExpr, etc structure. * That can happen because some parts of the system abuse * CaseTestExpr to cause a read of a value externally supplied * in econtext->caseValue_datum. We'll take care of that * scenario at runtime. */ scratch.opcode = EEOP_CASE_TESTVAL; scratch.d.casetest.value = state->innermost_caseval; scratch.d.casetest.isnull = state->innermost_casenull; ExprEvalPushStep(state, &scratch); break; } case T_ArrayExpr: { ArrayExpr *arrayexpr = (ArrayExpr *) node; int nelems = list_length(arrayexpr->elements); ListCell *lc; int elemoff; /* * Evaluate by computing each element, and then forming the * array. Elements are computed into scratch arrays * associated with the ARRAYEXPR step. */ scratch.opcode = EEOP_ARRAYEXPR; scratch.d.arrayexpr.elemvalues = (Datum *) palloc(sizeof(Datum) * nelems); scratch.d.arrayexpr.elemnulls = (bool *) palloc(sizeof(bool) * nelems); scratch.d.arrayexpr.nelems = nelems; /* fill remaining fields of step */ scratch.d.arrayexpr.multidims = arrayexpr->multidims; scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid; /* do one-time catalog lookup for type info */ get_typlenbyvalalign(arrayexpr->element_typeid, &scratch.d.arrayexpr.elemlength, &scratch.d.arrayexpr.elembyval, &scratch.d.arrayexpr.elemalign); /* prepare to evaluate all arguments */ elemoff = 0; foreach(lc, arrayexpr->elements) { Expr *e = (Expr *) lfirst(lc); ExecInitExprRec(e, state, &scratch.d.arrayexpr.elemvalues[elemoff], &scratch.d.arrayexpr.elemnulls[elemoff]); elemoff++; } /* and then collect all into an array */ ExprEvalPushStep(state, &scratch); break; } case T_RowExpr: { RowExpr *rowexpr = (RowExpr *) node; int nelems = list_length(rowexpr->args); TupleDesc tupdesc; int i; ListCell *l; /* Build tupdesc to describe result tuples */ if (rowexpr->row_typeid == RECORDOID) { /* generic record, use types of given expressions */ tupdesc = ExecTypeFromExprList(rowexpr->args); } else { /* it's been cast to a named type, use that */ tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1); } /* In either case, adopt RowExpr's column aliases */ ExecTypeSetColNames(tupdesc, rowexpr->colnames); /* Bless the tupdesc in case it's now of type RECORD */ BlessTupleDesc(tupdesc); /* * In the named-type case, the tupdesc could have more columns * than are in the args list, since the type might have had * columns added since the ROW() was parsed. We want those * extra columns to go to nulls, so we make sure that the * workspace arrays are large enough and then initialize any * extra columns to read as NULLs. */ Assert(nelems <= tupdesc->natts); nelems = Max(nelems, tupdesc->natts); /* * Evaluate by first building datums for each field, and then * a final step forming the composite datum. */ scratch.opcode = EEOP_ROW; scratch.d.row.tupdesc = tupdesc; /* space for the individual field datums */ scratch.d.row.elemvalues = (Datum *) palloc(sizeof(Datum) * nelems); scratch.d.row.elemnulls = (bool *) palloc(sizeof(bool) * nelems); /* as explained above, make sure any extra columns are null */ memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems); /* Set up evaluation, skipping any deleted columns */ i = 0; foreach(l, rowexpr->args) { Form_pg_attribute att = TupleDescAttr(tupdesc, i); Expr *e = (Expr *) lfirst(l); if (!att->attisdropped) { /* * Guard against ALTER COLUMN TYPE on rowtype since * the RowExpr was created. XXX should we check * typmod too? Not sure we can be sure it'll be the * same. */ if (exprType((Node *) e) != att->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("ROW() column has type %s instead of type %s", format_type_be(exprType((Node *) e)), format_type_be(att->atttypid)))); } else { /* * Ignore original expression and insert a NULL. We * don't really care what type of NULL it is, so * always make an int4 NULL. */ e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid); } /* Evaluate column expr into appropriate workspace slot */ ExecInitExprRec(e, state, &scratch.d.row.elemvalues[i], &scratch.d.row.elemnulls[i]); i++; } /* And finally build the row value */ ExprEvalPushStep(state, &scratch); break; } case T_RowCompareExpr: { RowCompareExpr *rcexpr = (RowCompareExpr *) node; int nopers = list_length(rcexpr->opnos); List *adjust_jumps = NIL; ListCell *l_left_expr, *l_right_expr, *l_opno, *l_opfamily, *l_inputcollid; ListCell *lc; int off; /* * Iterate over each field, prepare comparisons. To handle * NULL results, prepare jumps to after the expression. If a * comparison yields a != 0 result, jump to the final step. */ Assert(list_length(rcexpr->largs) == nopers); Assert(list_length(rcexpr->rargs) == nopers); Assert(list_length(rcexpr->opfamilies) == nopers); Assert(list_length(rcexpr->inputcollids) == nopers); off = 0; for (off = 0, l_left_expr = list_head(rcexpr->largs), l_right_expr = list_head(rcexpr->rargs), l_opno = list_head(rcexpr->opnos), l_opfamily = list_head(rcexpr->opfamilies), l_inputcollid = list_head(rcexpr->inputcollids); off < nopers; off++, l_left_expr = lnext(l_left_expr), l_right_expr = lnext(l_right_expr), l_opno = lnext(l_opno), l_opfamily = lnext(l_opfamily), l_inputcollid = lnext(l_inputcollid)) { Expr *left_expr = (Expr *) lfirst(l_left_expr); Expr *right_expr = (Expr *) lfirst(l_right_expr); Oid opno = lfirst_oid(l_opno); Oid opfamily = lfirst_oid(l_opfamily); Oid inputcollid = lfirst_oid(l_inputcollid); int strategy; Oid lefttype; Oid righttype; Oid proc; FmgrInfo *finfo; FunctionCallInfo fcinfo; get_op_opfamily_properties(opno, opfamily, false, &strategy, &lefttype, &righttype); proc = get_opfamily_proc(opfamily, lefttype, righttype, BTORDER_PROC); if (!OidIsValid(proc)) elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", BTORDER_PROC, lefttype, righttype, opfamily); /* Set up the primary fmgr lookup information */ finfo = palloc0(sizeof(FmgrInfo)); fcinfo = palloc0(sizeof(FunctionCallInfoData)); fmgr_info(proc, finfo); fmgr_info_set_expr((Node *) node, finfo); InitFunctionCallInfoData(*fcinfo, finfo, 2, inputcollid, NULL, NULL); /* * If we enforced permissions checks on index support * functions, we'd need to make a check here. But the * index support machinery doesn't do that, and thus * neither does this code. */ /* evaluate left and right args directly into fcinfo */ ExecInitExprRec(left_expr, state, &fcinfo->arg[0], &fcinfo->argnull[0]); ExecInitExprRec(right_expr, state, &fcinfo->arg[1], &fcinfo->argnull[1]); scratch.opcode = EEOP_ROWCOMPARE_STEP; scratch.d.rowcompare_step.finfo = finfo; scratch.d.rowcompare_step.fcinfo_data = fcinfo; scratch.d.rowcompare_step.fn_addr = finfo->fn_addr; /* jump targets filled below */ scratch.d.rowcompare_step.jumpnull = -1; scratch.d.rowcompare_step.jumpdone = -1; ExprEvalPushStep(state, &scratch); adjust_jumps = lappend_int(adjust_jumps, state->steps_len - 1); } /* * We could have a zero-column rowtype, in which case the rows * necessarily compare equal. */ if (nopers == 0) { scratch.opcode = EEOP_CONST; scratch.d.constval.value = Int32GetDatum(0); scratch.d.constval.isnull = false; ExprEvalPushStep(state, &scratch); } /* Finally, examine the last comparison result */ scratch.opcode = EEOP_ROWCOMPARE_FINAL; scratch.d.rowcompare_final.rctype = rcexpr->rctype; ExprEvalPushStep(state, &scratch); /* adjust jump targetss */ foreach(lc, adjust_jumps) { ExprEvalStep *as = &state->steps[lfirst_int(lc)]; Assert(as->opcode == EEOP_ROWCOMPARE_STEP); Assert(as->d.rowcompare_step.jumpdone == -1); Assert(as->d.rowcompare_step.jumpnull == -1); /* jump to comparison evaluation */ as->d.rowcompare_step.jumpdone = state->steps_len - 1; /* jump to the following expression */ as->d.rowcompare_step.jumpnull = state->steps_len; } break; } case T_CoalesceExpr: { CoalesceExpr *coalesce = (CoalesceExpr *) node; List *adjust_jumps = NIL; ListCell *lc; /* We assume there's at least one arg */ Assert(coalesce->args != NIL); /* * Prepare evaluation of all coalesced arguments, after each * one push a step that short-circuits if not null. */ foreach(lc, coalesce->args) { Expr *e = (Expr *) lfirst(lc); /* evaluate argument, directly into result datum */ ExecInitExprRec(e, state, resv, resnull); /* if it's not null, skip to end of COALESCE expr */ scratch.opcode = EEOP_JUMP_IF_NOT_NULL; scratch.d.jump.jumpdone = -1; /* adjust later */ ExprEvalPushStep(state, &scratch); adjust_jumps = lappend_int(adjust_jumps, state->steps_len - 1); } /* * No need to add a constant NULL return - we only can get to * the end of the expression if a NULL already is being * returned. */ /* adjust jump targets */ foreach(lc, adjust_jumps) { ExprEvalStep *as = &state->steps[lfirst_int(lc)]; Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL); Assert(as->d.jump.jumpdone == -1); as->d.jump.jumpdone = state->steps_len; } break; } case T_MinMaxExpr: { MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; int nelems = list_length(minmaxexpr->args); TypeCacheEntry *typentry; FmgrInfo *finfo; FunctionCallInfo fcinfo; ListCell *lc; int off; /* Look up the btree comparison function for the datatype */ typentry = lookup_type_cache(minmaxexpr->minmaxtype, TYPECACHE_CMP_PROC); if (!OidIsValid(typentry->cmp_proc)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", format_type_be(minmaxexpr->minmaxtype)))); /* * If we enforced permissions checks on index support * functions, we'd need to make a check here. But the index * support machinery doesn't do that, and thus neither does * this code. */ /* Perform function lookup */ finfo = palloc0(sizeof(FmgrInfo)); fcinfo = palloc0(sizeof(FunctionCallInfoData)); fmgr_info(typentry->cmp_proc, finfo); fmgr_info_set_expr((Node *) node, finfo); InitFunctionCallInfoData(*fcinfo, finfo, 2, minmaxexpr->inputcollid, NULL, NULL); scratch.opcode = EEOP_MINMAX; /* allocate space to store arguments */ scratch.d.minmax.values = (Datum *) palloc(sizeof(Datum) * nelems); scratch.d.minmax.nulls = (bool *) palloc(sizeof(bool) * nelems); scratch.d.minmax.nelems = nelems; scratch.d.minmax.op = minmaxexpr->op; scratch.d.minmax.finfo = finfo; scratch.d.minmax.fcinfo_data = fcinfo; /* evaluate expressions into minmax->values/nulls */ off = 0; foreach(lc, minmaxexpr->args) { Expr *e = (Expr *) lfirst(lc); ExecInitExprRec(e, state, &scratch.d.minmax.values[off], &scratch.d.minmax.nulls[off]); off++; } /* and push the final comparison */ ExprEvalPushStep(state, &scratch); break; } case T_SQLValueFunction: { SQLValueFunction *svf = (SQLValueFunction *) node; scratch.opcode = EEOP_SQLVALUEFUNCTION; scratch.d.sqlvaluefunction.svf = svf; ExprEvalPushStep(state, &scratch); break; } case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; int nnamed = list_length(xexpr->named_args); int nargs = list_length(xexpr->args); int off; ListCell *arg; scratch.opcode = EEOP_XMLEXPR; scratch.d.xmlexpr.xexpr = xexpr; /* allocate space for storing all the arguments */ if (nnamed) { scratch.d.xmlexpr.named_argvalue = (Datum *) palloc(sizeof(Datum) * nnamed); scratch.d.xmlexpr.named_argnull = (bool *) palloc(sizeof(bool) * nnamed); } else { scratch.d.xmlexpr.named_argvalue = NULL; scratch.d.xmlexpr.named_argnull = NULL; } if (nargs) { scratch.d.xmlexpr.argvalue = (Datum *) palloc(sizeof(Datum) * nargs); scratch.d.xmlexpr.argnull = (bool *) palloc(sizeof(bool) * nargs); } else { scratch.d.xmlexpr.argvalue = NULL; scratch.d.xmlexpr.argnull = NULL; } /* prepare argument execution */ off = 0; foreach(arg, xexpr->named_args) { Expr *e = (Expr *) lfirst(arg); ExecInitExprRec(e, state, &scratch.d.xmlexpr.named_argvalue[off], &scratch.d.xmlexpr.named_argnull[off]); off++; } off = 0; foreach(arg, xexpr->args) { Expr *e = (Expr *) lfirst(arg); ExecInitExprRec(e, state, &scratch.d.xmlexpr.argvalue[off], &scratch.d.xmlexpr.argnull[off]); off++; } /* and evaluate the actual XML expression */ ExprEvalPushStep(state, &scratch); break; } case T_NullTest: { NullTest *ntest = (NullTest *) node; if (ntest->nulltesttype == IS_NULL) { if (ntest->argisrow) scratch.opcode = EEOP_NULLTEST_ROWISNULL; else scratch.opcode = EEOP_NULLTEST_ISNULL; } else if (ntest->nulltesttype == IS_NOT_NULL) { if (ntest->argisrow) scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL; else scratch.opcode = EEOP_NULLTEST_ISNOTNULL; } else { elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); } /* initialize cache in case it's a row test */ scratch.d.nulltest_row.argdesc = NULL; /* first evaluate argument into result variable */ ExecInitExprRec(ntest->arg, state, resv, resnull); /* then push the test of that argument */ ExprEvalPushStep(state, &scratch); break; } case T_BooleanTest: { BooleanTest *btest = (BooleanTest *) node; /* * Evaluate argument, directly into result datum. That's ok, * because resv/resnull is definitely not used anywhere else, * and will get overwritten by the below EEOP_BOOLTEST_IS_* * step. */ ExecInitExprRec(btest->arg, state, resv, resnull); switch (btest->booltesttype) { case IS_TRUE: scratch.opcode = EEOP_BOOLTEST_IS_TRUE; break; case IS_NOT_TRUE: scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE; break; case IS_FALSE: scratch.opcode = EEOP_BOOLTEST_IS_FALSE; break; case IS_NOT_FALSE: scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE; break; case IS_UNKNOWN: /* Same as scalar IS NULL test */ scratch.opcode = EEOP_NULLTEST_ISNULL; break; case IS_NOT_UNKNOWN: /* Same as scalar IS NOT NULL test */ scratch.opcode = EEOP_NULLTEST_ISNOTNULL; break; default: elog(ERROR, "unrecognized booltesttype: %d", (int) btest->booltesttype); } ExprEvalPushStep(state, &scratch); break; } case T_CoerceToDomain: { CoerceToDomain *ctest = (CoerceToDomain *) node; ExecInitCoerceToDomain(&scratch, ctest, state, resv, resnull); break; } case T_CoerceToDomainValue: { /* * Read from location identified by innermost_domainval. Note * that innermost_domainval could be NULL, if we're compiling * a standalone domain check rather than one embedded in a * larger expression. In that case we must read from * econtext->domainValue_datum. We'll take care of that * scenario at runtime. */ scratch.opcode = EEOP_DOMAIN_TESTVAL; /* we share instruction union variant with case testval */ scratch.d.casetest.value = state->innermost_domainval; scratch.d.casetest.isnull = state->innermost_domainnull; ExprEvalPushStep(state, &scratch); break; } case T_CurrentOfExpr: { scratch.opcode = EEOP_CURRENTOFEXPR; ExprEvalPushStep(state, &scratch); break; } case T_NextValueExpr: { NextValueExpr *nve = (NextValueExpr *) node; scratch.opcode = EEOP_NEXTVALUEEXPR; scratch.d.nextvalueexpr.seqid = nve->seqid; scratch.d.nextvalueexpr.seqtypid = nve->typeId; ExprEvalPushStep(state, &scratch); break; } default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); break; }}/* * Add another expression evaluation step to ExprState->steps. * 添加表达式解析步骤到ExprState->steps数组中. * * Note that this potentially re-allocates es->steps, therefore no pointer * into that array may be used while the expression is still being built. */voidExprEvalPushStep(ExprState *es, const ExprEvalStep *s){ if (es->steps_alloc == 0) { es->steps_alloc = 16; es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); } else if (es->steps_alloc == es->steps_len) { es->steps_alloc *= 2; es->steps = repalloc(es->steps, sizeof(ExprEvalStep) * es->steps_alloc); } memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));}
三、跟踪分析
测试脚本
testdb=# select 1+id,c2 from t_expr where id < 3;
进入ExecInitExprRec,Node节点为OpExpr,执行ExprEvalPushStep压入步骤中
(gdb) stepExecInitExprRec (node=0x1c9a930, state=0x1c8f7d8, resv=0x1c8f7e0, resnull=0x1c8f7dd) at execExpr.c:645645 ExprEvalStep scratch = {0};(gdb) n648 check_stack_depth();(gdb) 651 Assert(resv != NULL && resnull != NULL);(gdb) 652 scratch.resvalue = resv;(gdb) 653 scratch.resnull = resnull;(gdb) 656 switch (nodeTag(node))(gdb) 891 OpExpr *op = (OpExpr *) node;(gdb) p *node$16 = {type = T_OpExpr}(gdb) n893 ExecInitFunc(&scratch, node,(gdb) 896 ExprEvalPushStep(state, &scratch);(gdb) 897 break;(gdb) 2122 }(gdb)
到此,相信大家对"怎么使用PostgreSQL中ExecInitExprRec函数"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
表达式
步骤
节点
函数
类型
结果
数组
系统
分配
元素
布尔
逻辑
存储
应用
参数
字段
指令
数据
方法
结构
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
青海果洛软件开发专业
网络安全校园征文手抄报
山东小软件开发公司
服务器安全认证错误什么意思
数据库边插边读取
创建会员管理数据库
乌云服务器
温州网上水费缴费软件开发团队
a股网络安全
软件开发公司售后服务怎样
数据库中加密字符串破解
重庆南华中天 网络安全
家用服务器连不上网
上海星牧网络技术有限公司
国内服务器芯片龙头股
vm虚拟服务器管理
web项目 数据库 小案例
数据库中命令从哪里输入
军测网络安全产品目录
tc网络服务器
湖北推广网络安全维护代理品牌
面向数据库开发用什么语言
网络安全不烦恼听后感
SEER数据库缩写
网络安全与个人隐私调查报告
魔兽世界怀旧服70服务器人口
软件开发电子电路设计
徐汇区市场软件开发代理价格
游卡网络技术有限公司融资
大连网络安全与信息化研究院