千家信息网

PostgreSQL如何构建表达式解析

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,这篇文章主要介绍PostgreSQL如何构建表达式解析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、数据结构EEO_XXX宏定义opcode分发器宏定义/* * Macro
千家信息网最后更新 2025年01月20日PostgreSQL如何构建表达式解析

这篇文章主要介绍PostgreSQL如何构建表达式解析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

一、数据结构

EEO_XXX宏定义
opcode分发器宏定义

/* * Macros for opcode dispatch. * opcode分发器宏定义 * * EEO_SWITCH - just hides the switch if not in use. * EEO_SWITCH - 如未使用,则隐藏switch *  * EEO_CASE - labels the implementation of named expression step type. * EEO_CASE - 标签化已命名的表达式步骤类型的实现 *  * EEO_DISPATCH - jump to the implementation of the step type for 'op'. * EEO_DISPATCH - 跳到'op'指定的步骤类型的实现 *  * EEO_OPCODE - compute opcode required by used expression evaluation method. *            - 通过请求的表达式解析方法计算opcode *  * EEO_NEXT - increment 'op' and jump to correct next step type. *          - 'op'++并跳转到下一个步骤类型 * * EEO_JUMP - jump to the specified step number within the current expression. * EEO_JUMP - 在当前表达式中跳转到指定的步骤编号 */#if defined(EEO_USE_COMPUTED_GOTO)//--------------- 定义了EEO_USE_COMPUTED_GOTO/* struct for jump target -> opcode lookup table *///跳转target -> opcode搜索表结构体typedef struct ExprEvalOpLookup{    const void *opcode;    ExprEvalOp  op;} ExprEvalOpLookup;/* to make dispatch_table accessible outside ExecInterpExpr() */static const void **dispatch_table = NULL;/* jump target -> opcode lookup table */static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST];#define EEO_SWITCH()#define EEO_CASE(name)      CASE_##name:#define EEO_DISPATCH()      goto *((void *) op->opcode)#define EEO_OPCODE(opcode)  ((intptr_t) dispatch_table[opcode])#else                           /* !EEO_USE_COMPUTED_GOTO *///--------------- 没有定义EEO_USE_COMPUTED_GOTO#define EEO_SWITCH()        starteval: switch ((ExprEvalOp) op->opcode)#define EEO_CASE(name)      case name:#define EEO_DISPATCH()      goto starteval#define EEO_OPCODE(opcode)  (opcode)#endif                          /* EEO_USE_COMPUTED_GOTO */#define EEO_NEXT() \    do { \        op++; \        EEO_DISPATCH(); \    } while (0)#define EEO_JUMP(stepno) \    do { \        op = &state->steps[stepno]; \        EEO_DISPATCH(); \    } while (0)

ExprState
解析表达式中运行期状态节点

/* Bits in ExprState->flags (see also execExpr.h for private flag bits): *//* expression is for use with ExecQual() */#define EEO_FLAG_IS_QUAL                    (1 << 0)typedef struct ExprState{    //节点tag    Node        tag;    //EEO_FLAG_IS_QUAL    uint8       flags;          /* bitmask of EEO_FLAG_* bits, see above */    /*     * Storage for result value of a scalar expression, or for individual     * column results within expressions built by ExecBuildProjectionInfo().     * 存储scalar expression表达式     *   和通过ExecBuildProjectionInfo()函数创建的expressions单列的结果.     */#define FIELDNO_EXPRSTATE_RESNULL 2    bool        resnull;#define FIELDNO_EXPRSTATE_RESVALUE 3    Datum       resvalue;    /*     * If projecting a tuple result, this slot holds the result; else NULL.     * 如果投影元组结果,该slot存储结果,或者为NULL.     */#define FIELDNO_EXPRSTATE_RESULTSLOT 4    TupleTableSlot *resultslot;    /*     * Instructions to compute expression's return value.     * 计算表达式返回结果的基础"架构"     */    struct ExprEvalStep *steps;    /*     * Function that actually evaluates the expression.  This can be set to     * different values depending on the complexity of the expression.     * 实际解析表达式的函数.     * 根据表达式的复杂程度,可以设置为不同的值.     */    ExprStateEvalFunc evalfunc;    /* original expression tree, for debugging only */    //原始的表达式树,仅用于debugging    Expr       *expr;    /* private state for an evalfunc */    //evalfunc的私有状态    void       *evalfunc_private;    /*     * XXX: following fields only needed during "compilation" (ExecInitExpr);     * could be thrown away afterwards.     * XXX: 接下来的字段在"compilation" (ExecInitExpr)期间需要,之后可被"扔掉".     */    //当前的步数    int         steps_len;      /* number of steps currently */    //steps数组已分配的长度    int         steps_alloc;    /* allocated length of steps array */    //父PlanState节点(如存在)    struct PlanState *parent;   /* parent PlanState node, if any */    //用于编译PARAM_EXTERN节点    ParamListInfo ext_params;   /* for compiling PARAM_EXTERN nodes */    //    Datum      *innermost_caseval;    bool       *innermost_casenull;    Datum      *innermost_domainval;    bool       *innermost_domainnull;} ExprState;

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;

ExprEvalOp
ExprEvalSteps的鉴频器,定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用.

