共計 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 小編將為大家輸出更多高質量的實用文章!