共计 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 小编将为大家输出更多高质量的实用文章!