千家信息网

PostgreSQL如何构建表达式解析

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要介绍PostgreSQL如何构建表达式解析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、数据结构EEO_XXX宏定义opcode分发器宏定义/* * Macro
千家信息网最后更新 2026年10月31日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如何构建表达式解析"这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

表达式 步骤 节点 处理 类型 结果 函数 投影 安全 信息 系统 元素 常规 存储 生成 数组 分配 应用 检查 属性 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 科技信息互联网 互联网科技开发公司 融贸通互联网科技 互联网科技的未来发展 国外互联网科技网站 互联网之后的科技是什么 恒银科技与互联网金融 互联网科技热点资讯 贵州叶脉互联网科技有限公司所属行业 互联网校园科技培训 昊天互联网科技有限公司 厦门千工互联网科技有限公司 畅思互联网科技 互联网时代科技型公司的机会 同城互联网络科技 福仕德教育科技互联网 玉溪新华互联网科技怎么选 绵阳真晨互联网科技有限公司 三亚科技创新互联网基地 互联网大会出现的黑科技 互联网科技公司经营范围 以房互联网科技 齐家互联网科技 互联网 视角下的科技创新 齐家互联网科技有限公司 湖北码尼互联网科技有限公司电话 乌苏新华互联网科技做什么的 沐思想互联网科技有限公司 四川互联网展示科技馆设计 九思连强互联网电子科技有限公司
0