怎么使用PostgreSQL的ExprEvalStep

79次阅读
没有评论

共计 25676 个字符,预计需要花费 65 分钟才能阅读完成。

本篇内容介绍了“怎么使用 PostgreSQL 的 ExprEvalStep”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

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;

FmgrInfo
在函数通过 fmgr 调用前, 该结构体持有系统目录 (字典) 信息, 用于检索相关信息.
如果相同的函数将被调用多次, 检索只需要完成一次即可, 该结构体会缓存多次使用.

/*
 * This struct holds the system-catalog information that must be looked up
 * before a function can be called through fmgr. If the same function is
 * to be called multiple times, the lookup need be done only once and the
 * info struct saved for re-use.
 *  在函数通过 fmgr 调用前, 该结构体持有系统目录 (字典) 信息, 用于检索相关信息.
 *  如果相同的函数将被调用多次, 检索只需要完成一次即可, 该结构体会缓存多次使用.
 *
 * Note that fn_expr really is parse-time-determined information about the
 * arguments, rather than about the function itself. But it s convenient
 * to store it here rather than in FunctionCallInfoData, where it might more
 * logically belong.
 *  注意,fn_expr 实际上是关于参数的解析时确定的信息, 而不是函数自身.
 *  但 fn_expr 在这里存储而不是 FunctionCallInfoData 中存储, 因为从逻辑上来说, 它就应该属于那.
 *
 * fn_extra is available for use by the called function; all other fields
 * should be treated as read-only after the struct is created.
 * fn_extra 可用于被调用函数的使用; 所有其他字段应该在结构体创建后被处理为只读.
 */
typedef struct FmgrInfo
 // 指向函数或者将被调用的处理器
 PGFunction fn_addr; /* pointer to function or handler to be called */
 // 函数的 oid
 Oid fn_oid; /* OID of function (NOT of handler, if any) */
 // 输入参数的个数,0..FUNC_MAX_ARGS
 short fn_nargs; /* number of input args (0..FUNC_MAX_ARGS) */
 // 函数是否严格(strict), 输入 NULL, 输出 NULL
 bool fn_strict; /* function is  strict  (NULL in =  NULL out) */
 // 函数是否返回集合
 bool fn_retset; /* function returns a set */
 // 如 track_functions   this, 则收集统计信息
 unsigned char fn_stats; /* collect stats if track_functions   this */
 //handler 使用的额外空间
 void *fn_extra; /* extra space for use by handler */
 // 存储 fn_extra 的内存上下文
 MemoryContext fn_mcxt; /* memory context to store fn_extra in */
 // 表达式解析树, 或者为 NULL
 fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
} FmgrInfo;
typedef struct Node *fmNodePtr;

FunctionCallInfoData
该结构体存储了实际传递给 fmgr-called 函数的参数

/*
 * This struct is the data actually passed to an fmgr-called function.
 *  该结构体存储了实际传递给 fmgr-called 函数的参数
 *
 * The called function is expected to set isnull, and possibly resultinfo or
 * fields in whatever resultinfo points to. It should not change any other
 * fields. (In particular, scribbling on the argument arrays is a bad idea,
 * since some callers assume they can re-call with the same arguments.)
 *  被调用的函数期望设置 isnull 以及可能的 resultinfo 或者 resultinfo 指向的域字段.
 *  不应该改变其他字段.
 * (特别的, 在参数数组上乱写是个坏主意, 因为某些调用者假定它们可以使用相同的参数重复调用)
 */
typedef struct FunctionCallInfoData
 // 指向该调用的检索信息
 FmgrInfo *flinfo; /* ptr to lookup info used for this call */
 // 调用上下文
 fmNodePtr context; /* pass info about context of call */
 // 传递或返回关于结果的特别信息
 fmNodePtr resultinfo; /* pass or return extra info about result */
 // 函数的 collation
 Oid fncollation; /* collation for function to use */
