本篇内容介绍了“怎么使用PostgreSQL的ExprEvalStep”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
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”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注捷杰建站网站,小编将为大家输出更多高质量的实用文章!
本站采用系统自动发货方式,付款后即出现下载入口,如有疑问请咨询在线客服!
售后时间:早10点 - 晚11:30点Copyright © 2024 jiecseo.com All rights reserved. 粤ICP备18085929号
欢迎光临【捷杰建站】,本站所有资源仅供学习与参考,禁止用于商业用途或从事违法行为!
技术营运:深圳市晟艺互动传媒有限公司