PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值
这篇文章主要介绍PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
表达式列是指除关系定义中的系统列/定义列之外的其他投影列.比如:
testdb=# create table t_expr(id int);CREATE TABLEtestdb=# insert into t_expr values(1);INSERT 0 1testdb=# insert into t_expr values(2);INSERT 0 1testdb=# alter table t_expr add column c2 varchar(20);ALTER TABLEtestdb=# insert into t_expr(id,c2) select x,'c2'||x from generate_series(3,100000) as x;INSERT 0 99998testdb=# select 1+id from t_expr;
该SQL语句中的"1+id"投影列视为表达式列.
一、数据结构
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;
二、源码解读
ExecInterpExpr函数是实现表达式列解析的主函数,解析给定"econtext"在执行上下文中通过"state"标识的表达式.
其主要实现逻辑是根据ExprState->opcode(ExprState的初始化后续再行介绍)指定的操作指定,调整到相应的实现逻辑中,执行完毕调到下一个步骤直至到达EEOP_DONE,即完成所有步骤.
/* * Evaluate expression identified by "state" in the execution context * given by "econtext". *isnull is set to the is-null flag for the result, * and the Datum value is the function result. * 解析给定"econtext"在执行上下文中通过"state"标识的表达式. * *isnull用于设置结果是否为null,Datum是函数执行的结果. * * As a special case, return the dispatch table's address if state is NULL. * This is used by ExecInitInterpreter to set up the dispatch_table global. * (Only applies when EEO_USE_COMPUTED_GOTO is defined.) * 作为一个特别的情况,如state为NULL,返回分发器表的地址. * 这个情况用于ExecInitInterpreter配置dispatch_table. * (只是在定义了EEO_USE_COMPUTED_GOTO时才应用) */static DatumExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull){ ExprEvalStep *op; TupleTableSlot *resultslot; TupleTableSlot *innerslot; TupleTableSlot *outerslot; TupleTableSlot *scanslot; /* * This array has to be in the same order as enum ExprEvalOp. * 该数组在枚举类型ExprEvalOp中具有同样的顺序 */#if defined(EEO_USE_COMPUTED_GOTO) static const void *const dispatch_table[] = { &&CASE_EEOP_DONE, &&CASE_EEOP_INNER_FETCHSOME, &&CASE_EEOP_OUTER_FETCHSOME, &&CASE_EEOP_SCAN_FETCHSOME, &&CASE_EEOP_INNER_VAR, &&CASE_EEOP_OUTER_VAR, &&CASE_EEOP_SCAN_VAR, &&CASE_EEOP_INNER_SYSVAR, &&CASE_EEOP_OUTER_SYSVAR, &&CASE_EEOP_SCAN_SYSVAR, &&CASE_EEOP_WHOLEROW, &&CASE_EEOP_ASSIGN_INNER_VAR, &&CASE_EEOP_ASSIGN_OUTER_VAR, &&CASE_EEOP_ASSIGN_SCAN_VAR, &&CASE_EEOP_ASSIGN_TMP, &&CASE_EEOP_ASSIGN_TMP_MAKE_RO, &&CASE_EEOP_CONST, &&CASE_EEOP_FUNCEXPR, &&CASE_EEOP_FUNCEXPR_STRICT, &&CASE_EEOP_FUNCEXPR_FUSAGE, &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE, &&CASE_EEOP_BOOL_AND_STEP_FIRST, &&CASE_EEOP_BOOL_AND_STEP, &&CASE_EEOP_BOOL_AND_STEP_LAST, &&CASE_EEOP_BOOL_OR_STEP_FIRST, &&CASE_EEOP_BOOL_OR_STEP, &&CASE_EEOP_BOOL_OR_STEP_LAST, &&CASE_EEOP_BOOL_NOT_STEP, &&CASE_EEOP_QUAL, &&CASE_EEOP_JUMP, &&CASE_EEOP_JUMP_IF_NULL, &&CASE_EEOP_JUMP_IF_NOT_NULL, &&CASE_EEOP_JUMP_IF_NOT_TRUE, &&CASE_EEOP_NULLTEST_ISNULL, &&CASE_EEOP_NULLTEST_ISNOTNULL, &&CASE_EEOP_NULLTEST_ROWISNULL, &&CASE_EEOP_NULLTEST_ROWISNOTNULL, &&CASE_EEOP_BOOLTEST_IS_TRUE, &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE, &&CASE_EEOP_BOOLTEST_IS_FALSE, &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE, &&CASE_EEOP_PARAM_EXEC, &&CASE_EEOP_PARAM_EXTERN, &&CASE_EEOP_PARAM_CALLBACK, &&CASE_EEOP_CASE_TESTVAL, &&CASE_EEOP_MAKE_READONLY, &&CASE_EEOP_IOCOERCE, &&CASE_EEOP_DISTINCT, &&CASE_EEOP_NOT_DISTINCT, &&CASE_EEOP_NULLIF, &&CASE_EEOP_SQLVALUEFUNCTION, &&CASE_EEOP_CURRENTOFEXPR, &&CASE_EEOP_NEXTVALUEEXPR, &&CASE_EEOP_ARRAYEXPR, &&CASE_EEOP_ARRAYCOERCE, &&CASE_EEOP_ROW, &&CASE_EEOP_ROWCOMPARE_STEP, &&CASE_EEOP_ROWCOMPARE_FINAL, &&CASE_EEOP_MINMAX, &&CASE_EEOP_FIELDSELECT, &&CASE_EEOP_FIELDSTORE_DEFORM, &&CASE_EEOP_FIELDSTORE_FORM, &&CASE_EEOP_ARRAYREF_SUBSCRIPT, &&CASE_EEOP_ARRAYREF_OLD, &&CASE_EEOP_ARRAYREF_ASSIGN, &&CASE_EEOP_ARRAYREF_FETCH, &&CASE_EEOP_DOMAIN_TESTVAL, &&CASE_EEOP_DOMAIN_NOTNULL, &&CASE_EEOP_DOMAIN_CHECK, &&CASE_EEOP_CONVERT_ROWTYPE, &&CASE_EEOP_SCALARARRAYOP, &&CASE_EEOP_XMLEXPR, &&CASE_EEOP_AGGREF, &&CASE_EEOP_GROUPING_FUNC, &&CASE_EEOP_WINDOW_FUNC, &&CASE_EEOP_SUBPLAN, &&CASE_EEOP_ALTERNATIVE_SUBPLAN, &&CASE_EEOP_AGG_STRICT_DESERIALIZE, &&CASE_EEOP_AGG_DESERIALIZE, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK, &&CASE_EEOP_AGG_INIT_TRANS, &&CASE_EEOP_AGG_STRICT_TRANS_CHECK, &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS, &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM, &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE, &&CASE_EEOP_LAST }; StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table), "dispatch_table out of whack with ExprEvalOp"); if (unlikely(state == NULL)) //如state == NULL,则调用PointerGetDatum return PointerGetDatum(dispatch_table);#else Assert(state != NULL);#endif /* EEO_USE_COMPUTED_GOTO */ /* setup state */ //配置状态变量 op = state->steps; resultslot = state->resultslot; innerslot = econtext->ecxt_innertuple; outerslot = econtext->ecxt_outertuple; scanslot = econtext->ecxt_scantuple;#if defined(EEO_USE_COMPUTED_GOTO) EEO_DISPATCH();//分发#endif EEO_SWITCH() { EEO_CASE(EEOP_DONE) { goto out; } EEO_CASE(EEOP_INNER_FETCHSOME) { /* XXX: worthwhile to check tts_nvalid inline first? */ slot_getsomeattrs(innerslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_OUTER_FETCHSOME) { slot_getsomeattrs(outerslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_SCAN_FETCHSOME) { slot_getsomeattrs(scanslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_INNER_VAR) { int attnum = op->d.var.attnum; /* * Since we already extracted all referenced columns from the * tuple with a FETCHSOME step, we can just grab the value * directly out of the slot's decomposed-data arrays. But let's * have an Assert to check that that did happen. */ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); *op->resvalue = innerslot->tts_values[attnum]; *op->resnull = innerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_OUTER_VAR) { int attnum = op->d.var.attnum; /* See EEOP_INNER_VAR comments */ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); *op->resvalue = outerslot->tts_values[attnum]; *op->resnull = outerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_SCAN_VAR) { int attnum = op->d.var.attnum; /* See EEOP_INNER_VAR comments */ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); *op->resvalue = scanslot->tts_values[attnum]; *op->resnull = scanslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_INNER_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(innerslot->tts_tuple != NULL); Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(innerslot->tts_tuple, attnum, innerslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_OUTER_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(outerslot->tts_tuple != NULL); Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(outerslot->tts_tuple, attnum, outerslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_SCAN_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(scanslot->tts_tuple != NULL); Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(scanslot->tts_tuple, attnum, scanslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_WHOLEROW) { /* too complex for an inline implementation */ ExecEvalWholeRowVar(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_INNER_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); resultslot->tts_values[resultnum] = innerslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_OUTER_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); resultslot->tts_values[resultnum] = outerslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_SCAN_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); resultslot->tts_values[resultnum] = scanslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_TMP) { int resultnum = op->d.assign_tmp.resultnum; resultslot->tts_values[resultnum] = state->resvalue; resultslot->tts_isnull[resultnum] = state->resnull; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO) { int resultnum = op->d.assign_tmp.resultnum; resultslot->tts_isnull[resultnum] = state->resnull; if (!resultslot->tts_isnull[resultnum]) resultslot->tts_values[resultnum] = MakeExpandedObjectReadOnlyInternal(state->resvalue); else resultslot->tts_values[resultnum] = state->resvalue; EEO_NEXT(); } EEO_CASE(EEOP_CONST) { *op->resnull = op->d.constval.isnull; *op->resvalue = op->d.constval.value; EEO_NEXT(); } /* * Function-call implementations. Arguments have previously been * evaluated directly into fcinfo->args. * * As both STRICT checks and function-usage are noticeable performance * wise, and function calls are a very hot-path (they also back * operators!), it's worth having so many separate opcodes. * * Note: the reason for using a temporary variable "d", here and in * other places, is that some compilers think "*op->resvalue = f();" * requires them to evaluate op->resvalue into a register before * calling f(), just in case f() is able to modify op->resvalue * somehow. The extra line of code can save a useless register spill * and reload across the function call. */ EEO_CASE(EEOP_FUNCEXPR) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; Datum d; fcinfo->isnull = false; d = op->d.func.fn_addr(fcinfo); *op->resvalue = d; *op->resnull = fcinfo->isnull; EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_STRICT) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; bool *argnull = fcinfo->argnull; int argno; Datum d; /* strict function, so check for NULL args */ for (argno = 0; argno < op->d.func.nargs; argno++) { if (argnull[argno]) { *op->resnull = true; goto strictfail; } } fcinfo->isnull = false; d = op->d.func.fn_addr(fcinfo); *op->resvalue = d; *op->resnull = fcinfo->isnull; strictfail: EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_FUSAGE) { /* not common enough to inline */ ExecEvalFuncExprFusage(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE) { /* not common enough to inline */ ExecEvalFuncExprStrictFusage(state, op, econtext); EEO_NEXT(); } /* * If any of its clauses is FALSE, an AND's result is FALSE regardless * of the states of the rest of the clauses, so we can stop evaluating * and return FALSE immediately. If none are FALSE and one or more is * NULL, we return NULL; otherwise we return TRUE. This makes sense * when you interpret NULL as "don't know": perhaps one of the "don't * knows" would have been FALSE if we'd known its value. Only when * all the inputs are known to be TRUE can we state confidently that * the AND's result is TRUE. */ EEO_CASE(EEOP_BOOL_AND_STEP_FIRST) { *op->d.boolexpr.anynull = false; /* * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the * same as EEOP_BOOL_AND_STEP - so fall through to that. */ /* FALL THROUGH */ } EEO_CASE(EEOP_BOOL_AND_STEP) { if (*op->resnull) { *op->d.boolexpr.anynull = true; } else if (!DatumGetBool(*op->resvalue)) { /* result is already set to FALSE, need not change it */ /* bail out early */ EEO_JUMP(op->d.boolexpr.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_AND_STEP_LAST) { if (*op->resnull) { /* result is already set to NULL, need not change it */ } else if (!DatumGetBool(*op->resvalue)) { /* result is already set to FALSE, need not change it */ /* * No point jumping early to jumpdone - would be same target * (as this is the last argument to the AND expression), * except more expensive. */ } else if (*op->d.boolexpr.anynull) { *op->resvalue = (Datum) 0; *op->resnull = true; } else { /* result is already set to TRUE, need not change it */ } EEO_NEXT(); } /* * If any of its clauses is TRUE, an OR's result is TRUE regardless of * the states of the rest of the clauses, so we can stop evaluating * and return TRUE immediately. If none are TRUE and one or more is * NULL, we return NULL; otherwise we return FALSE. This makes sense * when you interpret NULL as "don't know": perhaps one of the "don't * knows" would have been TRUE if we'd known its value. Only when all * the inputs are known to be FALSE can we state confidently that the * OR's result is FALSE. */ EEO_CASE(EEOP_BOOL_OR_STEP_FIRST) { *op->d.boolexpr.anynull = false; /* * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same * as EEOP_BOOL_OR_STEP - so fall through to that. */ /* FALL THROUGH */ } EEO_CASE(EEOP_BOOL_OR_STEP) { if (*op->resnull) { *op->d.boolexpr.anynull = true; } else if (DatumGetBool(*op->resvalue)) { /* result is already set to TRUE, need not change it */ /* bail out early */ EEO_JUMP(op->d.boolexpr.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_OR_STEP_LAST) { if (*op->resnull) { /* result is already set to NULL, need not change it */ } else if (DatumGetBool(*op->resvalue)) { /* result is already set to TRUE, need not change it */ /* * No point jumping to jumpdone - would be same target (as * this is the last argument to the AND expression), except * more expensive. */ } else if (*op->d.boolexpr.anynull) { *op->resvalue = (Datum) 0; *op->resnull = true; } else { /* result is already set to FALSE, need not change it */ } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_NOT_STEP) { /* * Evaluation of 'not' is simple... if expr is false, then return * 'true' and vice versa. It's safe to do this even on a * nominally null value, so we ignore resnull; that means that * NULL in produces NULL out, which is what we want. */ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_QUAL) { /* simplified version of BOOL_AND_STEP for use by ExecQual() */ /* If argument (also result) is false or null ... */ if (*op->resnull || !DatumGetBool(*op->resvalue)) { /* ... bail out early, returning FALSE */ *op->resnull = false; *op->resvalue = BoolGetDatum(false); EEO_JUMP(op->d.qualexpr.jumpdone); } /* * Otherwise, leave the TRUE value in place, in case this is the * last qual. Then, TRUE is the correct answer. */ EEO_NEXT(); } EEO_CASE(EEOP_JUMP) { /* Unconditionally jump to target step */ EEO_JUMP(op->d.jump.jumpdone); } EEO_CASE(EEOP_JUMP_IF_NULL) { /* Transfer control if current result is null */ if (*op->resnull) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_JUMP_IF_NOT_NULL) { /* Transfer control if current result is non-null */ if (!*op->resnull) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_JUMP_IF_NOT_TRUE) { /* Transfer control if current result is null or false */ if (*op->resnull || !DatumGetBool(*op->resvalue)) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ISNULL) { *op->resvalue = BoolGetDatum(*op->resnull); *op->resnull = false; EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ISNOTNULL) { *op->resvalue = BoolGetDatum(!*op->resnull); *op->resnull = false; EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ROWISNULL) { /* out of line implementation: too large */ ExecEvalRowNull(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL) { /* out of line implementation: too large */ ExecEvalRowNotNull(state, op, econtext); EEO_NEXT(); } /* BooleanTest implementations for all booltesttypes */ EEO_CASE(EEOP_BOOLTEST_IS_TRUE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } /* else, input value is the correct output as well */ EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_FALSE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } /* else, input value is the correct output as well */ EEO_NEXT(); } EEO_CASE(EEOP_PARAM_EXEC) { /* out of line implementation: too large */ ExecEvalParamExec(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_PARAM_EXTERN) { /* out of line implementation: too large */ ExecEvalParamExtern(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_PARAM_CALLBACK) { /* allow an extension module to supply a PARAM_EXTERN value */ op->d.cparam.paramfunc(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_CASE_TESTVAL) { /* * Normally upper parts of the expression tree have setup the * values to be returned here, but some parts of the system * currently misuse {caseValue,domainValue}_{datum,isNull} to set * run-time data. So if no values have been set-up, use * ExprContext's. This isn't pretty, but also not *that* ugly, * and this is unlikely to be performance sensitive enough to * worry about an extra branch. */ if (op->d.casetest.value) { *op->resvalue = *op->d.casetest.value; *op->resnull = *op->d.casetest.isnull; } else { *op->resvalue = econtext->caseValue_datum; *op->resnull = econtext->caseValue_isNull; } EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_TESTVAL) { /* * See EEOP_CASE_TESTVAL comment. */ if (op->d.casetest.value) { *op->resvalue = *op->d.casetest.value; *op->resnull = *op->d.casetest.isnull; } else { *op->resvalue = econtext->domainValue_datum; *op->resnull = econtext->domainValue_isNull; } EEO_NEXT(); } EEO_CASE(EEOP_MAKE_READONLY) { /* * Force a varlena value that might be read multiple times to R/O */ if (!*op->d.make_readonly.isnull) *op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value); *op->resnull = *op->d.make_readonly.isnull; EEO_NEXT(); } EEO_CASE(EEOP_IOCOERCE) { /* * Evaluate a CoerceViaIO node. This can be quite a hot path, so * inline as much work as possible. The source value is in our * result variable. */ char *str; /* call output function (similar to OutputFunctionCall) */ if (*op->resnull) { /* output functions are not called on nulls */ str = NULL; } else { FunctionCallInfo fcinfo_out; fcinfo_out = op->d.iocoerce.fcinfo_data_out; fcinfo_out->arg[0] = *op->resvalue; fcinfo_out->argnull[0] = false; fcinfo_out->isnull = false; str = DatumGetCString(FunctionCallInvoke(fcinfo_out)); /* OutputFunctionCall assumes result isn't null */ Assert(!fcinfo_out->isnull); } /* call input function (similar to InputFunctionCall) */ if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL) { FunctionCallInfo fcinfo_in; Datum d; fcinfo_in = op->d.iocoerce.fcinfo_data_in; fcinfo_in->arg[0] = PointerGetDatum(str); fcinfo_in->argnull[0] = *op->resnull; /* second and third arguments are already set up */ fcinfo_in->isnull = false; d = FunctionCallInvoke(fcinfo_in); *op->resvalue = d; /* Should get null result if and only if str is NULL */ if (str == NULL) { Assert(*op->resnull); Assert(fcinfo_in->isnull); } else { Assert(!*op->resnull); Assert(!fcinfo_in->isnull); } } EEO_NEXT(); } EEO_CASE(EEOP_DISTINCT) { /* * IS DISTINCT FROM must evaluate arguments (already done into * fcinfo->arg/argnull) to determine whether they are NULL; if * either is NULL then the result is determined. If neither is * NULL, then proceed to evaluate the comparison function, which * is just the type's standard equality operator. We need not * care whether that function is strict. Because the handling of * nulls is different, we can't just reuse EEOP_FUNCEXPR. */ FunctionCallInfo fcinfo = op->d.func.fcinfo_data; /* check function arguments for NULLness */ if (fcinfo->argnull[0] && fcinfo->argnull[1]) { /* Both NULL? Then is not distinct... */ *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { /* Only one is NULL? Then is distinct... */ *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else { /* Neither null, so apply the equality function */ Datum eqresult; fcinfo->isnull = false; eqresult = op->d.func.fn_addr(fcinfo); /* Must invert result of "="; safe to do even if null */ *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult)); *op->resnull = fcinfo->isnull; } EEO_NEXT(); } /* see EEOP_DISTINCT for comments, this is just inverted */ EEO_CASE(EEOP_NOT_DISTINCT) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; if (fcinfo->argnull[0] && fcinfo->argnull[1]) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else { Datum eqresult; fcinfo->isnull = false; eqresult = op->d.func.fn_addr(fcinfo); *op->resvalue = eqresult; *op->resnull = fcinfo->isnull; } EEO_NEXT(); } EEO_CASE(EEOP_NULLIF) { /* * The arguments are already evaluated into fcinfo->arg/argnull. */ FunctionCallInfo fcinfo = op->d.func.fcinfo_data; /* if either argument is NULL they can't be equal */ if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) { Datum result; fcinfo->isnull = false; result = op->d.func.fn_addr(fcinfo); /* if the arguments are equal return null */ if (!fcinfo->isnull && DatumGetBool(result)) { *op->resvalue = (Datum) 0; *op->resnull = true; EEO_NEXT(); } } /* Arguments aren't equal, so return the first one */ *op->resvalue = fcinfo->arg[0]; *op->resnull = fcinfo->argnull[0]; EEO_NEXT(); } EEO_CASE(EEOP_SQLVALUEFUNCTION) { /* * Doesn't seem worthwhile to have an inline implementation * efficiency-wise. */ ExecEvalSQLValueFunction(state, op); EEO_NEXT(); } EEO_CASE(EEOP_CURRENTOFEXPR) { /* error invocation uses space, and shouldn't ever occur */ ExecEvalCurrentOfExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_NEXTVALUEEXPR) { /* * Doesn't seem worthwhile to have an inline implementation * efficiency-wise. */ ExecEvalNextValueExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYEXPR) { /* too complex for an inline implementation */ ExecEvalArrayExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYCOERCE) { /* too complex for an inline implementation */ ExecEvalArrayCoerce(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ROW) { /* too complex for an inline implementation */ ExecEvalRow(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ROWCOMPARE_STEP) { FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data; Datum d; /* force NULL result if strict fn and NULL input */ if (op->d.rowcompare_step.finfo->fn_strict && (fcinfo->argnull[0] || fcinfo->argnull[1])) { *op->resnull = true; EEO_JUMP(op->d.rowcompare_step.jumpnull); } /* Apply comparison function */ fcinfo->isnull = false; d = op->d.rowcompare_step.fn_addr(fcinfo); *op->resvalue = d; /* force NULL result if NULL function result */ if (fcinfo->isnull) { *op->resnull = true; EEO_JUMP(op->d.rowcompare_step.jumpnull); } *op->resnull = false; /* If unequal, no need to compare remaining columns */ if (DatumGetInt32(*op->resvalue) != 0) { EEO_JUMP(op->d.rowcompare_step.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_ROWCOMPARE_FINAL) { int32 cmpresult = DatumGetInt32(*op->resvalue); RowCompareType rctype = op->d.rowcompare_final.rctype; *op->resnull = false; switch (rctype) { /* EQ and NE cases aren't allowed here */ case ROWCOMPARE_LT: *op->resvalue = BoolGetDatum(cmpresult < 0); break; case ROWCOMPARE_LE: *op->resvalue = BoolGetDatum(cmpresult <= 0); break; case ROWCOMPARE_GE: *op->resvalue = BoolGetDatum(cmpresult >= 0); break; case ROWCOMPARE_GT: *op->resvalue = BoolGetDatum(cmpresult > 0); break; default: Assert(false); break; } EEO_NEXT(); } EEO_CASE(EEOP_MINMAX) { /* too complex for an inline implementation */ ExecEvalMinMax(state, op); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSELECT) { /* too complex for an inline implementation */ ExecEvalFieldSelect(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSTORE_DEFORM) { /* too complex for an inline implementation */ ExecEvalFieldStoreDeForm(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSTORE_FORM) { /* too complex for an inline implementation */ ExecEvalFieldStoreForm(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT) { /* Process an array subscript */ /* too complex for an inline implementation */ if (ExecEvalArrayRefSubscript(state, op)) { EEO_NEXT(); } else { /* Subscript is null, short-circuit ArrayRef to NULL */ EEO_JUMP(op->d.arrayref_subscript.jumpdone); } } EEO_CASE(EEOP_ARRAYREF_OLD) { /* * Fetch the old value in an arrayref assignment, in case it's * referenced (via a CaseTestExpr) inside the assignment * expression. */ /* too complex for an inline implementation */ ExecEvalArrayRefOld(state, op); EEO_NEXT(); } /* * Perform ArrayRef assignment */ EEO_CASE(EEOP_ARRAYREF_ASSIGN) { /* too complex for an inline implementation */ ExecEvalArrayRefAssign(state, op); EEO_NEXT(); } /* * Fetch subset of an array. */ EEO_CASE(EEOP_ARRAYREF_FETCH) { /* too complex for an inline implementation */ ExecEvalArrayRefFetch(state, op); EEO_NEXT(); } EEO_CASE(EEOP_CONVERT_ROWTYPE) { /* too complex for an inline implementation */ ExecEvalConvertRowtype(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_SCALARARRAYOP) { /* too complex for an inline implementation */ ExecEvalScalarArrayOp(state, op); EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_NOTNULL) { /* too complex for an inline implementation */ ExecEvalConstraintNotNull(state, op); EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_CHECK) { /* too complex for an inline implementation */ ExecEvalConstraintCheck(state, op); EEO_NEXT(); } EEO_CASE(EEOP_XMLEXPR) { /* too complex for an inline implementation */ ExecEvalXmlExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_AGGREF) { /* * Returns a Datum whose value is the precomputed aggregate value * found in the given expression context. */ AggrefExprState *aggref = op->d.aggref.astate; Assert(econtext->ecxt_aggvalues != NULL); *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno]; *op->resnull = econtext->ecxt_aggnulls[aggref->aggno]; EEO_NEXT(); } EEO_CASE(EEOP_GROUPING_FUNC) { /* too complex/uncommon for an inline implementation */ ExecEvalGroupingFunc(state, op); EEO_NEXT(); } EEO_CASE(EEOP_WINDOW_FUNC) { /* * Like Aggref, just return a precomputed value from the econtext. */ WindowFuncExprState *wfunc = op->d.window_func.wfstate; Assert(econtext->ecxt_aggvalues != NULL); *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno]; *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno]; EEO_NEXT(); } EEO_CASE(EEOP_SUBPLAN) { /* too complex for an inline implementation */ ExecEvalSubPlan(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN) { /* too complex for an inline implementation */ ExecEvalAlternativeSubPlan(state, op, econtext); EEO_NEXT(); } /* evaluate a strict aggregate deserialization function */ EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE) { bool *argnull = op->d.agg_deserialize.fcinfo_data->argnull; /* Don't call a strict deserialization function with NULL input */ if (argnull[0]) EEO_JUMP(op->d.agg_deserialize.jumpnull); /* fallthrough */ } /* evaluate aggregate deserialization function (non-strict portion) */ EEO_CASE(EEOP_AGG_DESERIALIZE) { FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data; AggState *aggstate = op->d.agg_deserialize.aggstate; MemoryContext oldContext; /* * We run the deserialization functions in per-input-tuple memory * context. */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->isnull = false; *op->resvalue = FunctionCallInvoke(fcinfo); *op->resnull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* * Check that a strict aggregate transition / combination function's * input is not NULL. */ EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK) { int argno; bool *nulls = op->d.agg_strict_input_check.nulls; int nargs = op->d.agg_strict_input_check.nargs; for (argno = 0; argno < nargs; argno++) { if (nulls[argno]) EEO_JUMP(op->d.agg_strict_input_check.jumpnull); } EEO_NEXT(); } /* * Initialize an aggregate's first value if necessary. */ EEO_CASE(EEOP_AGG_INIT_TRANS) { AggState *aggstate; AggStatePerGroup pergroup; aggstate = op->d.agg_init_trans.aggstate; pergroup = &aggstate->all_pergroups [op->d.agg_init_trans.setoff] [op->d.agg_init_trans.transno]; /* If transValue has not yet been initialized, do so now. */ if (pergroup->noTransValue) { AggStatePerTrans pertrans = op->d.agg_init_trans.pertrans; aggstate->curaggcontext = op->d.agg_init_trans.aggcontext; aggstate->current_set = op->d.agg_init_trans.setno; ExecAggInitGroup(aggstate, pertrans, pergroup); /* copied trans value from input, done this round */ EEO_JUMP(op->d.agg_init_trans.jumpnull); } EEO_NEXT(); } /* check that a strict aggregate's input isn't NULL */ EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK) { AggState *aggstate; AggStatePerGroup pergroup; aggstate = op->d.agg_strict_trans_check.aggstate; pergroup = &aggstate->all_pergroups [op->d.agg_strict_trans_check.setoff] [op->d.agg_strict_trans_check.transno]; if (unlikely(pergroup->transValueIsNull)) EEO_JUMP(op->d.agg_strict_trans_check.jumpnull); EEO_NEXT(); } /* * Evaluate aggregate transition / combine function that has a * by-value transition type. That's a seperate case from the * by-reference implementation because it's a bit simpler. */ EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL) { AggState *aggstate; AggStatePerTrans pertrans; AggStatePerGroup pergroup; FunctionCallInfo fcinfo; MemoryContext oldContext; Datum newVal; aggstate = op->d.agg_trans.aggstate; pertrans = op->d.agg_trans.pertrans; pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(pertrans->transtypeByVal); fcinfo = &pertrans->transfn_fcinfo; /* cf. select_current_set() */ aggstate->curaggcontext = op->d.agg_trans.aggcontext; aggstate->current_set = op->d.agg_trans.setno; /* set up aggstate->curpertrans for AggGetAggref() */ aggstate->curpertrans = pertrans; /* invoke transition function in per-tuple context */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->arg[0] = pergroup->transValue; fcinfo->argnull[0] = pergroup->transValueIsNull; fcinfo->isnull = false; /* just in case transfn doesn't set it */ newVal = FunctionCallInvoke(fcinfo); pergroup->transValue = newVal; pergroup->transValueIsNull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* * Evaluate aggregate transition / combine function that has a * by-reference transition type. * * Could optimize a bit further by splitting off by-reference * fixed-length types, but currently that doesn't seem worth it. */ EEO_CASE(EEOP_AGG_PLAIN_TRANS) { AggState *aggstate; AggStatePerTrans pertrans; AggStatePerGroup pergroup; FunctionCallInfo fcinfo; MemoryContext oldContext; Datum newVal; aggstate = op->d.agg_trans.aggstate; pertrans = op->d.agg_trans.pertrans; pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(!pertrans->transtypeByVal); fcinfo = &pertrans->transfn_fcinfo; /* cf. select_current_set() */ aggstate->curaggcontext = op->d.agg_trans.aggcontext; aggstate->current_set = op->d.agg_trans.setno; /* set up aggstate->curpertrans for AggGetAggref() */ aggstate->curpertrans = pertrans; /* invoke transition function in per-tuple context */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->arg[0] = pergroup->transValue; fcinfo->argnull[0] = pergroup->transValueIsNull; fcinfo->isnull = false; /* just in case transfn doesn't set it */ newVal = FunctionCallInvoke(fcinfo); /* * For pass-by-ref datatype, must copy the new value into * aggcontext and free the prior transValue. But if transfn * returned a pointer to its first input, we don't need to do * anything. Also, if transfn returned a pointer to a R/W * expanded object that is already a child of the aggcontext, * assume we can adopt that value without copying it. */ if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue)) newVal = ExecAggTransReparent(aggstate, pertrans, newVal, fcinfo->isnull, pergroup->transValue, pergroup->transValueIsNull); pergroup->transValue = newVal; pergroup->transValueIsNull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* process single-column ordered aggregate datum */ EEO_CASE(EEOP_AGG_ORDERED_TRANS_DATUM) { /* too complex for an inline implementation */ ExecEvalAggOrderedTransDatum(state, op, econtext); EEO_NEXT(); } /* process multi-column ordered aggregate tuple */ EEO_CASE(EEOP_AGG_ORDERED_TRANS_TUPLE) { /* too complex for an inline implementation */ ExecEvalAggOrderedTransTuple(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_LAST) { /* unreachable */ Assert(false); goto out; } }out: *isnull = state->resnull; return state->resvalue;}
三、跟踪分析
测试脚本
testdb=# alter table t_expr add primary key(id);ALTER TABLEtestdb=# select 1+id from t_expr where id < 3;
调用栈
(gdb) bt#0 ExecInterpExpr (state=0x1e6baa8, econtext=0x1e6b6d8, isnull=0x7fffdbc3b877) at execExprInterp.c:402#1 0x00000000006cd7ed in ExecInterpExprStillValid (state=0x1e6baa8, econtext=0x1e6b6d8, isNull=0x7fffdbc3b877) at execExprInterp.c:1786#2 0x00000000006e1f7f in ExecEvalExprSwitchContext (state=0x1e6baa8, econtext=0x1e6b6d8, isNull=0x7fffdbc3b877) at ../../../src/include/executor/executor.h:313#3 0x00000000006e1fe8 in ExecProject (projInfo=0x1e6baa0) at ../../../src/include/executor/executor.h:347#4 0x00000000006e2358 in ExecScan (node=0x1e6b5c0, accessMtd=0x7103a9, recheckMtd=0x710474 ) at execScan.c:201#5 0x00000000007104be in ExecSeqScan (pstate=0x1e6b5c0) at nodeSeqscan.c:129#6 0x00000000006e05bb in ExecProcNodeFirst (node=0x1e6b5c0) at execProcnode.c:445#7 0x00000000006d551e in ExecProcNode (node=0x1e6b5c0) at ../../../src/include/executor/executor.h:247#8 0x00000000006d7d56 in ExecutePlan (estate=0x1e6b3a8, planstate=0x1e6b5c0, use_parallel_mode=false, operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x1e5ff50, execute_once=true) at execMain.c:1723#9 0x00000000006d5ae8 in standard_ExecutorRun (queryDesc=0x1da77e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:364#10 0x00000000006d5910 in ExecutorRun (queryDesc=0x1da77e8, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:307#11 0x00000000008c2206 in PortalRunSelect (portal=0x1df4608, forward=true, count=0, dest=0x1e5ff50) at pquery.c:932#12 0x00000000008c1ea4 in PortalRun (portal=0x1df4608, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x1e5ff50, altdest=0x1e5ff50, completionTag=0x7fffdbc3bc20 "") at pquery.c:773#13 0x00000000008bbf06 in exec_simple_query (query_string=0x1d85d78 "select 1+id from t_expr;") at postgres.c:1145#14 0x00000000008c0191 in PostgresMain (argc=1, argv=0x1db3cd8, dbname=0x1db3b40 "testdb", username=0x1db3b20 "xdb") at postgres.c:4182#15 0x000000000081e06c in BackendRun (port=0x1da7ae0) at postmaster.c:4361#16 0x000000000081d7df in BackendStartup (port=0x1da7ae0) at postmaster.c:4033#17 0x0000000000819bd9 in ServerLoop () at postmaster.c:1706---Type to continue, or q to quit---#18 0x000000000081948f in PostmasterMain (argc=1, argv=0x1d80a50) at postmaster.c:1379#19 0x0000000000742931 in main (argc=1, argv=0x1d80a50) at main.c:228
跟踪分析
进入ExecInterpExpr
Breakpoint 1, ExecInterpExpr (state=0x1e67678, econtext=0x1e672a8, isnull=0x7fffdbc3b897) at execExprInterp.c:402402 if (unlikely(state == NULL))(gdb) cContinuing.Breakpoint 1, ExecInterpExpr (state=0x1e67678, econtext=0x1e672a8, isnull=0x7fffdbc3b877) at execExprInterp.c:402402 if (unlikely(state == NULL))
获取步骤数组和相关的slot
(gdb) n409 op = state->steps;(gdb) 410 resultslot = state->resultslot;(gdb) 411 innerslot = econtext->ecxt_innertuple;(gdb) 412 outerslot = econtext->ecxt_outertuple;(gdb) p *econtext$21 = {type = T_ExprContext, ecxt_scantuple = 0x1e673a0, ecxt_innertuple = 0x0, ecxt_outertuple = 0x0, ecxt_per_query_memory = 0x1e66e60, ecxt_per_tuple_memory = 0x1e6d290, ecxt_param_exec_vals = 0x0, ecxt_param_list_info = 0x0, ecxt_aggvalues = 0x0, ecxt_aggnulls = 0x0, caseValue_datum = 0, caseValue_isNull = true, domainValue_datum = 0, domainValue_isNull = true, ecxt_estate = 0x1e66f78, ecxt_callbacks = 0x0}(gdb) n413 scanslot = econtext->ecxt_scantuple;(gdb) (gdb) p *scanslot$22 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, tts_tuple = 0x1e683f0, tts_tupleDescriptor = 0x7fa4f307fab8, tts_mcxt = 0x1e66e60, tts_buffer = 98, tts_nvalid = 0, tts_values = 0x1e67400, tts_isnull = 0x1e67408, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = { bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}
进行分发
416 EEO_DISPATCH();
首先是EEOP_SCAN_FETCHSOME(STEP 1),获取结果slot
(gdb) n443 slot_getsomeattrs(scanslot, op->d.fetch.last_var);(gdb) n445 EEO_NEXT();(gdb)
跳转到下一个步骤EEOP_SCAN_VAR(STEP 2),获取扫描获得的id列值
480 int attnum = op->d.var.attnum;(gdb) 484 Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);(gdb) 485 *op->resvalue = scanslot->tts_values[attnum];(gdb) 486 *op->resnull = scanslot->tts_isnull[attnum];(gdb) 488 EEO_NEXT();(gdb)
跳转到下一个步骤EEOP_FUNCEXPR_STRICT(STEP 3)
首先获取函数调用信息(参数),然后根据参数个数循环判断,接着调用实际的函数(fn_addr指向的函数),调用后赋值给联合体d.
663 FunctionCallInfo fcinfo = op->d.func.fcinfo_data;(gdb) 664 bool *argnull = fcinfo->argnull;(gdb) p fcinfo$23 = (FunctionCallInfo) 0x1e67b78(gdb) p *fcinfo$24 = {flinfo = 0x1e67b20, context = 0x0, resultinfo = 0x0, fncollation = 0, isnull = false, nargs = 2, arg = {1, 1, 0}, argnull = {false }}(gdb) p *fcinfo->flinfo$25 = {fn_addr = 0x93d60c , fn_oid = 177, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x1e66e60, fn_expr = 0x1d87bf8}(gdb) p *fcinfo->flinfo->fn_expr$26 = {type = T_OpExpr}(gdb) n669 for (argno = 0; argno < op->d.func.nargs; argno++)(gdb) 671 if (argnull[argno])(gdb) 669 for (argno = 0; argno < op->d.func.nargs; argno++)(gdb) 671 if (argnull[argno])(gdb) 669 for (argno = 0; argno < op->d.func.nargs; argno++)(gdb) 677 fcinfo->isnull = false;(gdb) 678 d = op->d.func.fn_addr(fcinfo);(gdb) 679 *op->resvalue = d;(gdb) 680 *op->resnull = fcinfo->isnull;(gdb) 683 EEO_NEXT();(gdb)
跳转到下一个步骤EEOP_ASSIGN_TMP(STEP 4),获取结果列所在的编号,赋值
603 int resultnum = op->d.assign_tmp.resultnum;(gdb) 605 resultslot->tts_values[resultnum] = state->resvalue;(gdb) 606 resultslot->tts_isnull[resultnum] = state->resnull;(gdb) p state->resvalue$27 = 2(gdb) n608 EEO_NEXT();
跳转到下一个步骤,EEO_DONE(STEP 5)
(gdb) 423 goto out;
退出,返回结果值
(gdb) n1764 *isnull = state->resnull;(gdb) 1765 return state->resvalue;(gdb) 1766 }(gdb) ExecInterpExprStillValid (state=0x1e67678, econtext=0x1e672a8, isNull=0x7fffdbc3b877) at execExprInterp.c:17871787 }(gdb) cContinuing.
以上是"PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!