#define FIELDNO_FUNCTIONCALLINFODATA_ISNULL 4
 // 如结果为 NULL, 则必须设置为 T
 bool isnull; /* function must set true if result is NULL */
 // 实际传递的参数个数
 short nargs; /* # arguments actually passed */
#define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
 // 传递给函数的参数
 Datum arg[FUNC_MAX_ARGS]; /* Arguments passed to function */
#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
 // 如 arg[i]为 NULL, 则对应的值为 T
 bool argnull[FUNC_MAX_ARGS]; /* T if arg[i] is actually NULL */
} FunctionCallInfoData;
 * All functions that can be called directly by fmgr must have this signature.
 * (Other functions can be called by using a handler that does have this
 * signature.)
 *  所有函数可以通过 fmgr 直接调用, 但必须持有签名.
 * (其他函数可通过使用 handler 的方式调用, 也有此签名)
 */
typedef struct FunctionCallInfoData *FunctionCallInfo;

二、源码解读

ExecInitFunc 函数为类函数表达式的执行配置步骤, 在 *state 的 steps 中追加参数解析步骤, 同时设置 *scratch 以便可以 push 到步骤中.
其主要逻辑如下:
1. 检查调用函数的权限
2. 检查 nargs 是否合法
3. 为该调用分配函数检索数据和参数空间
4. 配置主要的 fmgr 检索信息
5. 初始化函数调用参数结构体
6. 解析参数直接存储到 fcinfo 结构体中
7. 根据函数的严格性和统计级别插入相应的 opcode

/*
 * Perform setup necessary for the evaluation of a function-like expression,
 * appending argument evaluation steps to the steps list in *state, and
 * setting up *scratch so it is ready to be pushed.
 *  为类函数表达式的执行配置步骤, 在 *state 的 steps 中追加参数解析步骤,
 *  同时设置 *scratch 以便可以 push 到步骤中.
 *
 * *scratch is not pushed here, so that callers may override the opcode,
 * which is useful for function-like cases like DISTINCT.
 * *scratch 不在这里 push, 以便调用者可以覆盖 opcode, 这在 DISTINCT 这类操作时很有用.
 */