/* * Discriminator for ExprEvalSteps. * ExprEvalSteps的鉴频器 * * Identifies the operation to be executed and which member in the * ExprEvalStep->d union is valid. * 定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用. * * The order of entries needs to be kept in sync with the dispatch_table[] * array in execExprInterp.c:ExecInterpExpr(). * 条目的排序需要与execExprInterp.c:ExecInterpExpr()中dispatch_table[]数组的元素保持一致 */typedef enum ExprEvalOp{    /* entire expression has been evaluated completely, return */    //整个表达式已被解析,返回    EEOP_DONE,    /* apply slot_getsomeattrs on corresponding tuple slot */    //在相应的元组slot上应用了slot_getsomeattrs方法    EEOP_INNER_FETCHSOME,    EEOP_OUTER_FETCHSOME,    EEOP_SCAN_FETCHSOME,    /* compute non-system Var value */    //计算非系统Var变量值    EEOP_INNER_VAR,    EEOP_OUTER_VAR,    EEOP_SCAN_VAR,    /* compute system Var value */    //计算系统Var变量值    EEOP_INNER_SYSVAR,    EEOP_OUTER_SYSVAR,    EEOP_SCAN_SYSVAR,    /* compute wholerow Var */    //计算整行Var    EEOP_WHOLEROW,    /*     * Compute non-system Var value, assign it into ExprState's resultslot.     * These are not used if a CheckVarSlotCompatibility() check would be     * needed.     * 计算非系统Var值,分配到ExprState's的resultslot字段中.     * 如果CheckVarSlotCompatibility()需要时,这些都不需要.     */    EEOP_ASSIGN_INNER_VAR,    EEOP_ASSIGN_OUTER_VAR,    EEOP_ASSIGN_SCAN_VAR,    /* assign ExprState's resvalue/resnull to a column of its resultslot */    //分配ExprState's resvalue/resnull到该列的resultslot中    EEOP_ASSIGN_TMP,    /* ditto, applying MakeExpandedObjectReadOnly() */    //同上,应用MakeExpandedObjectReadOnly()    EEOP_ASSIGN_TMP_MAKE_RO,    /* evaluate Const value */    //解析常量值    EEOP_CONST,    /*     * Evaluate function call (including OpExprs etc).  For speed, we     * distinguish in the opcode whether the function is strict and/or     * requires usage stats tracking.     * 解析函数调用(包括OpExprs等等).     * 出于性能的考虑,需要区分opcode是strict函数还是非strict函数,以及是否需要统计跟踪.     */    EEOP_FUNCEXPR,    EEOP_FUNCEXPR_STRICT,    EEOP_FUNCEXPR_FUSAGE,    EEOP_FUNCEXPR_STRICT_FUSAGE,    /*     * Evaluate boolean AND expression, one step per subexpression. FIRST/LAST     * subexpressions are special-cased for performance.  Since AND always has     * at least two subexpressions, FIRST and LAST never apply to the same     * subexpression.     * 解析布尔AND表达式,每一个子表达式一个步骤.     * FIRST/LAST子表达式是性能上的特例.     * 由于AND通常至少有两个子表达式,FIRST和LAST永远都不会应用在同一个子表达式上.     */    EEOP_BOOL_AND_STEP_FIRST,    EEOP_BOOL_AND_STEP,    EEOP_BOOL_AND_STEP_LAST,    /* similarly for boolean OR expression */    //与布尔OR表达式类似    EEOP_BOOL_OR_STEP_FIRST,    EEOP_BOOL_OR_STEP,    EEOP_BOOL_OR_STEP_LAST,    /* evaluate boolean NOT expression */    //解析布尔NOT表达式    EEOP_BOOL_NOT_STEP,    /* simplified version of BOOL_AND_STEP for use by ExecQual() */    //用于ExecQual()中的BOOL_AND_STEP简化版本    EEOP_QUAL,    /* unconditional jump to another step */    //无条件跳转到另外一个步骤    EEOP_JUMP,    /* conditional jumps based on current result value */    //基于当前结果值的条件跳转    EEOP_JUMP_IF_NULL,    EEOP_JUMP_IF_NOT_NULL,    EEOP_JUMP_IF_NOT_TRUE,    /* perform NULL tests for scalar values */    //为scalar值执行NULL测试    EEOP_NULLTEST_ISNULL,    EEOP_NULLTEST_ISNOTNULL,    /* perform NULL tests for row values */    //为行值执行NULL测试    EEOP_NULLTEST_ROWISNULL,    EEOP_NULLTEST_ROWISNOTNULL,    /* evaluate a BooleanTest expression */    //解析BooleanTest表达式    EEOP_BOOLTEST_IS_TRUE,    EEOP_BOOLTEST_IS_NOT_TRUE,    EEOP_BOOLTEST_IS_FALSE,    EEOP_BOOLTEST_IS_NOT_FALSE,    /* evaluate PARAM_EXEC/EXTERN parameters */    //解析PARAM_EXEC/EXTERN参数    EEOP_PARAM_EXEC,    EEOP_PARAM_EXTERN,    EEOP_PARAM_CALLBACK,    /* return CaseTestExpr value */    //返回CaseTestExpr值    EEOP_CASE_TESTVAL,    /* apply MakeExpandedObjectReadOnly() to target value */    //对目标值应用MakeExpandedObjectReadOnly()    EEOP_MAKE_READONLY,    /* evaluate assorted special-purpose expression types */    //解析各种特殊用途的表达式类型    EEOP_IOCOERCE,    EEOP_DISTINCT,    EEOP_NOT_DISTINCT,    EEOP_NULLIF,    EEOP_SQLVALUEFUNCTION,    EEOP_CURRENTOFEXPR,    EEOP_NEXTVALUEEXPR,    EEOP_ARRAYEXPR,    EEOP_ARRAYCOERCE,    EEOP_ROW,    /*     * Compare two individual elements of each of two compared ROW()     * expressions.  Skip to ROWCOMPARE_FINAL if elements are not equal.     * 给出两个需要对比的ROW()表达式,两两比较行中的元素.     * 如果元素不相等,则跳转到ROWCOMPARE_FINAL     */    EEOP_ROWCOMPARE_STEP,    /* evaluate boolean value based on previous ROWCOMPARE_STEP operations */    //基于上一步的ROWCOMPARE_STEP操作解析布尔值    EEOP_ROWCOMPARE_FINAL,    /* evaluate GREATEST() or LEAST() */    //解析GREATEST()和LEAST()    EEOP_MINMAX,    /* evaluate FieldSelect expression */    //解析FieldSelect表达式    EEOP_FIELDSELECT,    /*     * Deform tuple before evaluating new values for individual fields in a     * FieldStore expression.     * 在解析FieldStore表达式中的独立列新值前重构元组     */    EEOP_FIELDSTORE_DEFORM,    /*     * Form the new tuple for a FieldStore expression.  Individual fields will     * have been evaluated into columns of the tuple deformed by the preceding     * DEFORM step.     * 为FieldStore表达式构成新元组.     * 单独的字段会解析到元组的列中(行已被上一个步骤EEOP_FIELDSTORE_DEFORM析构)     */    EEOP_FIELDSTORE_FORM,    /* Process an array subscript; short-circuit expression to NULL if NULL */    //处理数组子脚本.如为NULL则短路表达式为NULL    EEOP_ARRAYREF_SUBSCRIPT,    /*     * Compute old array element/slice when an ArrayRef assignment expression     * contains ArrayRef/FieldStore subexpressions.  Value is accessed using     * the CaseTest mechanism.     * 在ArrayRef分配表达式包含ArrayRef/FieldStore子表达式时计算旧的数组元素/片.     * 通过CaseTest机制访问Value     */    EEOP_ARRAYREF_OLD,    /* compute new value for ArrayRef assignment expression */    //为ArrayRef分配118    EEOP_ARRAYREF_ASSIGN,    /* compute element/slice for ArrayRef fetch expression */    //为ArrayRef提取表达式计算element/slice    EEOP_ARRAYREF_FETCH,    /* evaluate value for CoerceToDomainValue */    //为CoerceToDomainValue解析值    EEOP_DOMAIN_TESTVAL,    /* evaluate a domain's NOT NULL constraint */    //解析域 NOT NULL 约束    EEOP_DOMAIN_NOTNULL,    /* evaluate a single domain CHECK constraint */    //解析单个域CHECK约束    EEOP_DOMAIN_CHECK,    /* evaluate assorted special-purpose expression types */    //解析特殊目的的表达式类型    EEOP_CONVERT_ROWTYPE,    EEOP_SCALARARRAYOP,    EEOP_XMLEXPR,    EEOP_AGGREF,    EEOP_GROUPING_FUNC,    EEOP_WINDOW_FUNC,    EEOP_SUBPLAN,    EEOP_ALTERNATIVE_SUBPLAN,    /* aggregation related nodes */    //聚合相关节点    EEOP_AGG_STRICT_DESERIALIZE,    EEOP_AGG_DESERIALIZE,    EEOP_AGG_STRICT_INPUT_CHECK,    EEOP_AGG_INIT_TRANS,    EEOP_AGG_STRICT_TRANS_CHECK,    EEOP_AGG_PLAIN_TRANS_BYVAL,    EEOP_AGG_PLAIN_TRANS,    EEOP_AGG_ORDERED_TRANS_DATUM,    EEOP_AGG_ORDERED_TRANS_TUPLE,    /* non-existent operation, used e.g. to check array lengths */    //不存在的操作,比如用于检测数组长度    EEOP_LAST} ExprEvalOp;

二、源码解读

ExecInitIndexScan
初始化Index Scan运行期状态信息,调用ExecAssignScanProjectionInfo->…->ExecBuildProjectionInfo函数构建投影信息.

/* ---------------------------------------------------------------- *      ExecInitIndexScan * *      Initializes the index scan's state information, creates *      scan keys, and opens the base and index relations. * *      Note: index scans have 2 sets of state information because *            we have to keep track of the base relation and the *            index relation. * ---------------------------------------------------------------- */IndexScanState *ExecInitIndexScan(IndexScan *node, EState *estate, int eflags){    ...     /*     * Initialize result slot, type and projection.     * 初始化结果slot,类型和投影     */    ExecInitResultTupleSlotTL(estate, &indexstate->ss.ps);    ExecAssignScanProjectionInfo(&indexstate->ss);    ...}

ExecBuildProjectionInfo
为给定的econtext中的tlist构建ProjectionInfo,并把结果存储在tuple slot中.(调用者必须确保元组slot与此tlist匹配)
其主要逻辑是:
1.初始化
2.如需要,插入EEOP*_FETCHSOME步骤(调用ExecInitExprSlots)
3.遍历targetList,处理targetList(目标列)中的每一个列
3.1.对于"安全"Var则只需要生成EEOP_ASSIGN*_VAR步骤
3.2对于非"安全"VAr,使用常规办法处理列表达式,调用ExecInitExprRec函数处理,并通过ExprEvalPushStep压步骤
4.压入EEOP_DONE步骤

/*
* ExecAssignScanProjectionInfo
* Set up projection info for a scan node, if necessary.
* ExecAssignScanProjectionInfo
* 为扫描节点配置投影信息.
*
* We can avoid a projection step if the requested tlist exactly matches
* the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
* Note that this case occurs not only for simple "SELECT * FROM ...", but
* also in most cases where there are joins or other processing nodes above
* the scan node, because the planner will preferentially generate a matching
* tlist.
* 如果请求的tlist刚好与潜在的tuple类型相匹配,则可以避免投影这一步骤.
* 因此,只需要简单的把ps_ProjInfo设置为NULL即可.
* 注意这种情况不单在"SELECT * FROM ..."时会出现,
* 而且在存在连接或者其他处理节点在扫描节点的上层时也会发生,
* 因为规划器会优先生成匹配的tlist.
*
* The scan slot's descriptor must have been set already.
* 扫描slot的描述符必须已设置.
*/
void
ExecAssignScanProjectionInfo(ScanState *node)
{
//扫描节点
Scan *scan = (Scan *) node->ps.plan;
//元组描述符
TupleDesc tupdesc = node->ss_ScanTupleSlot->tts_tupleDescriptor;
//执行ExecConditionalAssignProjectionInfo
ExecConditionalAssignProjectionInfo(&node->ps, tupdesc, scan->scanrelid);
}
/* ----------------
* ExecConditionalAssignProjectionInfo
*
* as ExecAssignProjectionInfo, but store NULL rather than building projection
* info if no projection is required
* 与ExecAssignProjectionInfo类似,但在不需要投影操作时只需要存储NULL而不是构建投影信息
* ----------------
*/
void
ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
Index varno)
{
//正好匹配元组类型,则设计为NULL
if (tlist_matches_tupdesc(planstate,
planstate->plan->targetlist,
varno,
inputDesc))
planstate->ps_ProjInfo = NULL;
else
//否则执行ExecAssignProjectionInfo
ExecAssignProjectionInfo(planstate, inputDesc);
}
/* ----------------
* ExecAssignProjectionInfo
*
* forms the projection information from the node's targetlist
* 通过节点的targetlist构造投影信息.
*
* Notes for inputDesc are same as for ExecBuildProjectionInfo: supply it
* for a relation-scan node, can pass NULL for upper-level nodes
* 注意inputDesc与ExecBuildProjectionInfo的一样:
* 为relation-scan节点提供该描述符,可能为高层的节点传递NULL值
* ----------------
*/
void
ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc)
{
//直接调用ExecBuildProjectionInfo
planstate->ps_ProjInfo =
ExecBuildProjectionInfo(planstate->plan->targetlist,
planstate->ps_ExprContext,
planstate->ps_ResultTupleSlot,
planstate,
inputDesc);
}
/*
* ExecBuildProjectionInfo
*
* Build a ProjectionInfo node for evaluating the given tlist in the given
* econtext, and storing the result into the tuple slot. (Caller must have
* ensured that tuple slot has a descriptor matching the tlist!)
* 为给定的econtext中的tlist构建ProjectionInfo,并把结果存储在tuple slot中.
* (调用者必须确保元组slot与此tlist匹配)
*
* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.
* inputDesc可以为NULL,但如果不是,检查tlist中的简单Vars是否与描述符匹配.
* 对于relation-scan节点来说,提供inputDesc是十分重要的,
* 交叉检查在plan已生成的情况下relation没有出现变化.
* 而plan更高的层次,则不需要重新检查.
*
* This is implemented by internally building an ExprState that performs the
* whole projection in one go.
* 通过内部构造ExprState(在一轮执行中完成整个投影操作)来实现.
*
* Caution: before PG v10, the targetList was a list of ExprStates; now it
* should be the planner-created targetlist, since we do the compilation here.
* 注意:在PG v10前,targetlist是ExprState链表,现在tlist应该是planner创建的targetlist,
* 如不是则需报错.
*/
ProjectionInfo *
ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot,
PlanState *parent,
TupleDesc inputDesc)
{
//生成ProjectionInfo节点
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
//表达式状态节点
ExprState *state;
//表达式解析步骤
ExprEvalStep scratch = {0};
//临时变量
ListCell *lc;
//expr上下文
projInfo->pi_exprContext = econtext;
/* We embed ExprState into ProjectionInfo instead of doing extra palloc */
//集成ExprState到ProjectionInfo中,而不需要另外的palloc
projInfo->pi_state.tag.type = T_ExprState;
state = &projInfo->pi_state;
state->expr = (Expr *) targetList;
state->parent = parent;
state->ext_params = NULL;
state->resultslot = slot;
/* Insert EEOP_*_FETCHSOME steps as needed */
//如需要,插入EEOP_*_FETCHSOME步骤
ExecInitExprSlots(state, (Node *) targetList);
/* Now compile each tlist column */
//现在"编译"tlist中的每一个列
foreach(lc, targetList)
{
TargetEntry *tle = lfirst_node(TargetEntry, lc);
Var *variable = NULL;
AttrNumber attnum = 0;
bool isSafeVar = false;
/*
* If tlist expression is a safe non-system Var, use the fast-path
* ASSIGN_*_VAR opcodes. "Safe" means that we don't need to apply
* CheckVarSlotCompatibility() during plan startup. If a source slot
* was provided, we make the equivalent tests here; if a slot was not
* provided, we assume that no check is needed because we're dealing
* with a non-relation-scan-level expression.
* 如果tlist表达式是安全的非系统Var,使用快速路径ASSIGN_*_VAR opcodes.
* "Safe"的意思是在计划启动执行时我们不需要应用CheckVarSlotCompatibility().
* 如果提供了源slot,假定不需要再做检查,因为我们正在处理的是非关系扫描级别的表达式.
*/
if (tle->expr != NULL &&
IsA(tle->expr, Var) &&
((Var *) tle->expr)->varattno > 0)
{
/* Non-system Var, but how safe is it? */
//非系统Var,但有多安全呢?
variable = (Var *) tle->expr;
attnum = variable->varattno;
if (inputDesc == NULL)
//无法检查,假定是OK的.
isSafeVar = true; /* can't check, just assume OK */
else if (attnum <= inputDesc->natts)
{
//---- 属性编号小于输入的属性个数
Form_pg_attribute attr = TupleDescAttr(inputDesc, attnum - 1);
/*
* If user attribute is dropped or has a type mismatch, don't
* use ASSIGN_*_VAR. Instead let the normal expression
* machinery handle it (which'll possibly error out).
* 如果用户属性已被清除或者有类型不匹配,不要使用ASSIGN_*_VAR.
* 让常规的表达式匹配处理这周情况(可能会出现错误)
*/
if (!attr->attisdropped && variable->vartype == attr->atttypid)
{
isSafeVar = true;
}
}
}
if (isSafeVar)
{
//--- 如为安全的Var
/* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */
//Fast-path: 只需要生成EEOP_ASSIGN_*_VAR步骤即可
switch (variable->varno)
{
case INNER_VAR:
/* get the tuple from the inner node */
//内关系VAR,从inner节点获取元组
scratch.opcode = EEOP_ASSIGN_INNER_VAR;
break;
case OUTER_VAR:
/* get the tuple from the outer node */
//从外关系获取元组
scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
//默认: INDEX_VAR
default:
/* get the tuple from the relation being scanned */
//从正在扫描的关系中获取元组
scratch.opcode = EEOP_ASSIGN_SCAN_VAR;
break;
}
//EEOP_ASSIGN_*_VAR
scratch.d.assign_var.attnum = attnum - 1;
scratch.d.assign_var.resultnum = tle->resno - 1;
ExprEvalPushStep(state, &scratch);
}
else
{
/*
* Otherwise, compile the column expression normally.
* 否则的话,使用常规办法"编译"列表达式
*
* We can't tell the expression to evaluate directly into the
* result slot, as the result slot (and the exprstate for that
* matter) can change between executions. We instead evaluate
* into the ExprState's resvalue/resnull and then move.
* 我们不能够直接告知表达式进行解析到结果slot中,
* 因为结果slot(以及这种情况下的exprstate)在执行期间会出现变化.
* 我们只需要解析到ExprState's的resvalue/resnull中并进行移动即可.
*/
ExecInitExprRec(tle->expr, state,
&state->resvalue, &state->resnull);
/*
* Column might be referenced multiple times in upper nodes, so
* force value to R/O - but only if it could be an expanded datum.
* 列可能在高层节点被依赖多次,因此强制值为R/O - 只在可扩展datum时才这样处理
*/
//d.assign_tmp.resultnum/attnum
if (get_typlen(exprType((Node *) tle->expr)) == -1)
scratch.opcode = EEOP_ASSIGN_TMP_MAKE_RO;
else
scratch.opcode = EEOP_ASSIGN_TMP;
scratch.d.assign_tmp.resultnum = tle->resno - 1;
ExprEvalPushStep(state, &scratch);
}
}
scratch.opcode = EEOP_DONE;
ExprEvalPushStep(state, &scratch);
ExecReadyExpr(state);
//返回投影信息.
return projInfo;
}

