怎么使用PostgreSQL的ExprEvalStep

开发技术 作者:iii 2024-04-06 03:35:01
本篇内容介绍了“怎么使用PostgreSQL的ExprEvalStep”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理...

本篇内容介绍了“怎么使用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”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注捷杰建站网站,小编将为大家输出更多高质量的实用文章!

原创声明
本站部分文章基于互联网的整理,我们会把真正“有用/优质”的文章整理提供给各位开发者。本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
本文链接:http://www.jiecseo.com/news/show_6372.html
postgresql ExprEvalStep