static void
ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
 Oid inputcollid, ExprState *state)
 int nargs = list_length(args);
 AclResult aclresult;
 FmgrInfo *flinfo;
 FunctionCallInfo fcinfo;
 int argno;
 ListCell *lc;
 /* Check permission to call function */
 // 检查调用函数的权限
 aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
 if (aclresult != ACLCHECK_OK)
 aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(funcid));
 InvokeFunctionExecuteHook(funcid);
 /*
 * Safety check on nargs. Under normal circumstances this should never
 * fail, as parser should check sooner. But possibly it might fail if
 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
 * declared in pg_proc?
 *  检查 nargs. 在通常的环境下, 这不会出现异常.
 *  但如果服务器使用 FUNC_MAX_ARGS 宏定义比某些在 pg_proc 中定义的函数要小时会出现问题.
 */
 if (nargs   FUNC_MAX_ARGS)
 ereport(ERROR,
 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
 errmsg_plural( cannot pass more than %d argument to a function ,
  cannot pass more than %d arguments to a function ,
 FUNC_MAX_ARGS,
 FUNC_MAX_ARGS)));
 /* Allocate function lookup data and parameter workspace for this call */
 // 为该调用分配函数检索数据和参数空间
 scratch- d.func.finfo = palloc0(sizeof(FmgrInfo));
 scratch- d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
 flinfo = scratch- d.func.finfo;
 fcinfo = scratch- d.func.fcinfo_data;
 /* Set up the primary fmgr lookup information */
 // 配置主要的 fmgr 检索信息
 fmgr_info(funcid, flinfo);
 fmgr_info_set_expr((Node *) node, flinfo);
 /* Initialize function call parameter structure too */
 // 初始化函数调用参数结构体
 InitFunctionCallInfoData(*fcinfo, flinfo,
 nargs, inputcollid, NULL, NULL);
 /* Keep extra copies of this info to save an indirection at runtime */
 // 保留此信息的额外副本, 以便在运行时保存间接信息
 scratch- d.func.fn_addr = flinfo- fn_addr;
 scratch- d.func.nargs = nargs;
 /* We only support non-set functions here */
 // 只支持 non-set 函数
 if (flinfo- fn_retset)
 ereport(ERROR,
 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 errmsg(set-valued function called in context that cannot accept a set),
 state- parent ?
 executor_errposition(state- parent- state,
 exprLocation((Node *) node)) : 0));
 /* Build code to evaluate arguments directly into the fcinfo struct */
 // 解析参数直接存储到 fcinfo 结构体中
 argno = 0;
 foreach(lc, args)
 {
 // 遍历参数
 Expr *arg = (Expr *) lfirst(lc);
 if (IsA(arg, Const))
 {
 // 常量
 /*
 * Don t evaluate const arguments every round; especially
 * interesting for constants in comparisons.
 *  不要每轮都对常量参数进行解析, 只需要关注在对比中感兴趣的常量即可.
 */
 Const *con = (Const *) arg;
 fcinfo- arg[argno] = con- constvalue;
 fcinfo- argnull[argno] = con- constisnull;
 }
 else
 {
 // 非常量, 则递归调用
 ExecInitExprRec(arg, state,
  fcinfo- arg[argno],  fcinfo- argnull[argno]);
 }
 argno++;
 }
 /* Insert appropriate opcode depending on strictness and stats level */
 // 根据函数的严格性和统计级别插入相应的 opcode
 if (pgstat_track_functions  = flinfo- fn_stats)
 { if (flinfo- fn_strict   nargs   0)
 scratch- opcode = EEOP_FUNCEXPR_STRICT;
 else
 scratch- opcode = EEOP_FUNCEXPR;
 }
 else
 { if (flinfo- fn_strict   nargs   0)
 scratch- opcode = EEOP_FUNCEXPR_STRICT_FUSAGE;
 else
 scratch- opcode = EEOP_FUNCEXPR_FUSAGE;
 }
}

三、跟踪分析

测试脚本

testdb=# select 1+id,c2 from t_expr where id   3;

设置断点, 跟踪

(gdb) b ExecInitFunc
Breakpoint 1 at 0x6c8c33: file execExpr.c, line 2160.
(gdb) c
Continuing.
Breakpoint 1, ExecInitFunc (scratch=0x7ffd862de730, node=0x2e675d8, args=0x2e67520, funcid=177, inputcollid=0, 
 state=0x2f228c0) at execExpr.c:2160
2160 int nargs = list_length(args);
(gdb) bt
#0 ExecInitFunc (scratch=0x7ffd862de730, node=0x2e675d8, args=0x2e67520, funcid=177, inputcollid=0, state=0x2f228c0)
 at execExpr.c:2160
#1 0x00000000006c6200 in ExecInitExprRec (node=0x2e675d8, state=0x2f228c0, resv=0x2f228c8, resnull=0x2f228c5)
 at execExpr.c:893
#2 0x00000000006c55bc in ExecBuildProjectionInfo (targetList=0x2f2a348, econtext=0x2f223a8, slot=0x2f22820, 
 parent=0x2f22190, inputDesc=0x7ff79b051ab8) at execExpr.c:452
#3 0x00000000006e60d5 in ExecAssignProjectionInfo (planstate=0x2f22190, inputDesc=0x7ff79b051ab8) at execUtils.c:468
#4 0x00000000006e613c in ExecConditionalAssignProjectionInfo (planstate=0x2f22190, inputDesc=0x7ff79b051ab8, varno=1)
 at execUtils.c:493
#5 0x00000000006e23f5 in ExecAssignScanProjectionInfo (node=0x2f22190) at execScan.c:240
#6 0x0000000000700afc in ExecInitIndexScan (node=0x2e425f0, estate=0x2f21f78, eflags=16) at nodeIndexscan.c:962
#7 0x00000000006e00cc in ExecInitNode (node=0x2e425f0, estate=0x2f21f78, eflags=16) at execProcnode.c:217
#8 0x00000000006d6abe in InitPlan (queryDesc=0x2e65688, eflags=16) at execMain.c:1046
#9 0x00000000006d58ad in standard_ExecutorStart (queryDesc=0x2e65688, eflags=16) at execMain.c:265
#10 0x00000000006d5649 in ExecutorStart (queryDesc=0x2e65688, eflags=0) at execMain.c:147
#11 0x00000000008c18d6 in PortalStart (portal=0x2eaf608, params=0x0, eflags=0, snapshot=0x0) at pquery.c:520
#12 0x00000000008bbe1b in exec_simple_query (query_string=0x2e40d78  select 1+id,c2 from t_expr where id   3;)
 at postgres.c:1106
#13 0x00000000008c0191 in PostgresMain (argc=1, argv=0x2e6ecb8, dbname=0x2e6eb20  testdb , username=0x2e3da98  xdb)
 at postgres.c:4182
#14 0x000000000081e06c in BackendRun (port=0x2e62ae0) at postmaster.c:4361
#15 0x000000000081d7df in BackendStartup (port=0x2e62ae0) at postmaster.c:4033
#16 0x0000000000819bd9 in ServerLoop () at postmaster.c:1706
#17 0x000000000081948f in PostmasterMain (argc=1, argv=0x2e3ba50) at postmaster.c:1379
#18 0x0000000000742931 in main (argc=1, argv=0x2e3ba50) at main.c:228
(gdb)

输入参数

(gdb) p *scratch --   步骤
$1 = {opcode = 0, resvalue = 0x2f228c8, resnull = 0x2f228c5, d = {fetch = {last_var = 0, known_desc = 0x0}, var = { attnum = 0, vartype = 0}, wholerow = {var = 0x0, first = false, slow = false, tupdesc = 0x0, junkFilter = 0x0}, 
 assign_var = {resultnum = 0, attnum = 0}, assign_tmp = {resultnum = 0}, constval = {value = 0, isnull = false}, func = { finfo = 0x0, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0}, boolexpr = {anynull = 0x0, jumpdone = 0}, qualexpr = { jumpdone = 0}, jump = {jumpdone = 0}, nulltest_row = {argdesc = 0x0}, param = {paramid = 0, paramtype = 0}, cparam = { paramfunc = 0x0, paramarg = 0x0, paramid = 0, paramtype = 0}, casetest = {value = 0x0, isnull = 0x0}, 
 make_readonly = {value = 0x0, isnull = 0x0}, iocoerce = {finfo_out = 0x0, fcinfo_data_out = 0x0, finfo_in = 0x0, 
 fcinfo_data_in = 0x0}, sqlvaluefunction = {svf = 0x0}, nextvalueexpr = {seqid = 0, seqtypid = 0}, arrayexpr = {
 elemvalues = 0x0, elemnulls = 0x0, nelems = 0, elemtype = 0, elemlength = 0, elembyval = false, elemalign = 0  \000 , 
 multidims = false}, arraycoerce = {elemexprstate = 0x0, resultelemtype = 0, amstate = 0x0}, row = {tupdesc = 0x0, 
 elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {finfo = 0x0, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0, 
 jumpdone = 0}, rowcompare_final = {rctype = 0}, minmax = {values = 0x0, nulls = 0x0, nelems = 0, op = IS_GREATEST, 
 finfo = 0x0, fcinfo_data = 0x0}, fieldselect = {fieldnum = 0, resulttype = 0, argdesc = 0x0}, fieldstore = { fstore = 0x0, argdesc = 0x0, values = 0x0, nulls = 0x0, ncolumns = 0}, arrayref_subscript = {state = 0x0, off = 0, 
 isupper = false, jumpdone = 0}, arrayref = {state = 0x0}, domaincheck = {constraintname = 0x0, checkvalue = 0x0, 
 checknull = 0x0, resulttype = 0}, convert_rowtype = {convert = 0x0, indesc = 0x0, outdesc = 0x0, map = 0x0, 
 initialized = false}, scalararrayop = {element_type = 0, useOr = false, typlen = 0, typbyval = false, 
 typalign = 0  \000 , finfo = 0x0, fcinfo_data = 0x0, fn_addr = 0x0}, xmlexpr = {xexpr = 0x0, named_argvalue = 0x0, 
 named_argnull = 0x0, argvalue = 0x0, argnull = 0x0}, aggref = {astate = 0x0}, grouping_func = {parent = 0x0, 
 clauses = 0x0}, window_func = {wfstate = 0x0}, subplan = {sstate = 0x0}, alternative_subplan = {asstate = 0x0}, 
 agg_deserialize = {aggstate = 0x0, fcinfo_data = 0x0, jumpnull = 0}, agg_strict_input_check = {nulls = 0x0, nargs = 0, 
 jumpnull = 0}, agg_init_trans = {aggstate = 0x0, pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, 
 setoff = 0, jumpnull = 0}, agg_strict_trans_check = {aggstate = 0x0, setno = 0, transno = 0, setoff = 0, 
 jumpnull = 0}, agg_trans = {aggstate = 0x0, pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0}}}
######################################### 
(gdb) p *node
$2 = {type = T_OpExpr}
(gdb) p *(OpExpr *)node --  OpExpr 节点
$3 = {xpr = {type = T_OpExpr}, opno = 551, opfuncid = 177, opresulttype = 23, opretset = false, opcollid = 0, 
 inputcollid = 0, args = 0x2e67520, location = 8}
testdb=# \x
Expanded display is on.
testdb=# select * from pg_proc where oid=177; --  opfuncid = 177 对应的系统 proc
-[ RECORD 1 ]---+-------
proname | int4pl
pronamespace | 11
proowner | 10
prolang | 12
procost | 1
prorows | 0
provariadic | 0
protransform | -
prokind | f
prosecdef | f
proleakproof | f
proisstrict | t
proretset | f
provolatile | i
proparallel | s
pronargs | 2
pronargdefaults | 0
prorettype | 23
proargtypes | 23 23
proallargtypes | 
proargmodes | 
proargnames | 
proargdefaults | 
protrftypes | 
prosrc | int4pl
probin | 
proconfig | 
proacl | 
#########################################
(gdb) p *args
$4 = {type = T_List, length = 2, head = 0x2e674f8, tail = 0x2e675b0}
#########################################
(gdb) p *state --  ExprState
$5 = {tag = {type = T_ExprState}, flags = 0  \000 , resnull = false, resvalue = 0, resultslot = 0x2f22820, 
 steps = 0x2f229b0, evalfunc = 0x0, expr = 0x2f2a348, evalfunc_private = 0x0, steps_len = 1, steps_alloc = 16, 
 parent = 0x2f22190, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0, 
 innermost_domainnull = 0x0}

1. 检查调用函数的权限

(gdb) n
2168 aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
(gdb) 
2169 if (aclresult != ACLCHECK_OK)
(gdb) 
2171 InvokeFunctionExecuteHook(funcid);
(gdb)

2. 检查 nargs 是否合法

(gdb) 
2179 if (nargs   FUNC_MAX_ARGS)
(gdb)

3. 为该调用分配函数检索数据和参数空间

(gdb) 
2188 scratch- d.func.finfo = palloc0(sizeof(FmgrInfo));
(gdb) 
2189 scratch- d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
(gdb)

4. 配置主要的 fmgr 检索信息

(gdb) 
2194 fmgr_info(funcid, flinfo);
(gdb) 
2195 fmgr_info_set_expr((Node *) node, flinfo);
(gdb) 
(gdb) p *flinfo
$6 = {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 = 0x2f21e60, fn_expr = 0x2e675d8}
(gdb) p *node
$7 = {type = T_OpExpr}
(gdb) p *(OpExpr *)node
$8 = {xpr = {type = T_OpExpr}, opno = 551, opfuncid = 177, opresulttype = 23, opretset = false, opcollid = 0, 
 inputcollid = 0, args = 0x2e67520, location = 8}
(gdb)

5. 初始化函数调用参数结构体

(gdb) 
2198 InitFunctionCallInfoData(*fcinfo, flinfo,
(gdb) n
2202 scratch- d.func.fn_addr = flinfo- fn_addr;
(gdb) 
2203 scratch- d.func.nargs = nargs;
(gdb) 
2206 if (flinfo- fn_retset)
(gdb) 
2215 argno = 0;
(gdb) 
2216 foreach(lc, args)
(gdb) p nargs
$9 = 2
(gdb) p flinfo- fn_addr
$10 = (PGFunction) 0x93d60c  int4pl 
(gdb) p *fcinfo
$11 = {flinfo = 0x2f226b0, context = 0x0, resultinfo = 0x0, fncollation = 0, isnull = false, nargs = 2, arg = { 0  repeats 100 times}, argnull = {false  repeats 100 times}}
(gdb) p scratch- d.func
$12 = {finfo = 0x2f226b0, fcinfo_data = 0x2f22dc8, fn_addr = 0x93d60c  int4pl , nargs = 2}
(gdb)

6. 循环解析参数 args 直接存储到 fcinfo 结构体中
第 1 个参数, 是常量 1

(gdb) n
2218 Expr *arg = (Expr *) lfirst(lc);
(gdb) 
2220 if (IsA(arg, Const))
(gdb) p *arg
$13 = {type = T_Const}
(gdb) p *(Const *)arg
$14 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 1, 
 constisnull = false, constbyval = true, location = 7}
(gdb) n
2226 Const *con = (Const *) arg;
(gdb) 
2228 fcinfo- arg[argno] = con- constvalue;
(gdb) 
2229 fcinfo- argnull[argno] = con- constisnull;
(gdb) 
2236 argno++;
(gdb)

第 2 个参数, 是 Var, 递归调用 ExecInitExprRec 解析

(gdb) 
2216 foreach(lc, args)
(gdb) 
2218 Expr *arg = (Expr *) lfirst(lc);
(gdb) 
2220 if (IsA(arg, Const))
(gdb) 
2233 ExecInitExprRec(arg, state,
(gdb) p *arg
$15 = {type = T_Var}
(gdb) p *(Var *)arg
$16 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 23, vartypmod = -1, varcollid = 0, varlevelsup = 0, 
 varnoold = 1, varoattno = 1, location = 9}
(gdb) n
2236 argno++;
(gdb) 
(gdb) n
2216 foreach(lc, args)
(gdb)

7. 根据函数的严格性和统计级别插入相应的 opcode

2240 if (pgstat_track_functions  = flinfo- fn_stats)
(gdb) n
2242 if (flinfo- fn_strict   nargs   0)
(gdb) 
2243 scratch- opcode = EEOP_FUNCEXPR_STRICT;
(gdb) p pgstat_track_functions
$17 = 0
(gdb) p flinfo- fn_stats
$18 = 2  \002 
(gdb) n

完成调用

2254 }
(gdb) 
ExecInitExprRec (node=0x2e675d8, state=0x2f228c0, resv=0x2f228c8, resnull=0x2f228c5) at execExpr.c:896
896 ExprEvalPushStep(state,  scratch);
(gdb)

“怎么使用 PostgreSQL 的 ExprEvalStep”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-24发表,共计25676字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)