温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

怎么使用PostgreSQL中ExecInitExprRec函数

发布时间:2021-11-09 14:40:31 来源:亿速云 阅读:366 作者:iii 栏目:关系型数据库

本篇内容主要讲解“怎么使用PostgreSQL中ExecInitExprRec函数”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用PostgreSQL中ExecInitExprRec函数”吧!

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;

二、源码解读

ExecInitExprRec函数,把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中.
其主要逻辑是根据节点类型,执行相应的处理逻辑,比如节点类型为OpExpr,则其逻辑为:

case T_OpExpr://操作符表达式
            {
                OpExpr     *op = (OpExpr *) node;
                ExecInitFunc(&scratch, node,
                             op->args, op->opfuncid, op->inputcollid,
                             state);
                ExprEvalPushStep(state, &scratch);
                break;
            }

其他节点类型类似,代码虽然很长,但逻辑清晰简单.

/*
 * Append the steps necessary for the evaluation of node to ExprState->steps,
 * possibly recursing into sub-expressions of node.
 * 把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中.
 *
 * node - expression to evaluate
 * state - ExprState to whose ->steps to append the necessary operations
 * resv / resnull - where to store the result of the node into
 * node - 待解析的表达式
 * state - 步骤追加到该ExprState ->steps中
 * resv / resnull - 节点结果存储的位置
 */
