这篇文章主要介绍PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!
表达式列是指除关系定义中的系统列/定义列之外的其他投影列.比如:
testdb=# create table t_expr(id int);
CREATE TABLE
testdb=# insert into t_expr values(1);
INSERT 0 1
testdb=# insert into t_expr values(2);
INSERT 0 1
testdb=# alter table t_expr add column c2 varchar(20);
ALTER TABLE
testdb=# insert into t_expr(id,c2) select x,'c2'||x from generate_series(3,100000) as x;
INSERT 0 99998
testdb=# 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 Datum
ExecInterpExpr(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 TABLE
testdb=# 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 <SeqNext>, recheckMtd=0x710474 <SeqRecheck>)
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 <return> to continue, or q <return> 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:402
402 if (unlikely(state == NULL))
(gdb) c
Continuing.
Breakpoint 1, ExecInterpExpr (state=0x1e67678, econtext=0x1e672a8, isnull=0x7fffdbc3b877) at execExprInterp.c:402
402 if (unlikely(state == NULL))
获取步骤数组和相关的slot
(gdb) n
409 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) n
413 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) n
443 slot_getsomeattrs(scanslot, op->d.fetch.last_var);
(gdb) n
445 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 <repeats 98 times>}, argnull = {false <repeats 100 times>}}
(gdb) p *fcinfo->flinfo
$25 = {fn_addr = 0x93d60c <int4pl>, 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) n
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)
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) n
608 EEO_NEXT();
跳转到下一个步骤,EEO_DONE(STEP 5)
(gdb)
423 goto out;
退出,返回结果值
(gdb) n
1764 *isnull = state->resnull;
(gdb)
1765 return state->resvalue;
(gdb)
1766 }
(gdb)
ExecInterpExprStillValid (state=0x1e67678, econtext=0x1e672a8, isNull=0x7fffdbc3b877) at execExprInterp.c:1787
1787 }
(gdb) c
Continuing.
以上是“PostgreSQL如何解析查询语句中的表达式列并计算得出该列的值”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:http://blog.itpub.net/6906/viewspace-2640022/