三、跟踪分析

测试脚本

testdb=# select 1+id,c2 from t_expr where id < 3;

调用栈

(gdb) bt
#0 ExecBuildProjectionInfo (targetList=0x1cc7550, econtext=0x1c8f408, slot=0x1c8f710, parent=0x1c8f1f0,
inputDesc=0x7f8386bb6ab8) at execExpr.c:355
#1 0x00000000006e60d5 in ExecAssignProjectionInfo (planstate=0x1c8f1f0, inputDesc=0x7f8386bb6ab8) at execUtils.c:468
#2 0x00000000006e613c in ExecConditionalAssignProjectionInfo (planstate=0x1c8f1f0, inputDesc=0x7f8386bb6ab8, varno=1)
at execUtils.c:493
#3 0x00000000006e23f5 in ExecAssignScanProjectionInfo (node=0x1c8f1f0) at execScan.c:240
#4 0x0000000000700afc in ExecInitIndexScan (node=0x1ba8a18, estate=0x1c8efd8, eflags=16) at nodeIndexscan.c:962
#5 0x00000000006e00cc in ExecInitNode (node=0x1ba8a18, estate=0x1c8efd8, eflags=16) at execProcnode.c:217
#6 0x00000000006d6abe in InitPlan (queryDesc=0x1c94aa8, eflags=16) at execMain.c:1046
#7 0x00000000006d58ad in standard_ExecutorStart (queryDesc=0x1c94aa8, eflags=16) at execMain.c:265
#8 0x00000000006d5649 in ExecutorStart (queryDesc=0x1c94aa8, eflags=0) at execMain.c:147
#9 0x00000000008c18d6 in PortalStart (portal=0x1c15608, params=0x0, eflags=0, snapshot=0x0) at pquery.c:520
#10 0x00000000008bbe1b in exec_simple_query (query_string=0x1ba6d78 "select 1+id,c2 from t_expr where id < 3;")
at postgres.c:1106
#11 0x00000000008c0191 in PostgresMain (argc=1, argv=0x1bd4cb8, dbname=0x1bd4b20 "testdb", username=0x1ba3a98 "xdb")
at postgres.c:4182
#12 0x000000000081e06c in BackendRun (port=0x1bc8ae0) at postmaster.c:4361
#13 0x000000000081d7df in BackendStartup (port=0x1bc8ae0) at postmaster.c:4033
#14 0x0000000000819bd9 in ServerLoop () at postmaster.c:1706
#15 0x000000000081948f in PostmasterMain (argc=1, argv=0x1ba1a50) at postmaster.c:1379
#16 0x0000000000742931 in main (argc=1, argv=0x1ba1a50) at main.c:228