static void
ExecInitExprRec(Expr *node, ExprState *state,
                Datum *resv, bool *resnull)
{
    ExprEvalStep scratch = {0};
    /* Guard against stack overflow due to overly complex expressions */
    //避免出现堆栈溢出
    check_stack_depth();
    /* Step's output location is always what the caller gave us */
    //步骤的输出位置往往是调用者提供给我们的
    Assert(resv != NULL && resnull != NULL);
    scratch.resvalue = resv;
    scratch.resnull = resnull;
    /* cases should be ordered as they are in enum NodeTag */
    //CASE的顺序与NodeTag枚举类型中的顺序一样
    switch (nodeTag(node))
    {
        case T_Var://VAR
            {
                Var        *variable = (Var *) node;
                if (variable->varattno == InvalidAttrNumber)
                {
                    /* whole-row Var */
                    ExecInitWholeRowVar(&scratch, variable, state);
                }
                else if (variable->varattno <= 0)
                {
                    /* system column */
                    scratch.d.var.attnum = variable->varattno;
                    scratch.d.var.vartype = variable->vartype;
                    switch (variable->varno)
                    {
                        case INNER_VAR:
                            scratch.opcode = EEOP_INNER_SYSVAR;
                            break;
                        case OUTER_VAR:
                            scratch.opcode = EEOP_OUTER_SYSVAR;
                            break;
                            /* INDEX_VAR is handled by default case */
                        default:
                            scratch.opcode = EEOP_SCAN_SYSVAR;
                            break;
                    }
                }
                else
                {
                    /* regular user column */
                    scratch.d.var.attnum = variable->varattno - 1;
                    scratch.d.var.vartype = variable->vartype;
                    switch (variable->varno)
                    {
                        case INNER_VAR:
                            scratch.opcode = EEOP_INNER_VAR;
                            break;
                        case OUTER_VAR:
                            scratch.opcode = EEOP_OUTER_VAR;
                            break;
                            /* INDEX_VAR is handled by default case */
                        default:
                            scratch.opcode = EEOP_SCAN_VAR;
                            break;
                    }
                }
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_Const://常量
            {
                Const      *con = (Const *) node;
                scratch.opcode = EEOP_CONST;
                scratch.d.constval.value = con->constvalue;
                scratch.d.constval.isnull = con->constisnull;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_Param://参数
            {
                Param      *param = (Param *) node;
                ParamListInfo params;
                switch (param->paramkind)
                {
                    case PARAM_EXEC:
                        scratch.opcode = EEOP_PARAM_EXEC;
                        scratch.d.param.paramid = param->paramid;
                        scratch.d.param.paramtype = param->paramtype;
                        ExprEvalPushStep(state, &scratch);
                        break;
                    case PARAM_EXTERN:
                        /*
                         * If we have a relevant ParamCompileHook, use it;
                         * otherwise compile a standard EEOP_PARAM_EXTERN
                         * step.  ext_params, if supplied, takes precedence
                         * over info from the parent node's EState (if any).
                         */
                        if (state->ext_params)
                            params = state->ext_params;
                        else if (state->parent &&
                                 state->parent->state)
                            params = state->parent->state->es_param_list_info;
                        else
                            params = NULL;
                        if (params && params->paramCompile)
                        {
                            params->paramCompile(params, param, state,
                                                 resv, resnull);
                        }
                        else
                        {
                            scratch.opcode = EEOP_PARAM_EXTERN;
                            scratch.d.param.paramid = param->paramid;
                            scratch.d.param.paramtype = param->paramtype;
                            ExprEvalPushStep(state, &scratch);
                        }
                        break;
                    default:
                        elog(ERROR, "unrecognized paramkind: %d",
                             (int) param->paramkind);
                        break;
                }
                break;
            }
        case T_Aggref://聚集
            {
                Aggref     *aggref = (Aggref *) node;
                AggrefExprState *astate = makeNode(AggrefExprState);
                scratch.opcode = EEOP_AGGREF;
                scratch.d.aggref.astate = astate;
                astate->aggref = aggref;
                if (state->parent && IsA(state->parent, AggState))
                {
                    AggState   *aggstate = (AggState *) state->parent;
                    aggstate->aggs = lcons(astate, aggstate->aggs);
                    aggstate->numaggs++;
                }
                else
                {
                    /* planner messed up */
                    elog(ERROR, "Aggref found in non-Agg plan node");
                }
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_GroupingFunc:
            {
                GroupingFunc *grp_node = (GroupingFunc *) node;
                Agg        *agg;
                if (!state->parent || !IsA(state->parent, AggState) ||
                    !IsA(state->parent->plan, Agg))
                    elog(ERROR, "GroupingFunc found in non-Agg plan node");
                scratch.opcode = EEOP_GROUPING_FUNC;
                scratch.d.grouping_func.parent = (AggState *) state->parent;
                agg = (Agg *) (state->parent->plan);
                if (agg->groupingSets)
                    scratch.d.grouping_func.clauses = grp_node->cols;
                else
                    scratch.d.grouping_func.clauses = NIL;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_WindowFunc:
            {
                WindowFunc *wfunc = (WindowFunc *) node;
                WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
                wfstate->wfunc = wfunc;
                if (state->parent && IsA(state->parent, WindowAggState))
                {
                    WindowAggState *winstate = (WindowAggState *) state->parent;
                    int         nfuncs;
                    winstate->funcs = lcons(wfstate, winstate->funcs);
                    nfuncs = ++winstate->numfuncs;
                    if (wfunc->winagg)
                        winstate->numaggs++;
                    /* for now initialize agg using old style expressions */
                    wfstate->args = ExecInitExprList(wfunc->args,
                                                     state->parent);
                    wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
                                                      state->parent);
                    /*
                     * Complain if the windowfunc's arguments contain any
                     * windowfuncs; nested window functions are semantically
                     * nonsensical.  (This should have been caught earlier,
                     * but we defend against it here anyway.)
                     */
                    if (nfuncs != winstate->numfuncs)
                        ereport(ERROR,
                                (errcode(ERRCODE_WINDOWING_ERROR),
                                 errmsg("window function calls cannot be nested")));
                }
                else
                {
                    /* planner messed up */
                    elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
                }
                scratch.opcode = EEOP_WINDOW_FUNC;
                scratch.d.window_func.wfstate = wfstate;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_ArrayRef:
            {
                ArrayRef   *aref = (ArrayRef *) node;
                ExecInitArrayRef(&scratch, aref, state, resv, resnull);
                break;
            }
        case T_FuncExpr://函数表达式
            {
                FuncExpr   *func = (FuncExpr *) node;
                ExecInitFunc(&scratch, node,
                             func->args, func->funcid, func->inputcollid,
                             state);
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_OpExpr://操作符表达式
            {
                OpExpr     *op = (OpExpr *) node;
                ExecInitFunc(&scratch, node,
                             op->args, op->opfuncid, op->inputcollid,
                             state);
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_DistinctExpr:
            {
                DistinctExpr *op = (DistinctExpr *) node;
                ExecInitFunc(&scratch, node,
                             op->args, op->opfuncid, op->inputcollid,
                             state);
                /*
                 * Change opcode of call instruction to EEOP_DISTINCT.
                 *
                 * XXX: historically we've not called the function usage
                 * pgstat infrastructure - that seems inconsistent given that
                 * we do so for normal function *and* operator evaluation.  If
                 * we decided to do that here, we'd probably want separate
                 * opcodes for FUSAGE or not.
                 */
                scratch.opcode = EEOP_DISTINCT;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_NullIfExpr:
            {
                NullIfExpr *op = (NullIfExpr *) node;
                ExecInitFunc(&scratch, node,
                             op->args, op->opfuncid, op->inputcollid,
                             state);
                /*
                 * Change opcode of call instruction to EEOP_NULLIF.
                 *
                 * XXX: historically we've not called the function usage
                 * pgstat infrastructure - that seems inconsistent given that
                 * we do so for normal function *and* operator evaluation.  If
                 * we decided to do that here, we'd probably want separate
                 * opcodes for FUSAGE or not.
                 */
                scratch.opcode = EEOP_NULLIF;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_ScalarArrayOpExpr:
            {
                ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
                Expr       *scalararg;
                Expr       *arrayarg;
                FmgrInfo   *finfo;
                FunctionCallInfo fcinfo;
                AclResult   aclresult;
                Assert(list_length(opexpr->args) == 2);
                scalararg = (Expr *) linitial(opexpr->args);
                arrayarg = (Expr *) lsecond(opexpr->args);
                /* Check permission to call function */
                aclresult = pg_proc_aclcheck(opexpr->opfuncid,
                                             GetUserId(),
                                             ACL_EXECUTE);
                if (aclresult != ACLCHECK_OK)
                    aclcheck_error(aclresult, OBJECT_FUNCTION,
                                   get_func_name(opexpr->opfuncid));
                InvokeFunctionExecuteHook(opexpr->opfuncid);
                /* Set up the primary fmgr lookup information */
                finfo = palloc0(sizeof(FmgrInfo));
                fcinfo = palloc0(sizeof(FunctionCallInfoData));
                fmgr_info(opexpr->opfuncid, finfo);
                fmgr_info_set_expr((Node *) node, finfo);
                InitFunctionCallInfoData(*fcinfo, finfo, 2,
                                         opexpr->inputcollid, NULL, NULL);
                /* Evaluate scalar directly into left function argument */
                ExecInitExprRec(scalararg, state,
                                &fcinfo->arg[0], &fcinfo->argnull[0]);
                /*
                 * Evaluate array argument into our return value.  There's no
                 * danger in that, because the return value is guaranteed to
                 * be overwritten by EEOP_SCALARARRAYOP, and will not be
                 * passed to any other expression.
                 */
                ExecInitExprRec(arrayarg, state, resv, resnull);
                /* And perform the operation */
                scratch.opcode = EEOP_SCALARARRAYOP;
                scratch.d.scalararrayop.element_type = InvalidOid;
                scratch.d.scalararrayop.useOr = opexpr->useOr;
                scratch.d.scalararrayop.finfo = finfo;
                scratch.d.scalararrayop.fcinfo_data = fcinfo;
                scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_BoolExpr:
            {
                BoolExpr   *boolexpr = (BoolExpr *) node;
                int         nargs = list_length(boolexpr->args);
                List       *adjust_jumps = NIL;
                int         off;
                ListCell   *lc;
                /* allocate scratch memory used by all steps of AND/OR */
                if (boolexpr->boolop != NOT_EXPR)
                    scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool));
                /*
                 * For each argument evaluate the argument itself, then
                 * perform the bool operation's appropriate handling.
                 *
                 * We can evaluate each argument into our result area, since
                 * the short-circuiting logic means we only need to remember
                 * previous NULL values.
                 *
                 * AND/OR is split into separate STEP_FIRST (one) / STEP (zero
                 * or more) / STEP_LAST (one) steps, as each of those has to
                 * perform different work.  The FIRST/LAST split is valid
                 * because AND/OR have at least two arguments.
                 */
                off = 0;
                foreach(lc, boolexpr->args)
                {
                    Expr       *arg = (Expr *) lfirst(lc);
                    /* Evaluate argument into our output variable */
                    ExecInitExprRec(arg, state, resv, resnull);
                    /* Perform the appropriate step type */
                    switch (boolexpr->boolop)
                    {
                        case AND_EXPR:
                            Assert(nargs >= 2);
                            if (off == 0)
                                scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
                            else if (off + 1 == nargs)
                                scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
                            else
                                scratch.opcode = EEOP_BOOL_AND_STEP;
                            break;
                        case OR_EXPR:
                            Assert(nargs >= 2);
                            if (off == 0)
                                scratch.opcode = EEOP_BOOL_OR_STEP_FIRST;
                            else if (off + 1 == nargs)
                                scratch.opcode = EEOP_BOOL_OR_STEP_LAST;
                            else
                                scratch.opcode = EEOP_BOOL_OR_STEP;
                            break;
                        case NOT_EXPR:
                            Assert(nargs == 1);
                            scratch.opcode = EEOP_BOOL_NOT_STEP;
                            break;
                        default:
                            elog(ERROR, "unrecognized boolop: %d",
                                 (int) boolexpr->boolop);
                            break;
                    }
                    scratch.d.boolexpr.jumpdone = -1;
                    ExprEvalPushStep(state, &scratch);
                    adjust_jumps = lappend_int(adjust_jumps,
                                               state->steps_len - 1);
                    off++;
                }
                /* adjust jump targets */
                foreach(lc, adjust_jumps)
                {
                    ExprEvalStep *as = &state->steps[lfirst_int(lc)];
                    Assert(as->d.boolexpr.jumpdone == -1);
                    as->d.boolexpr.jumpdone = state->steps_len;
                }
                break;
            }
        case T_SubPlan:
            {
                SubPlan    *subplan = (SubPlan *) node;
                SubPlanState *sstate;
                if (!state->parent)
                    elog(ERROR, "SubPlan found with no parent plan");
                sstate = ExecInitSubPlan(subplan, state->parent);
                /* add SubPlanState nodes to state->parent->subPlan */
                state->parent->subPlan = lappend(state->parent->subPlan,
                                                 sstate);
                scratch.opcode = EEOP_SUBPLAN;
                scratch.d.subplan.sstate = sstate;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_AlternativeSubPlan:
            {
                AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
                AlternativeSubPlanState *asstate;
                if (!state->parent)
                    elog(ERROR, "AlternativeSubPlan found with no parent plan");
                asstate = ExecInitAlternativeSubPlan(asplan, state->parent);
                scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
                scratch.d.alternative_subplan.asstate = asstate;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_FieldSelect:
            {
                FieldSelect *fselect = (FieldSelect *) node;
                /* evaluate row/record argument into result area */
                ExecInitExprRec(fselect->arg, state, resv, resnull);
                /* and extract field */
                scratch.opcode = EEOP_FIELDSELECT;
                scratch.d.fieldselect.fieldnum = fselect->fieldnum;
                scratch.d.fieldselect.resulttype = fselect->resulttype;
                scratch.d.fieldselect.argdesc = NULL;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_FieldStore:
            {
                FieldStore *fstore = (FieldStore *) node;
                TupleDesc   tupDesc;
                TupleDesc  *descp;
                Datum      *values;
                bool       *nulls;
                int         ncolumns;
                ListCell   *l1,
                           *l2;
                /* find out the number of columns in the composite type */
                tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
                ncolumns = tupDesc->natts;
                DecrTupleDescRefCount(tupDesc);
                /* create workspace for column values */
                values = (Datum *) palloc(sizeof(Datum) * ncolumns);
                nulls = (bool *) palloc(sizeof(bool) * ncolumns);
                /* create workspace for runtime tupdesc cache */
                descp = (TupleDesc *) palloc(sizeof(TupleDesc));
                *descp = NULL;
                /* emit code to evaluate the composite input value */
                ExecInitExprRec(fstore->arg, state, resv, resnull);
                /* next, deform the input tuple into our workspace */
                scratch.opcode = EEOP_FIELDSTORE_DEFORM;
                scratch.d.fieldstore.fstore = fstore;
                scratch.d.fieldstore.argdesc = descp;
                scratch.d.fieldstore.values = values;
                scratch.d.fieldstore.nulls = nulls;
                scratch.d.fieldstore.ncolumns = ncolumns;
                ExprEvalPushStep(state, &scratch);
                /* evaluate new field values, store in workspace columns */
                forboth(l1, fstore->newvals, l2, fstore->fieldnums)
                {
                    Expr       *e = (Expr *) lfirst(l1);
                    AttrNumber  fieldnum = lfirst_int(l2);
                    Datum      *save_innermost_caseval;
                    bool       *save_innermost_casenull;
                    if (fieldnum <= 0 || fieldnum > ncolumns)
                        elog(ERROR, "field number %d is out of range in FieldStore",
                             fieldnum);
                    /*
                     * Use the CaseTestExpr mechanism to pass down the old
                     * value of the field being replaced; this is needed in
                     * case the newval is itself a FieldStore or ArrayRef that
                     * has to obtain and modify the old value.  It's safe to
                     * reuse the CASE mechanism because there cannot be a CASE
                     * between here and where the value would be needed, and a
                     * field assignment can't be within a CASE either.  (So
                     * saving and restoring innermost_caseval is just
                     * paranoia, but let's do it anyway.)
                     *
                     * Another non-obvious point is that it's safe to use the
                     * field's values[]/nulls[] entries as both the caseval
                     * source and the result address for this subexpression.
                     * That's okay only because (1) both FieldStore and
                     * ArrayRef evaluate their arg or refexpr inputs first,
                     * and (2) any such CaseTestExpr is directly the arg or
                     * refexpr input.  So any read of the caseval will occur
                     * before there's a chance to overwrite it.  Also, if
                     * multiple entries in the newvals/fieldnums lists target
                     * the same field, they'll effectively be applied
                     * left-to-right which is what we want.
                     */
                    save_innermost_caseval = state->innermost_caseval;
                    save_innermost_casenull = state->innermost_casenull;
                    state->innermost_caseval = &values[fieldnum - 1];
                    state->innermost_casenull = &nulls[fieldnum - 1];
                    ExecInitExprRec(e, state,
                                    &values[fieldnum - 1],
                                    &nulls[fieldnum - 1]);
                    state->innermost_caseval = save_innermost_caseval;
                    state->innermost_casenull = save_innermost_casenull;
                }
                /* finally, form result tuple */
                scratch.opcode = EEOP_FIELDSTORE_FORM;
                scratch.d.fieldstore.fstore = fstore;
                scratch.d.fieldstore.argdesc = descp;
                scratch.d.fieldstore.values = values;
                scratch.d.fieldstore.nulls = nulls;
                scratch.d.fieldstore.ncolumns = ncolumns;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_RelabelType:
            {
                /* relabel doesn't need to do anything at runtime */
                RelabelType *relabel = (RelabelType *) node;
                ExecInitExprRec(relabel->arg, state, resv, resnull);
                break;
            }
        case T_CoerceViaIO:
            {
                CoerceViaIO *iocoerce = (CoerceViaIO *) node;
                Oid         iofunc;
                bool        typisvarlena;
                Oid         typioparam;
                FunctionCallInfo fcinfo_in;
                /* evaluate argument into step's result area */
                ExecInitExprRec(iocoerce->arg, state, resv, resnull);
                /*
                 * Prepare both output and input function calls, to be
                 * evaluated inside a single evaluation step for speed - this
                 * can be a very common operation.
                 *
                 * We don't check permissions here as a type's input/output
                 * function are assumed to be executable by everyone.
                 */
                scratch.opcode = EEOP_IOCOERCE;
                /* lookup the source type's output function */
                scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
                scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
                getTypeOutputInfo(exprType((Node *) iocoerce->arg),
                                  &iofunc, &typisvarlena);
                fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
                fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
                InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
                                         scratch.d.iocoerce.finfo_out,
                                         1, InvalidOid, NULL, NULL);
                /* lookup the result type's input function */
                scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
                scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
                getTypeInputInfo(iocoerce->resulttype,
                                 &iofunc, &typioparam);
                fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
                fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
                InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
                                         scratch.d.iocoerce.finfo_in,
                                         3, InvalidOid, NULL, NULL);
                /*
                 * We can preload the second and third arguments for the input
                 * function, since they're constants.
                 */
                fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
                fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
                fcinfo_in->argnull[1] = false;
                fcinfo_in->arg[2] = Int32GetDatum(-1);
                fcinfo_in->argnull[2] = false;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_ArrayCoerceExpr:
            {
                ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
                Oid         resultelemtype;
                ExprState  *elemstate;
                /* evaluate argument into step's result area */
                ExecInitExprRec(acoerce->arg, state, resv, resnull);
                resultelemtype = get_element_type(acoerce->resulttype);
                if (!OidIsValid(resultelemtype))
                    ereport(ERROR,
                            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                             errmsg("target type is not an array")));
                /*
                 * Construct a sub-expression for the per-element expression;
                 * but don't ready it until after we check it for triviality.
                 * We assume it hasn't any Var references, but does have a
                 * CaseTestExpr representing the source array element values.
                 */
                elemstate = makeNode(ExprState);
                elemstate->expr = acoerce->elemexpr;
                elemstate->parent = state->parent;
                elemstate->ext_params = state->ext_params;
                elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
                elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
                ExecInitExprRec(acoerce->elemexpr, elemstate,
                                &elemstate->resvalue, &elemstate->resnull);
                if (elemstate->steps_len == 1 &&
                    elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
                {
                    /* Trivial, so we need no per-element work at runtime */
                    elemstate = NULL;
                }
                else
                {
                    /* Not trivial, so append a DONE step */
                    scratch.opcode = EEOP_DONE;
                    ExprEvalPushStep(elemstate, &scratch);
                    /* and ready the subexpression */
                    ExecReadyExpr(elemstate);
                }
                scratch.opcode = EEOP_ARRAYCOERCE;
                scratch.d.arraycoerce.elemexprstate = elemstate;
                scratch.d.arraycoerce.resultelemtype = resultelemtype;
                if (elemstate)
                {
                    /* Set up workspace for array_map */
                    scratch.d.arraycoerce.amstate =
                        (ArrayMapState *) palloc0(sizeof(ArrayMapState));
                }
                else
                {
                    /* Don't need workspace if there's no subexpression */
                    scratch.d.arraycoerce.amstate = NULL;
                }
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_ConvertRowtypeExpr:
            {
                ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
                /* evaluate argument into step's result area */
                ExecInitExprRec(convert->arg, state, resv, resnull);
                /* and push conversion step */
                scratch.opcode = EEOP_CONVERT_ROWTYPE;
                scratch.d.convert_rowtype.convert = convert;
                scratch.d.convert_rowtype.indesc = NULL;
                scratch.d.convert_rowtype.outdesc = NULL;
                scratch.d.convert_rowtype.map = NULL;
                scratch.d.convert_rowtype.initialized = false;
                ExprEvalPushStep(state, &scratch);
                break;
            }
            /* note that CaseWhen expressions are handled within this block */
        case T_CaseExpr:
            {
                CaseExpr   *caseExpr = (CaseExpr *) node;
                List       *adjust_jumps = NIL;
                Datum      *caseval = NULL;
                bool       *casenull = NULL;
                ListCell   *lc;
                /*
                 * If there's a test expression, we have to evaluate it and
                 * save the value where the CaseTestExpr placeholders can find
                 * it.
                 */
                if (caseExpr->arg != NULL)
                {
                    /* Evaluate testexpr into caseval/casenull workspace */
                    caseval = palloc(sizeof(Datum));
                    casenull = palloc(sizeof(bool));
                    ExecInitExprRec(caseExpr->arg, state,
                                    caseval, casenull);
                    /*
                     * Since value might be read multiple times, force to R/O
                     * - but only if it could be an expanded datum.
                     */
                    if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
                    {
                        /* change caseval in-place */
                        scratch.opcode = EEOP_MAKE_READONLY;
                        scratch.resvalue = caseval;
                        scratch.resnull = casenull;
                        scratch.d.make_readonly.value = caseval;
                        scratch.d.make_readonly.isnull = casenull;
                        ExprEvalPushStep(state, &scratch);
                        /* restore normal settings of scratch fields */
                        scratch.resvalue = resv;
                        scratch.resnull = resnull;
                    }
                }
                /*
                 * Prepare to evaluate each of the WHEN clauses in turn; as
                 * soon as one is true we return the value of the
                 * corresponding THEN clause.  If none are true then we return
                 * the value of the ELSE clause, or NULL if there is none.
                 */
                foreach(lc, caseExpr->args)
                {
                    CaseWhen   *when = (CaseWhen *) lfirst(lc);
                    Datum      *save_innermost_caseval;
                    bool       *save_innermost_casenull;
                    int         whenstep;
                    /*
                     * Make testexpr result available to CaseTestExpr nodes
                     * within the condition.  We must save and restore prior
                     * setting of innermost_caseval fields, in case this node
                     * is itself within a larger CASE.
                     *
                     * If there's no test expression, we don't actually need
                     * to save and restore these fields; but it's less code to
                     * just do so unconditionally.
                     */
                    save_innermost_caseval = state->innermost_caseval;
                    save_innermost_casenull = state->innermost_casenull;
                    state->innermost_caseval = caseval;
                    state->innermost_casenull = casenull;
                    /* evaluate condition into CASE's result variables */
                    ExecInitExprRec(when->expr, state, resv, resnull);
                    state->innermost_caseval = save_innermost_caseval;
                    state->innermost_casenull = save_innermost_casenull;
                    /* If WHEN result isn't true, jump to next CASE arm */
                    scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
                    scratch.d.jump.jumpdone = -1;   /* computed later */
                    ExprEvalPushStep(state, &scratch);
                    whenstep = state->steps_len - 1;
                    /*
                     * If WHEN result is true, evaluate THEN result, storing
                     * it into the CASE's result variables.
                     */
                    ExecInitExprRec(when->result, state, resv, resnull);
                    /* Emit JUMP step to jump to end of CASE's code */
                    scratch.opcode = EEOP_JUMP;
                    scratch.d.jump.jumpdone = -1;   /* computed later */
                    ExprEvalPushStep(state, &scratch);
                    /*
                     * Don't know address for that jump yet, compute once the
                     * whole CASE expression is built.
                     */
                    adjust_jumps = lappend_int(adjust_jumps,
                                               state->steps_len - 1);
                    /*
                     * But we can set WHEN test's jump target now, to make it
                     * jump to the next WHEN subexpression or the ELSE.
                     */
                    state->steps[whenstep].d.jump.jumpdone = state->steps_len;
                }
                /* transformCaseExpr always adds a default */
                Assert(caseExpr->defresult);
                /* evaluate ELSE expr into CASE's result variables */
                ExecInitExprRec(caseExpr->defresult, state,
                                resv, resnull);
                /* adjust jump targets */
                foreach(lc, adjust_jumps)
                {
                    ExprEvalStep *as = &state->steps[lfirst_int(lc)];
                    Assert(as->opcode == EEOP_JUMP);
                    Assert(as->d.jump.jumpdone == -1);
                    as->d.jump.jumpdone = state->steps_len;
                }
                break;
            }
        case T_CaseTestExpr:
            {
                /*
                 * Read from location identified by innermost_caseval.  Note
                 * that innermost_caseval could be NULL, if this node isn't
                 * actually within a CaseExpr, ArrayCoerceExpr, etc structure.
                 * That can happen because some parts of the system abuse
                 * CaseTestExpr to cause a read of a value externally supplied
                 * in econtext->caseValue_datum.  We'll take care of that
                 * scenario at runtime.
                 */
                scratch.opcode = EEOP_CASE_TESTVAL;
                scratch.d.casetest.value = state->innermost_caseval;
                scratch.d.casetest.isnull = state->innermost_casenull;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_ArrayExpr:
            {
                ArrayExpr  *arrayexpr = (ArrayExpr *) node;
                int         nelems = list_length(arrayexpr->elements);
                ListCell   *lc;
                int         elemoff;
                /*
                 * Evaluate by computing each element, and then forming the
                 * array.  Elements are computed into scratch arrays
                 * associated with the ARRAYEXPR step.
                 */
                scratch.opcode = EEOP_ARRAYEXPR;
                scratch.d.arrayexpr.elemvalues =
                    (Datum *) palloc(sizeof(Datum) * nelems);
                scratch.d.arrayexpr.elemnulls =
                    (bool *) palloc(sizeof(bool) * nelems);
                scratch.d.arrayexpr.nelems = nelems;
                /* fill remaining fields of step */
                scratch.d.arrayexpr.multidims = arrayexpr->multidims;
                scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
                /* do one-time catalog lookup for type info */
                get_typlenbyvalalign(arrayexpr->element_typeid,
                                     &scratch.d.arrayexpr.elemlength,
                                     &scratch.d.arrayexpr.elembyval,
                                     &scratch.d.arrayexpr.elemalign);
                /* prepare to evaluate all arguments */
                elemoff = 0;
                foreach(lc, arrayexpr->elements)
                {
                    Expr       *e = (Expr *) lfirst(lc);
                    ExecInitExprRec(e, state,
                                    &scratch.d.arrayexpr.elemvalues[elemoff],
                                    &scratch.d.arrayexpr.elemnulls[elemoff]);
                    elemoff++;
                }
                /* and then collect all into an array */
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_RowExpr:
            {
                RowExpr    *rowexpr = (RowExpr *) node;
                int         nelems = list_length(rowexpr->args);
                TupleDesc   tupdesc;
                int         i;
                ListCell   *l;
                /* Build tupdesc to describe result tuples */
                if (rowexpr->row_typeid == RECORDOID)
                {
                    /* generic record, use types of given expressions */
                    tupdesc = ExecTypeFromExprList(rowexpr->args);
                }
                else
                {
                    /* it's been cast to a named type, use that */
                    tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
                }
                /* In either case, adopt RowExpr's column aliases */
                ExecTypeSetColNames(tupdesc, rowexpr->colnames);
                /* Bless the tupdesc in case it's now of type RECORD */
                BlessTupleDesc(tupdesc);
                /*
                 * In the named-type case, the tupdesc could have more columns
                 * than are in the args list, since the type might have had
                 * columns added since the ROW() was parsed.  We want those
                 * extra columns to go to nulls, so we make sure that the
                 * workspace arrays are large enough and then initialize any
                 * extra columns to read as NULLs.
                 */
                Assert(nelems <= tupdesc->natts);
                nelems = Max(nelems, tupdesc->natts);
                /*
                 * Evaluate by first building datums for each field, and then
                 * a final step forming the composite datum.
                 */
                scratch.opcode = EEOP_ROW;
                scratch.d.row.tupdesc = tupdesc;
                /* space for the individual field datums */
                scratch.d.row.elemvalues =
                    (Datum *) palloc(sizeof(Datum) * nelems);
                scratch.d.row.elemnulls =
                    (bool *) palloc(sizeof(bool) * nelems);
                /* as explained above, make sure any extra columns are null */
                memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems);
                /* Set up evaluation, skipping any deleted columns */
                i = 0;
                foreach(l, rowexpr->args)
                {
                    Form_pg_attribute att = TupleDescAttr(tupdesc, i);
                    Expr       *e = (Expr *) lfirst(l);
                    if (!att->attisdropped)
                    {
                        /*
                         * Guard against ALTER COLUMN TYPE on rowtype since
                         * the RowExpr was created.  XXX should we check
                         * typmod too?  Not sure we can be sure it'll be the
                         * same.
                         */
                        if (exprType((Node *) e) != att->atttypid)
                            ereport(ERROR,
                                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                                     errmsg("ROW() column has type %s instead of type %s",
                                            format_type_be(exprType((Node *) e)),
                                            format_type_be(att->atttypid))));
                    }
                    else
                    {
                        /*
                         * Ignore original expression and insert a NULL. We
                         * don't really care what type of NULL it is, so
                         * always make an int4 NULL.
                         */
                        e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
                    }
                    /* Evaluate column expr into appropriate workspace slot */
                    ExecInitExprRec(e, state,
                                    &scratch.d.row.elemvalues[i],
                                    &scratch.d.row.elemnulls[i]);
                    i++;
                }
                /* And finally build the row value */
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_RowCompareExpr:
            {
                RowCompareExpr *rcexpr = (RowCompareExpr *) node;
                int         nopers = list_length(rcexpr->opnos);
                List       *adjust_jumps = NIL;
                ListCell   *l_left_expr,
                           *l_right_expr,
                           *l_opno,
                           *l_opfamily,
                           *l_inputcollid;
                ListCell   *lc;
                int         off;
                /*
                 * Iterate over each field, prepare comparisons.  To handle
                 * NULL results, prepare jumps to after the expression.  If a
                 * comparison yields a != 0 result, jump to the final step.
                 */
                Assert(list_length(rcexpr->largs) == nopers);
                Assert(list_length(rcexpr->rargs) == nopers);
                Assert(list_length(rcexpr->opfamilies) == nopers);
                Assert(list_length(rcexpr->inputcollids) == nopers);
                off = 0;
                for (off = 0,
                     l_left_expr = list_head(rcexpr->largs),
                     l_right_expr = list_head(rcexpr->rargs),
                     l_opno = list_head(rcexpr->opnos),
                     l_opfamily = list_head(rcexpr->opfamilies),
                     l_inputcollid = list_head(rcexpr->inputcollids);
                     off < nopers;
                     off++,
                     l_left_expr = lnext(l_left_expr),
                     l_right_expr = lnext(l_right_expr),
                     l_opno = lnext(l_opno),
                     l_opfamily = lnext(l_opfamily),
                     l_inputcollid = lnext(l_inputcollid))
                {
                    Expr       *left_expr = (Expr *) lfirst(l_left_expr);
                    Expr       *right_expr = (Expr *) lfirst(l_right_expr);
                    Oid         opno = lfirst_oid(l_opno);
                    Oid         opfamily = lfirst_oid(l_opfamily);
                    Oid         inputcollid = lfirst_oid(l_inputcollid);
                    int         strategy;
                    Oid         lefttype;
                    Oid         righttype;
                    Oid         proc;
                    FmgrInfo   *finfo;
                    FunctionCallInfo fcinfo;
                    get_op_opfamily_properties(opno, opfamily, false,
                                               &strategy,
                                               &lefttype,
                                               &righttype);
                    proc = get_opfamily_proc(opfamily,
                                             lefttype,
                                             righttype,
                                             BTORDER_PROC);
                    if (!OidIsValid(proc))
                        elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
                             BTORDER_PROC, lefttype, righttype, opfamily);
                    /* Set up the primary fmgr lookup information */
                    finfo = palloc0(sizeof(FmgrInfo));
                    fcinfo = palloc0(sizeof(FunctionCallInfoData));
                    fmgr_info(proc, finfo);
                    fmgr_info_set_expr((Node *) node, finfo);
                    InitFunctionCallInfoData(*fcinfo, finfo, 2,
                                             inputcollid, NULL, NULL);
                    /*
                     * If we enforced permissions checks on index support
                     * functions, we'd need to make a check here.  But the
                     * index support machinery doesn't do that, and thus
                     * neither does this code.
                     */
                    /* evaluate left and right args directly into fcinfo */
                    ExecInitExprRec(left_expr, state,
                                    &fcinfo->arg[0], &fcinfo->argnull[0]);
                    ExecInitExprRec(right_expr, state,
                                    &fcinfo->arg[1], &fcinfo->argnull[1]);
                    scratch.opcode = EEOP_ROWCOMPARE_STEP;
                    scratch.d.rowcompare_step.finfo = finfo;
                    scratch.d.rowcompare_step.fcinfo_data = fcinfo;
                    scratch.d.rowcompare_step.fn_addr = finfo->fn_addr;
                    /* jump targets filled below */
                    scratch.d.rowcompare_step.jumpnull = -1;
                    scratch.d.rowcompare_step.jumpdone = -1;
                    ExprEvalPushStep(state, &scratch);
                    adjust_jumps = lappend_int(adjust_jumps,
                                               state->steps_len - 1);
                }
                /*
                 * We could have a zero-column rowtype, in which case the rows
                 * necessarily compare equal.
                 */
                if (nopers == 0)
                {
                    scratch.opcode = EEOP_CONST;
                    scratch.d.constval.value = Int32GetDatum(0);
                    scratch.d.constval.isnull = false;
                    ExprEvalPushStep(state, &scratch);
                }
                /* Finally, examine the last comparison result */
                scratch.opcode = EEOP_ROWCOMPARE_FINAL;
                scratch.d.rowcompare_final.rctype = rcexpr->rctype;
                ExprEvalPushStep(state, &scratch);
                /* adjust jump targetss */
                foreach(lc, adjust_jumps)
                {
                    ExprEvalStep *as = &state->steps[lfirst_int(lc)];
                    Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
                    Assert(as->d.rowcompare_step.jumpdone == -1);
                    Assert(as->d.rowcompare_step.jumpnull == -1);
                    /* jump to comparison evaluation */
                    as->d.rowcompare_step.jumpdone = state->steps_len - 1;
                    /* jump to the following expression */
                    as->d.rowcompare_step.jumpnull = state->steps_len;
                }
                break;
            }
        case T_CoalesceExpr:
            {
                CoalesceExpr *coalesce = (CoalesceExpr *) node;
                List       *adjust_jumps = NIL;
                ListCell   *lc;
                /* We assume there's at least one arg */
                Assert(coalesce->args != NIL);
                /*
                 * Prepare evaluation of all coalesced arguments, after each
                 * one push a step that short-circuits if not null.
                 */
                foreach(lc, coalesce->args)
                {
                    Expr       *e = (Expr *) lfirst(lc);
                    /* evaluate argument, directly into result datum */
                    ExecInitExprRec(e, state, resv, resnull);
                    /* if it's not null, skip to end of COALESCE expr */
                    scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
                    scratch.d.jump.jumpdone = -1;   /* adjust later */
                    ExprEvalPushStep(state, &scratch);
                    adjust_jumps = lappend_int(adjust_jumps,
                                               state->steps_len - 1);
                }
                /*
                 * No need to add a constant NULL return - we only can get to
                 * the end of the expression if a NULL already is being
                 * returned.
                 */
                /* adjust jump targets */
                foreach(lc, adjust_jumps)
                {
                    ExprEvalStep *as = &state->steps[lfirst_int(lc)];
                    Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
                    Assert(as->d.jump.jumpdone == -1);
                    as->d.jump.jumpdone = state->steps_len;
                }
                break;
            }
        case T_MinMaxExpr:
            {
                MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
                int         nelems = list_length(minmaxexpr->args);
                TypeCacheEntry *typentry;
                FmgrInfo   *finfo;
                FunctionCallInfo fcinfo;
                ListCell   *lc;
                int         off;
                /* Look up the btree comparison function for the datatype */
                typentry = lookup_type_cache(minmaxexpr->minmaxtype,
                                             TYPECACHE_CMP_PROC);
                if (!OidIsValid(typentry->cmp_proc))
                    ereport(ERROR,
                            (errcode(ERRCODE_UNDEFINED_FUNCTION),
                             errmsg("could not identify a comparison function for type %s",
                                    format_type_be(minmaxexpr->minmaxtype))));
                /*
                 * If we enforced permissions checks on index support
                 * functions, we'd need to make a check here.  But the index
                 * support machinery doesn't do that, and thus neither does
                 * this code.
                 */
                /* Perform function lookup */
                finfo = palloc0(sizeof(FmgrInfo));
                fcinfo = palloc0(sizeof(FunctionCallInfoData));
                fmgr_info(typentry->cmp_proc, finfo);
                fmgr_info_set_expr((Node *) node, finfo);
                InitFunctionCallInfoData(*fcinfo, finfo, 2,
                                         minmaxexpr->inputcollid, NULL, NULL);
                scratch.opcode = EEOP_MINMAX;
                /* allocate space to store arguments */
                scratch.d.minmax.values =
                    (Datum *) palloc(sizeof(Datum) * nelems);
                scratch.d.minmax.nulls =
                    (bool *) palloc(sizeof(bool) * nelems);
                scratch.d.minmax.nelems = nelems;
                scratch.d.minmax.op = minmaxexpr->op;
                scratch.d.minmax.finfo = finfo;
                scratch.d.minmax.fcinfo_data = fcinfo;
                /* evaluate expressions into minmax->values/nulls */
                off = 0;
                foreach(lc, minmaxexpr->args)
                {
                    Expr       *e = (Expr *) lfirst(lc);
                    ExecInitExprRec(e, state,
                                    &scratch.d.minmax.values[off],
                                    &scratch.d.minmax.nulls[off]);
                    off++;
                }
                /* and push the final comparison */
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_SQLValueFunction:
            {
                SQLValueFunction *svf = (SQLValueFunction *) node;
                scratch.opcode = EEOP_SQLVALUEFUNCTION;
                scratch.d.sqlvaluefunction.svf = svf;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_XmlExpr:
            {
                XmlExpr    *xexpr = (XmlExpr *) node;
                int         nnamed = list_length(xexpr->named_args);
                int         nargs = list_length(xexpr->args);
                int         off;
                ListCell   *arg;
                scratch.opcode = EEOP_XMLEXPR;
                scratch.d.xmlexpr.xexpr = xexpr;
                /* allocate space for storing all the arguments */
                if (nnamed)
                {
                    scratch.d.xmlexpr.named_argvalue =
                        (Datum *) palloc(sizeof(Datum) * nnamed);
                    scratch.d.xmlexpr.named_argnull =
                        (bool *) palloc(sizeof(bool) * nnamed);
                }
                else
                {
                    scratch.d.xmlexpr.named_argvalue = NULL;
                    scratch.d.xmlexpr.named_argnull = NULL;
                }
                if (nargs)
                {
                    scratch.d.xmlexpr.argvalue =
                        (Datum *) palloc(sizeof(Datum) * nargs);
                    scratch.d.xmlexpr.argnull =
                        (bool *) palloc(sizeof(bool) * nargs);
                }
                else
                {
                    scratch.d.xmlexpr.argvalue = NULL;
                    scratch.d.xmlexpr.argnull = NULL;
                }
                /* prepare argument execution */
                off = 0;
                foreach(arg, xexpr->named_args)
                {
                    Expr       *e = (Expr *) lfirst(arg);
                    ExecInitExprRec(e, state,
                                    &scratch.d.xmlexpr.named_argvalue[off],
                                    &scratch.d.xmlexpr.named_argnull[off]);
                    off++;
                }
                off = 0;
                foreach(arg, xexpr->args)
                {
                    Expr       *e = (Expr *) lfirst(arg);
                    ExecInitExprRec(e, state,
                                    &scratch.d.xmlexpr.argvalue[off],
                                    &scratch.d.xmlexpr.argnull[off]);
                    off++;
                }
                /* and evaluate the actual XML expression */
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_NullTest:
            {
                NullTest   *ntest = (NullTest *) node;
                if (ntest->nulltesttype == IS_NULL)
                {
                    if (ntest->argisrow)
                        scratch.opcode = EEOP_NULLTEST_ROWISNULL;
                    else
                        scratch.opcode = EEOP_NULLTEST_ISNULL;
                }
                else if (ntest->nulltesttype == IS_NOT_NULL)
                {
                    if (ntest->argisrow)
                        scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL;
                    else
                        scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
                }
                else
                {
                    elog(ERROR, "unrecognized nulltesttype: %d",
                         (int) ntest->nulltesttype);
                }
                /* initialize cache in case it's a row test */
                scratch.d.nulltest_row.argdesc = NULL;
                /* first evaluate argument into result variable */
                ExecInitExprRec(ntest->arg, state,
                                resv, resnull);
                /* then push the test of that argument */
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_BooleanTest:
            {
                BooleanTest *btest = (BooleanTest *) node;
                /*
                 * Evaluate argument, directly into result datum.  That's ok,
                 * because resv/resnull is definitely not used anywhere else,
                 * and will get overwritten by the below EEOP_BOOLTEST_IS_*
                 * step.
                 */
                ExecInitExprRec(btest->arg, state, resv, resnull);
                switch (btest->booltesttype)
                {
                    case IS_TRUE:
                        scratch.opcode = EEOP_BOOLTEST_IS_TRUE;
                        break;
                    case IS_NOT_TRUE:
                        scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
                        break;
                    case IS_FALSE:
                        scratch.opcode = EEOP_BOOLTEST_IS_FALSE;
                        break;
                    case IS_NOT_FALSE:
                        scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
                        break;
                    case IS_UNKNOWN:
                        /* Same as scalar IS NULL test */
                        scratch.opcode = EEOP_NULLTEST_ISNULL;
                        break;
                    case IS_NOT_UNKNOWN:
                        /* Same as scalar IS NOT NULL test */
                        scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
                        break;
                    default:
                        elog(ERROR, "unrecognized booltesttype: %d",
                             (int) btest->booltesttype);
                }
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_CoerceToDomain:
            {
                CoerceToDomain *ctest = (CoerceToDomain *) node;
                ExecInitCoerceToDomain(&scratch, ctest, state,
                                       resv, resnull);
                break;
            }
        case T_CoerceToDomainValue:
            {
                /*
                 * Read from location identified by innermost_domainval.  Note
                 * that innermost_domainval could be NULL, if we're compiling
                 * a standalone domain check rather than one embedded in a
                 * larger expression.  In that case we must read from
                 * econtext->domainValue_datum.  We'll take care of that
                 * scenario at runtime.
                 */
                scratch.opcode = EEOP_DOMAIN_TESTVAL;
                /* we share instruction union variant with case testval */
                scratch.d.casetest.value = state->innermost_domainval;
                scratch.d.casetest.isnull = state->innermost_domainnull;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_CurrentOfExpr:
            {
                scratch.opcode = EEOP_CURRENTOFEXPR;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        case T_NextValueExpr:
            {
                NextValueExpr *nve = (NextValueExpr *) node;
                scratch.opcode = EEOP_NEXTVALUEEXPR;
                scratch.d.nextvalueexpr.seqid = nve->seqid;
                scratch.d.nextvalueexpr.seqtypid = nve->typeId;
                ExprEvalPushStep(state, &scratch);
                break;
            }
        default:
            elog(ERROR, "unrecognized node type: %d",
                 (int) nodeTag(node));
            break;
    }
}
/*
 * Add another expression evaluation step to ExprState->steps.
 * 添加表达式解析步骤到ExprState->steps数组中.
 *
 * Note that this potentially re-allocates es->steps, therefore no pointer
 * into that array may be used while the expression is still being built.
 */
void
ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
{
    if (es->steps_alloc == 0)
    {
        es->steps_alloc = 16;
        es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
    }
    else if (es->steps_alloc == es->steps_len)
    {
        es->steps_alloc *= 2;
        es->steps = repalloc(es->steps,
                             sizeof(ExprEvalStep) * es->steps_alloc);
    }
    memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
}

三、跟踪分析

测试脚本

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

进入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)

到此,相信大家对“怎么使用PostgreSQL中ExecInitExprRec函数”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI