本篇内容主要讲解“怎么使用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 void ExecInitExprRec(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. */ void ExprEvalPushStep(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) step ExecInitExprRec (node=0x1c9a930, state=0x1c8f7d8, resv=0x1c8f7e0, resnull=0x1c8f7dd) at execExpr.c:645 645 ExprEvalStep scratch = {0}; (gdb) n 648 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) n 893 ExecInitFunc(&scratch, node, (gdb) 896 ExprEvalPushStep(state, &scratch); (gdb) 897 break; (gdb) 2122 } (gdb)
到此,相信大家对“怎么使用PostgreSQL中ExecInitExprRec函数”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。