本篇内容介绍了“怎么使用PostgreSQL的ExprEvalStep”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
ExprEvalStep
表达式解析步骤结构体
typedef struct ExprEvalStep
{
intptr_t opcode;
//存储该步骤的结果
Datum *resvalue;
bool *resnull;
union
{
//用于EEOP_INNER/OUTER/SCAN_FETCHSOME
struct
{
//获取到的属性编号
int last_var;
TupleDesc known_desc;
} fetch;
struct
{
//attnum是常规VAR的attr number - 1
//对于SYSVAR,该值是常规的attr number
int attnum;
Oid vartype;
} var;
struct
{
Var *var;
bool first;
bool slow;
TupleDesc tupdesc;
JunkFilter *junkFilter;
} wholerow;
struct
{
int resultnum;
int attnum;
} assign_var;
struct
{
int resultnum;
} assign_tmp;
struct
{
Datum value;
bool isnull;
} constval;
//对于EEOP_FUNCEXPR_* / NULLIF / DISTINCT
struct
{
//函数的检索数据
FmgrInfo *finfo;
//参数信息等
FunctionCallInfo fcinfo_data;
//无需额外的指向,更快速的访问
PGFunction fn_addr;
int nargs;
} func;
struct
{
bool *anynull;
int jumpdone;
} boolexpr;
struct
{
int jumpdone;
} qualexpr;
struct
{
int jumpdone;
} jump;
struct
{
TupleDesc argdesc;
} nulltest_row;
struct
{
int paramid;
Oid paramtype;
} param;
struct
{
ExecEvalSubroutine paramfunc;
void *paramarg;
int paramid;
Oid paramtype;
} cparam;
struct
{
Datum *value;
bool *isnull;
} casetest;
struct
{
Datum *value;
bool *isnull;
} make_readonly;
struct
{
FmgrInfo *finfo_out;
FunctionCallInfo fcinfo_data_out;
FmgrInfo *finfo_in;
FunctionCallInfo fcinfo_data_in;
} iocoerce;
struct
{
SQLValueFunction *svf;
} sqlvaluefunction;
//EEOP_NEXTVALUEEXPR
struct
{
Oid seqid;
Oid seqtypid;
} nextvalueexpr;
struct
{
Datum *elemvalues;
bool *elemnulls;
int nelems;
Oid elemtype;
int16 elemlength;
bool elembyval;
char elemalign;
bool multidims;
} arrayexpr;
struct
{
ExprState *elemexprstate;
Oid resultelemtype;
struct ArrayMapState *amstate;
} arraycoerce;
struct
{
TupleDesc tupdesc;
Datum *elemvalues;
bool *elemnulls;
} row;
struct
{
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
PGFunction fn_addr;
int jumpnull;
int jumpdone;
} rowcompare_step;
struct
{
RowCompareType rctype;
} rowcompare_final;
struct
{
Datum *values;
bool *nulls;
int nelems;
MinMaxOp op;
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
} minmax;
struct
{
AttrNumber fieldnum;
Oid resulttype;
TupleDesc argdesc;
} fieldselect;
struct
{
FieldStore *fstore;
TupleDesc *argdesc;
Datum *values;
bool *nulls;
int ncolumns;
} fieldstore;
struct
{
struct ArrayRefState *state;
int off;
bool isupper;
int jumpdone;
} arrayref_subscript;
struct
{
struct ArrayRefState *state;
} arrayref;
struct
{
char *constraintname;
Datum *checkvalue;
bool *checknull;
Oid resulttype;
} domaincheck;
struct
{
ConvertRowtypeExpr *convert;
TupleDesc indesc;
TupleDesc outdesc;
TupleConversionMap *map;
bool initialized;
} convert_rowtype;
struct
{
Oid element_type;
bool useOr;
int16 typlen;
bool typbyval;
char typalign;
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
PGFunction fn_addr;
} scalararrayop;
struct
{
XmlExpr *xexpr;
Datum *named_argvalue;
bool *named_argnull;
Datum *argvalue;
bool *argnull;
} xmlexpr;
struct
{
AggrefExprState *astate;
} aggref;
struct
{
AggState *parent;
List *clauses;
} grouping_func;
struct
{
WindowFuncExprState *wfstate;
} window_func;
struct
{
SubPlanState *sstate;
} subplan;
struct
{
AlternativeSubPlanState *asstate;
} alternative_subplan;
struct
{
AggState *aggstate;
FunctionCallInfo fcinfo_data;
int jumpnull;
} agg_deserialize;
struct
{
bool *nulls;
int nargs;
int jumpnull;
} agg_strict_input_check;
struct
{
AggState *aggstate;
AggStatePerTrans pertrans;
ExprContext *aggcontext;
int setno;
int transno;
int setoff;
int jumpnull;
} agg_init_trans;
struct
{
AggState *aggstate;
int setno;
int transno;
int setoff;
int jumpnull;
} agg_strict_trans_check;
struct
{
AggState *aggstate;
AggStatePerTrans pertrans;
ExprContext *aggcontext;
int setno;
int transno;
int setoff;
} agg_trans;
} d;
} ExprEvalStep;
FmgrInfo
在函数通过fmgr调用前,该结构体持有系统目录(字典)信息,用于检索相关信息.
如果相同的函数将被调用多次,检索只需要完成一次即可,该结构体会缓存多次使用.
typedef struct FmgrInfo
{
//指向函数或者将被调用的处理器
PGFunction fn_addr;
//函数的oid
Oid fn_oid;
//输入参数的个数,0..FUNC_MAX_ARGS
short fn_nargs;
//函数是否严格(strict),输入NULL,输出NULL
bool fn_strict;
//函数是否返回集合
bool fn_retset;
//如track_functions > this,则收集统计信息
unsigned char fn_stats;
//handler使用的额外空间
void *fn_extra;
//存储fn_extra的内存上下文
MemoryContext fn_mcxt;
//表达式解析树,或者为NULL
fmNodePtr fn_expr;
} FmgrInfo;
typedef struct Node *fmNodePtr;
FunctionCallInfoData
该结构体存储了实际传递给fmgr-called函数的参数
typedef struct FunctionCallInfoData
{
//指向该调用的检索信息
FmgrInfo *flinfo;
//调用上下文
fmNodePtr context;
//传递或返回关于结果的特别信息
fmNodePtr resultinfo;
//函数的collation
Oid fncollation;
#define FIELDNO_FUNCTIONCALLINFODATA_ISNULL 4
//如结果为NULL,则必须设置为T
bool isnull;
//实际传递的参数个数
short nargs;
#define FIELDNO_FUNCTIONCALLINFODATA_ARG 6
//传递给函数的参数
Datum arg[FUNC_MAX_ARGS];
#define FIELDNO_FUNCTIONCALLINFODATA_ARGNULL 7
//如arg[i]为NULL,则对应的值为T
bool argnull[FUNC_MAX_ARGS];
} FunctionCallInfoData;
typedef struct FunctionCallInfoData *FunctionCallInfo;
二、源码解读
ExecInitFunc函数为类函数表达式的执行配置步骤,在*state的steps中追加参数解析步骤,同时设置*scratch以便可以push到步骤中.
其主要逻辑如下:
1.检查调用函数的权限
2.检查nargs是否合法
3.为该调用分配函数检索数据和参数空间
4.配置主要的fmgr检索信息
5.初始化函数调用参数结构体
6.解析参数直接存储到fcinfo结构体中
7.根据函数的严格性和统计级别插入相应的opcode
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;
//检查调用函数的权限
aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(funcid));
InvokeFunctionExecuteHook(funcid);
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)));
//为该调用分配函数检索数据和参数空间
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;
//配置主要的fmgr检索信息
fmgr_info(funcid, flinfo);
fmgr_info_set_expr((Node *) node, flinfo);
//初始化函数调用参数结构体
InitFunctionCallInfoData(*fcinfo, flinfo,
nargs, inputcollid, NULL, NULL);
//保留此信息的额外副本,以便在运行时保存间接信息
scratch->d.func.fn_addr = flinfo->fn_addr;
scratch->d.func.nargs = nargs;
//只支持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));
//解析参数直接存储到fcinfo结构体中
argno = 0;
foreach(lc, args)
{
//遍历参数
Expr *arg = (Expr *) lfirst(lc);
if (IsA(arg, Const))
{
//常量
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++;
}
//根据函数的严格性和统计级别插入相应的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”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!