执行跟踪,进入函数ExecBuildProjectionInfo

(gdb) b ExecBuildProjectionInfo
Breakpoint 1 at 0x6c5377: file execExpr.c, line 355.
(gdb) c
Continuing.
Breakpoint 1, ExecBuildProjectionInfo (targetList=0x1c93498, econtext=0x1c883a8, slot=0x1c887c8, parent=0x1c88190,
inputDesc=0x1c884a0) at execExpr.c:355
355 ProjectionInfo *projInfo = makeNode(ProjectionInfo);
(gdb)

1.初始化

(gdb) n
357 ExprEvalStep scratch = {0};
(gdb)
360 projInfo->pi_exprContext = econtext;
(gdb)
362 projInfo->pi_state.tag.type = T_ExprState;
(gdb)
363 state = &projInfo->pi_state;
(gdb)
364 state->expr = (Expr *) targetList;
(gdb)
365 state->parent = parent;
(gdb)
366 state->ext_params = NULL;
(gdb)
368 state->resultslot = slot;
(gdb)

查看相关变量

(gdb) p *state
$1 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710, steps = 0x0,
evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 0, steps_alloc = 0, parent = 0x1c8f1f0,
ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}

目标列链表

(gdb) p targetList
$2 = (List *) 0x1cc7550
(gdb) p *targetList
$3 = {type = T_List, length = 2, head = 0x1cc7528, tail = 0x1cc75e0}

第一个元素是1+id,第二个元素是c2

(gdb) p *(TargetEntry *)targetList->head->data.ptr_value
$7 = {xpr = {type = T_TargetEntry}, expr = 0x1c9a930, resno = 1, resname = 0xbcf498 "?column?", ressortgroupref = 0,
resorigtbl = 0, resorigcol = 0, resjunk = false}
(gdb) p *(OpExpr *)((TargetEntry *)targetList->head->data.ptr_value)->expr
$9 = {xpr = {type = T_OpExpr}, opno = 551, opfuncid = 177, opresulttype = 23, opretset = false, opcollid = 0,
inputcollid = 0, args = 0x1c9a878, location = 8}
(gdb) p *(Node *)targetList->head->next->data.ptr_value
$10 = {type = T_TargetEntry}
(gdb) p *(TargetEntry *)targetList->head->next->data.ptr_value
$11 = {xpr = {type = T_TargetEntry}, expr = 0x1c9aa40, resno = 2, resname = 0x1ba7a40 "c2", ressortgroupref = 0,
resorigtbl = 237600, resorigcol = 2, resjunk = false}

2.如需要,插入EEOP_*_FETCHSOME步骤(调用ExecInitExprSlots)

(gdb) 
371 ExecInitExprSlots(state, (Node *) targetList);

第一个步骤,opcode = 3,即EEOP_SCAN_FETCHSOME

(gdb) n
374 foreach(lc, targetList)
(gdb) p *state
$13 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710,
steps = 0x1c8f868, evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 1, steps_alloc = 16,
parent = 0x1c8f1f0, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}
(gdb) p state->steps[0]
$14 = {opcode = 3, resvalue = 0x0, resnull = 0x0, d = {fetch = {last_var = 2, known_desc = 0x0}, var = {attnum = 2,
vartype = 0}, wholerow = {var = 0x2, first = false, slow = false, tupdesc = 0x0, junkFilter = 0x0}, assign_var = {
resultnum = 2, attnum = 0}, assign_tmp = {resultnum = 2}, constval = {value = 2, isnull = false}, func = {
finfo = 0x2, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0}, boolexpr = {anynull = 0x2, jumpdone = 0}, qualexpr = {
jumpdone = 2}, jump = {jumpdone = 2}, nulltest_row = {argdesc = 0x2}, param = {paramid = 2, paramtype = 0}, cparam = {
paramfunc = 0x2, paramarg = 0x0, paramid = 0, paramtype = 0}, casetest = {value = 0x2, isnull = 0x0},
make_readonly = {value = 0x2, isnull = 0x0}, iocoerce = {finfo_out = 0x2, fcinfo_data_out = 0x0, finfo_in = 0x0,
fcinfo_data_in = 0x0}, sqlvaluefunction = {svf = 0x2}, nextvalueexpr = {seqid = 2, seqtypid = 0}, arrayexpr = {
elemvalues = 0x2, elemnulls = 0x0, nelems = 0, elemtype = 0, elemlength = 0, elembyval = false, elemalign = 0 '\000',
multidims = false}, arraycoerce = {elemexprstate = 0x2, resultelemtype = 0, amstate = 0x0}, row = {tupdesc = 0x2,
elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {finfo = 0x2, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0,
jumpdone = 0}, rowcompare_final = {rctype = ROWCOMPARE_LE}, minmax = {values = 0x2, nulls = 0x0, nelems = 0,
op = IS_GREATEST, finfo = 0x0, fcinfo_data = 0x0}, fieldselect = {fieldnum = 2, resulttype = 0, argdesc = 0x0},
fieldstore = {fstore = 0x2, argdesc = 0x0, values = 0x0, nulls = 0x0, ncolumns = 0}, arrayref_subscript = {state = 0x2,
off = 0, isupper = false, jumpdone = 0}, arrayref = {state = 0x2}, domaincheck = {
constraintname = 0x2
, checkvalue = 0x0, checknull = 0x0, resulttype = 0},
convert_rowtype = {convert = 0x2, indesc = 0x0, outdesc = 0x0, map = 0x0, initialized = false}, scalararrayop = {
element_type = 2, useOr = false, typlen = 0, typbyval = false, typalign = 0 '\000', finfo = 0x0, fcinfo_data = 0x0,
fn_addr = 0x0}, xmlexpr = {xexpr = 0x2, named_argvalue = 0x0, named_argnull = 0x0, argvalue = 0x0, argnull = 0x0},
aggref = {astate = 0x2}, grouping_func = {parent = 0x2, clauses = 0x0}, window_func = {wfstate = 0x2}, subplan = {
sstate = 0x2}, alternative_subplan = {asstate = 0x2}, agg_deserialize = {aggstate = 0x2, fcinfo_data = 0x0,
jumpnull = 0}, agg_strict_input_check = {nulls = 0x2, nargs = 0, jumpnull = 0}, agg_init_trans = {aggstate = 0x2,
pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_strict_trans_check = {
aggstate = 0x2, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_trans = {aggstate = 0x2, pertrans = 0x0,
aggcontext = 0x0, setno = 0, transno = 0, setoff = 0}}}
(gdb)

3.遍历targetList,处理targetList(目标列)中的每一个列
3.1.对于"安全"Var则只需要生成EEOP
ASSIGN*_VAR步骤
3.2.对于非"安全"VAr,使用常规办法处理列表达式,调用ExecInitExprRec函数处理,并通过ExprEvalPushStep压步骤

(gdb) n
376 TargetEntry *tle = lfirst_node(TargetEntry, lc);
(gdb)
377 Var *variable = NULL;
(gdb)
378 AttrNumber attnum = 0;
(gdb)
379 bool isSafeVar = false;
(gdb)
389 if (tle->expr != NULL &&
(gdb)
390 IsA(tle->expr, Var) &&
(gdb)
389 if (tle->expr != NULL &&
(gdb)
415 if (isSafeVar)
(gdb) p *tle
$15 = {xpr = {type = T_TargetEntry}, expr = 0x1c9a930, resno = 1, resname = 0xbcf498 "?column?", ressortgroupref = 0,
resorigtbl = 0, resorigcol = 0, resjunk = false}
(gdb) n
452 ExecInitExprRec(tle->expr, state,
(gdb)

进入ExecInitExprRec,Node节点为OpExpr,执行ExprEvalPushStep压入步骤中

(gdb) step
ExecInitExprRec (node=0x1c9a930, state=0x1c8f7d8, resv=0x1c8f7e0, resnull=0x1c8f7dd) at execExpr.c:645
645 ExprEvalStep scratch = {0};
(gdb) n
648 check_stack_depth();
(gdb)
651 Assert(resv != NULL && resnull != NULL);
(gdb)
652 scratch.resvalue = resv;
(gdb)
653 scratch.resnull = resnull;
(gdb)
656 switch (nodeTag(node))
(gdb)
891 OpExpr *op = (OpExpr *) node;
(gdb) p *node
$16 = {type = T_OpExpr}
(gdb) n
893 ExecInitFunc(&scratch, node,
(gdb)
896 ExprEvalPushStep(state, &scratch);
(gdb)
897 break;
(gdb)
2122 }
(gdb)
ExecBuildProjectionInfo (targetList=0x1cc7550, econtext=0x1c8f408, slot=0x1c8f710, parent=0x1c8f1f0,
inputDesc=0x7f8386bb6ab8) at execExpr.c:459
459 if (get_typlen(exprType((Node *) tle->expr)) == -1)
(gdb)
462 scratch.opcode = EEOP_ASSIGN_TMP;
(gdb)

ExecInitExprRec调用完毕,增加了2个步骤,分别是:
1.opcode = 6,即EEOP_SCAN_VAR
2.opcode = 18,即EEOP_FUNCEXPR_STRICT

(gdb) p *state
$17 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710,
steps = 0x1c8f868, evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 3, steps_alloc = 16,
parent = 0x1c8f1f0, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}
(gdb) p state->steps[1]
$18 = {opcode = 6, resvalue = 0x1c8fd00, resnull = 0x1c90019, d = {fetch = {last_var = 0, known_desc = 0x0}, var = {
attnum = 0, vartype = 23}, wholerow = {var = 0x1700000000, first = false, slow = false, tupdesc = 0x0,
junkFilter = 0x0}, assign_var = {resultnum = 0, attnum = 23}, assign_tmp = {resultnum = 0}, constval = {
value = 98784247808, isnull = false}, func = {finfo = 0x1700000000, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0},
boolexpr = {anynull = 0x1700000000, jumpdone = 0}, qualexpr = {jumpdone = 0}, jump = {jumpdone = 0}, nulltest_row = {
argdesc = 0x1700000000}, param = {paramid = 0, paramtype = 23}, cparam = {paramfunc = 0x1700000000, paramarg = 0x0,
paramid = 0, paramtype = 0}, casetest = {value = 0x1700000000, isnull = 0x0}, make_readonly = {value = 0x1700000000,
isnull = 0x0}, iocoerce = {finfo_out = 0x1700000000, fcinfo_data_out = 0x0, finfo_in = 0x0, fcinfo_data_in = 0x0},
sqlvaluefunction = {svf = 0x1700000000}, nextvalueexpr = {seqid = 0, seqtypid = 23}, arrayexpr = {
elemvalues = 0x1700000000, elemnulls = 0x0, nelems = 0, elemtype = 0, elemlength = 0, elembyval = false,
elemalign = 0 '\000', multidims = false}, arraycoerce = {elemexprstate = 0x1700000000, resultelemtype = 0,
amstate = 0x0}, row = {tupdesc = 0x1700000000, elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {
finfo = 0x1700000000, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0, jumpdone = 0}, rowcompare_final = {rctype = 0},
minmax = {values = 0x1700000000, nulls = 0x0, nelems = 0, op = IS_GREATEST, finfo = 0x0, fcinfo_data = 0x0},
fieldselect = {fieldnum = 0, resulttype = 23, argdesc = 0x0}, fieldstore = {fstore = 0x1700000000, argdesc = 0x0,
values = 0x0, nulls = 0x0, ncolumns = 0}, arrayref_subscript = {state = 0x1700000000, off = 0, isupper = false,
jumpdone = 0}, arrayref = {state = 0x1700000000}, domaincheck = {
constraintname = 0x1700000000
, checkvalue = 0x0, checknull = 0x0,
resulttype = 0}, convert_rowtype = {convert = 0x1700000000, indesc = 0x0, outdesc = 0x0, map = 0x0,
initialized = false}, scalararrayop = {element_type = 0, useOr = 23, typlen = 0, typbyval = false,
typalign = 0 '\000', finfo = 0x0, fcinfo_data = 0x0, fn_addr = 0x0}, xmlexpr = {xexpr = 0x1700000000,
named_argvalue = 0x0, named_argnull = 0x0, argvalue = 0x0, argnull = 0x0}, aggref = {astate = 0x1700000000},
grouping_func = {parent = 0x1700000000, clauses = 0x0}, window_func = {wfstate = 0x1700000000}, subplan = {
sstate = 0x1700000000}, alternative_subplan = {asstate = 0x1700000000}, agg_deserialize = {aggstate = 0x1700000000,
fcinfo_data = 0x0, jumpnull = 0}, agg_strict_input_check = {nulls = 0x1700000000, nargs = 0, jumpnull = 0},
agg_init_trans = {aggstate = 0x1700000000, pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0,
jumpnull = 0}, agg_strict_trans_check = {aggstate = 0x1700000000, setno = 0, transno = 0, setoff = 0, jumpnull = 0},
---Type to continue, or q to quit---
agg_trans = {aggstate = 0x1700000000, pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0}}}
(gdb) p state->steps[2]
$19 = {opcode = 18, resvalue = 0x1c8f7e0, resnull = 0x1c8f7dd, d = {fetch = {last_var = 29949056, known_desc = 0x1c8fcd8},
var = {attnum = 29949056, vartype = 0}, wholerow = {var = 0x1c8fc80, first = 216, slow = 252,
tupdesc = 0x93d60c , junkFilter = 0x2}, assign_var = {resultnum = 29949056, attnum = 0}, assign_tmp = {
resultnum = 29949056}, constval = {value = 29949056, isnull = 216}, func = {finfo = 0x1c8fc80,
fcinfo_data = 0x1c8fcd8, fn_addr = 0x93d60c , nargs = 2}, boolexpr = {anynull = 0x1c8fc80,
jumpdone = 29949144}, qualexpr = {jumpdone = 29949056}, jump = {jumpdone = 29949056}, nulltest_row = {
argdesc = 0x1c8fc80}, param = {paramid = 29949056, paramtype = 0}, cparam = {paramfunc = 0x1c8fc80,
paramarg = 0x1c8fcd8, paramid = 9688588, paramtype = 0}, casetest = {value = 0x1c8fc80, isnull = 0x1c8fcd8},
make_readonly = {value = 0x1c8fc80, isnull = 0x1c8fcd8}, iocoerce = {finfo_out = 0x1c8fc80,
fcinfo_data_out = 0x1c8fcd8, finfo_in = 0x93d60c , fcinfo_data_in = 0x2}, sqlvaluefunction = {
svf = 0x1c8fc80}, nextvalueexpr = {seqid = 29949056, seqtypid = 0}, arrayexpr = {elemvalues = 0x1c8fc80,
elemnulls = 0x1c8fcd8, nelems = 9688588, elemtype = 0, elemlength = 2, elembyval = false, elemalign = 0 '\000',
multidims = false}, arraycoerce = {elemexprstate = 0x1c8fc80, resultelemtype = 29949144,
amstate = 0x93d60c }, row = {tupdesc = 0x1c8fc80, elemvalues = 0x1c8fcd8, elemnulls = 0x93d60c },
rowcompare_step = {finfo = 0x1c8fc80, fcinfo_data = 0x1c8fcd8, fn_addr = 0x93d60c , jumpnull = 2,
jumpdone = 0}, rowcompare_final = {rctype = 29949056}, minmax = {values = 0x1c8fc80, nulls = 0x1c8fcd8,
nelems = 9688588, op = IS_GREATEST, finfo = 0x2, fcinfo_data = 0x0}, fieldselect = {fieldnum = -896, resulttype = 0,
argdesc = 0x1c8fcd8}, fieldstore = {fstore = 0x1c8fc80, argdesc = 0x1c8fcd8, values = 0x93d60c , nulls = 0x2,
ncolumns = 0}, arrayref_subscript = {state = 0x1c8fc80, off = 29949144, isupper = false, jumpdone = 9688588},
arrayref = {state = 0x1c8fc80}, domaincheck = {constraintname = 0x1c8fc80 "\f֓", checkvalue = 0x1c8fcd8,
checknull = 0x93d60c , resulttype = 2}, convert_rowtype = {convert = 0x1c8fc80, indesc = 0x1c8fcd8,
outdesc = 0x93d60c , map = 0x2, initialized = false}, scalararrayop = {element_type = 29949056,
useOr = false, typlen = 0, typbyval = 216, typalign = -4 '\374', finfo = 0x93d60c , fcinfo_data = 0x2,
fn_addr = 0x0}, xmlexpr = {xexpr = 0x1c8fc80, named_argvalue = 0x1c8fcd8, named_argnull = 0x93d60c ,
argvalue = 0x2, argnull = 0x0}, aggref = {astate = 0x1c8fc80}, grouping_func = {parent = 0x1c8fc80,
clauses = 0x1c8fcd8}, window_func = {wfstate = 0x1c8fc80}, subplan = {sstate = 0x1c8fc80}, alternative_subplan = {
asstate = 0x1c8fc80}, agg_deserialize = {aggstate = 0x1c8fc80, fcinfo_data = 0x1c8fcd8, jumpnull = 9688588},
---Type to continue, or q to quit---
agg_strict_input_check = {nulls = 0x1c8fc80, nargs = 29949144, jumpnull = 0}, agg_init_trans = {aggstate = 0x1c8fc80,
pertrans = 0x1c8fcd8, aggcontext = 0x93d60c , setno = 2, transno = 0, setoff = 0, jumpnull = 0},
agg_strict_trans_check = {aggstate = 0x1c8fc80, setno = 29949144, transno = 0, setoff = 9688588, jumpnull = 0},
agg_trans = {aggstate = 0x1c8fc80, pertrans = 0x1c8fcd8, aggcontext = 0x93d60c , setno = 2, transno = 0,
setoff = 0}}}
(gdb)

压入对应该表达式列的编号,opcode = 14,即EEOP_ASSIGN_TMP

(gdb) n
463 scratch.d.assign_tmp.resultnum = tle->resno - 1;
(gdb)
464 ExprEvalPushStep(state, &scratch);
(gdb)
(gdb)
374 foreach(lc, targetList)
(gdb) p *state
$20 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710,
steps = 0x1c8f868, evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 4, steps_alloc = 16,
parent = 0x1c8f1f0, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}
(gdb) p state->steps[3]
$21 = {opcode = 14, resvalue = 0x0, resnull = 0x0, 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)

继续处理下一个列,这是一个"安全"列,压入EEOP_ASSIGN_SCAN_VAR步骤

(gdb) n
376 TargetEntry *tle = lfirst_node(TargetEntry, lc);
(gdb)
377 Var *variable = NULL;
(gdb) p *tle
$22 = {xpr = {type = T_TargetEntry}, expr = 0x1c9aa40, resno = 2, resname = 0x1ba7a40 "c2", ressortgroupref = 0,
resorigtbl = 237600, resorigcol = 2, resjunk = false}
(gdb) n
378 AttrNumber attnum = 0;
(gdb)
379 bool isSafeVar = false;
(gdb)
389 if (tle->expr != NULL &&
(gdb)
390 IsA(tle->expr, Var) &&
(gdb)
389 if (tle->expr != NULL &&
(gdb)
391 ((Var *) tle->expr)->varattno > 0)
(gdb)
390 IsA(tle->expr, Var) &&
(gdb)
394 variable = (Var *) tle->expr;
(gdb)
395 attnum = variable->varattno;
(gdb)
397 if (inputDesc == NULL)
(gdb)
399 else if (attnum <= inputDesc->natts)
(gdb)
401 Form_pg_attribute attr = TupleDescAttr(inputDesc, attnum - 1);
(gdb)
408 if (!attr->attisdropped && variable->vartype == attr->atttypid)
(gdb)
410 isSafeVar = true;
(gdb)
415 if (isSafeVar)
(gdb)
418 switch (variable->varno)
(gdb)
434 scratch.opcode = EEOP_ASSIGN_SCAN_VAR;
(gdb)
435 break;
(gdb)
438 scratch.d.assign_var.attnum = attnum - 1;
(gdb)
439 scratch.d.assign_var.resultnum = tle->resno - 1;
(gdb)
440 ExprEvalPushStep(state, &scratch);
(gdb) p *state
$23 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710,
steps = 0x1c8f868, evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 4, steps_alloc = 16,
parent = 0x1c8f1f0, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}
(gdb) n
374 foreach(lc, targetList)
(gdb) p *state
$24 = {tag = {type = T_ExprState}, flags = 0 '\000', resnull = false, resvalue = 0, resultslot = 0x1c8f710,
steps = 0x1c8f868, evalfunc = 0x0, expr = 0x1cc7550, evalfunc_private = 0x0, steps_len = 5, steps_alloc = 16,
parent = 0x1c8f1f0, ext_params = 0x0, innermost_caseval = 0x0, innermost_casenull = 0x0, innermost_domainval = 0x0,
innermost_domainnull = 0x0}
(gdb) p state->steps[4]
$25 = {opcode = 13, resvalue = 0x0, resnull = 0x0, d = {fetch = {last_var = 1, known_desc = 0x0}, var = {attnum = 1,
vartype = 1}, wholerow = {var = 0x100000001, first = false, slow = false, tupdesc = 0x0, junkFilter = 0x0},
assign_var = {resultnum = 1, attnum = 1}, assign_tmp = {resultnum = 1}, constval = {value = 4294967297,
isnull = false}, func = {finfo = 0x100000001, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0}, boolexpr = {
anynull = 0x100000001, jumpdone = 0}, qualexpr = {jumpdone = 1}, jump = {jumpdone = 1}, nulltest_row = {
argdesc = 0x100000001}, param = {paramid = 1, paramtype = 1}, cparam = {paramfunc = 0x100000001, paramarg = 0x0,
paramid = 0, paramtype = 0}, casetest = {value = 0x100000001, isnull = 0x0}, make_readonly = {value = 0x100000001,
isnull = 0x0}, iocoerce = {finfo_out = 0x100000001, fcinfo_data_out = 0x0, finfo_in = 0x0, fcinfo_data_in = 0x0},
sqlvaluefunction = {svf = 0x100000001}, nextvalueexpr = {seqid = 1, seqtypid = 1}, arrayexpr = {
elemvalues = 0x100000001, elemnulls = 0x0, nelems = 0, elemtype = 0, elemlength = 0, elembyval = false,
elemalign = 0 '\000', multidims = false}, arraycoerce = {elemexprstate = 0x100000001, resultelemtype = 0,
amstate = 0x0}, row = {tupdesc = 0x100000001, elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {
finfo = 0x100000001, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0, jumpdone = 0}, rowcompare_final = {
rctype = ROWCOMPARE_LT}, minmax = {values = 0x100000001, nulls = 0x0, nelems = 0, op = IS_GREATEST, finfo = 0x0,
fcinfo_data = 0x0}, fieldselect = {fieldnum = 1, resulttype = 1, argdesc = 0x0}, fieldstore = {fstore = 0x100000001,
argdesc = 0x0, values = 0x0, nulls = 0x0, ncolumns = 0}, arrayref_subscript = {state = 0x100000001, off = 0,
isupper = false, jumpdone = 0}, arrayref = {state = 0x100000001}, domaincheck = {
constraintname = 0x100000001
, checkvalue = 0x0, checknull = 0x0, resulttype = 0},
convert_rowtype = {convert = 0x100000001, indesc = 0x0, outdesc = 0x0, map = 0x0, initialized = false},
scalararrayop = {element_type = 1, useOr = true, typlen = 0, typbyval = false, typalign = 0 '\000', finfo = 0x0,
fcinfo_data = 0x0, fn_addr = 0x0}, xmlexpr = {xexpr = 0x100000001, named_argvalue = 0x0, named_argnull = 0x0,
argvalue = 0x0, argnull = 0x0}, aggref = {astate = 0x100000001}, grouping_func = {parent = 0x100000001,
clauses = 0x0}, window_func = {wfstate = 0x100000001}, subplan = {sstate = 0x100000001}, alternative_subplan = {
asstate = 0x100000001}, agg_deserialize = {aggstate = 0x100000001, fcinfo_data = 0x0, jumpnull = 0},
agg_strict_input_check = {nulls = 0x100000001, nargs = 0, jumpnull = 0}, agg_init_trans = {aggstate = 0x100000001,
pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_strict_trans_check = {
aggstate = 0x100000001, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_trans = {aggstate = 0x100000001,
---Type to continue, or q to quit---
pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0}}}
(gdb)

4.压入EEOP_DONE步骤

(gdb) n
468 scratch.opcode = EEOP_DONE;
(gdb)
469 ExprEvalPushStep(state, &scratch);
(gdb)
471 ExecReadyExpr(state);
(gdb) p state->steps[5]
$26 = {opcode = 0, resvalue = 0x0, resnull = 0x0, d = {fetch = {last_var = 1, known_desc = 0x0}, var = {attnum = 1,
vartype = 1}, wholerow = {var = 0x100000001, first = false, slow = false, tupdesc = 0x0, junkFilter = 0x0},
assign_var = {resultnum = 1, attnum = 1}, assign_tmp = {resultnum = 1}, constval = {value = 4294967297,
isnull = false}, func = {finfo = 0x100000001, fcinfo_data = 0x0, fn_addr = 0x0, nargs = 0}, boolexpr = {
anynull = 0x100000001, jumpdone = 0}, qualexpr = {jumpdone = 1}, jump = {jumpdone = 1}, nulltest_row = {
argdesc = 0x100000001}, param = {paramid = 1, paramtype = 1}, cparam = {paramfunc = 0x100000001, paramarg = 0x0,
paramid = 0, paramtype = 0}, casetest = {value = 0x100000001, isnull = 0x0}, make_readonly = {value = 0x100000001,
isnull = 0x0}, iocoerce = {finfo_out = 0x100000001, fcinfo_data_out = 0x0, finfo_in = 0x0, fcinfo_data_in = 0x0},
sqlvaluefunction = {svf = 0x100000001}, nextvalueexpr = {seqid = 1, seqtypid = 1}, arrayexpr = {
elemvalues = 0x100000001, elemnulls = 0x0, nelems = 0, elemtype = 0, elemlength = 0, elembyval = false,
elemalign = 0 '\000', multidims = false}, arraycoerce = {elemexprstate = 0x100000001, resultelemtype = 0,
amstate = 0x0}, row = {tupdesc = 0x100000001, elemvalues = 0x0, elemnulls = 0x0}, rowcompare_step = {
finfo = 0x100000001, fcinfo_data = 0x0, fn_addr = 0x0, jumpnull = 0, jumpdone = 0}, rowcompare_final = {
rctype = ROWCOMPARE_LT}, minmax = {values = 0x100000001, nulls = 0x0, nelems = 0, op = IS_GREATEST, finfo = 0x0,
fcinfo_data = 0x0}, fieldselect = {fieldnum = 1, resulttype = 1, argdesc = 0x0}, fieldstore = {fstore = 0x100000001,
argdesc = 0x0, values = 0x0, nulls = 0x0, ncolumns = 0}, arrayref_subscript = {state = 0x100000001, off = 0,
isupper = false, jumpdone = 0}, arrayref = {state = 0x100000001}, domaincheck = {
constraintname = 0x100000001
, checkvalue = 0x0, checknull = 0x0, resulttype = 0},
convert_rowtype = {convert = 0x100000001, indesc = 0x0, outdesc = 0x0, map = 0x0, initialized = false},
scalararrayop = {element_type = 1, useOr = true, typlen = 0, typbyval = false, typalign = 0 '\000', finfo = 0x0,
fcinfo_data = 0x0, fn_addr = 0x0}, xmlexpr = {xexpr = 0x100000001, named_argvalue = 0x0, named_argnull = 0x0,
argvalue = 0x0, argnull = 0x0}, aggref = {astate = 0x100000001}, grouping_func = {parent = 0x100000001,
clauses = 0x0}, window_func = {wfstate = 0x100000001}, subplan = {sstate = 0x100000001}, alternative_subplan = {
asstate = 0x100000001}, agg_deserialize = {aggstate = 0x100000001, fcinfo_data = 0x0, jumpnull = 0},
agg_strict_input_check = {nulls = 0x100000001, nargs = 0, jumpnull = 0}, agg_init_trans = {aggstate = 0x100000001,
pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_strict_trans_check = {
aggstate = 0x100000001, setno = 0, transno = 0, setoff = 0, jumpnull = 0}, agg_trans = {aggstate = 0x100000001,
---Type to continue, or q to quit---
pertrans = 0x0, aggcontext = 0x0, setno = 0, transno = 0, setoff = 0}}}
(gdb)

结束调用

(gdb) n
473 return projInfo;
(gdb)
474 }
(gdb)
ExecAssignProjectionInfo (planstate=0x1c8f1f0, inputDesc=0x7f8386bb6ab8) at execUtils.c:467
467 planstate->ps_ProjInfo =
(gdb)

以上是"PostgreSQL如何构建表达式解析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

0