中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

?PostgreSQL如何解析查詢語句中的表達式列并計算得出該列的值

發布時間:2021-11-09 09:25:13 來源:億速云 閱讀:176 作者:小新 欄目:關系型數據庫

這篇文章主要介紹PostgreSQL如何解析查詢語句中的表達式列并計算得出該列的值,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

表達式列是指除關系定義中的系統列/定義列之外的其他投影列.比如:

testdb=# create table t_expr(id int);
CREATE TABLE
testdb=# insert into t_expr values(1);
INSERT 0 1
testdb=# insert into t_expr values(2);
INSERT 0 1
testdb=# alter table t_expr add column c2 varchar(20);
ALTER TABLE
testdb=# insert into t_expr(id,c2) select x,'c2'||x from generate_series(3,100000) as x;
INSERT 0 99998
testdb=# select 1+id from t_expr;

該SQL語句中的”1+id”投影列視為表達式列.

一、數據結構

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;

二、源碼解讀

ExecInterpExpr函數是實現表達式列解析的主函數,解析給定”econtext”在執行上下文中通過”state”標識的表達式.
其主要實現邏輯是根據ExprState->opcode(ExprState的初始化后續再行介紹)指定的操作指定,調整到相應的實現邏輯中,執行完畢調到下一個步驟直至到達EEOP_DONE,即完成所有步驟.

/*
 * Evaluate expression identified by "state" in the execution context
 * given by "econtext".  *isnull is set to the is-null flag for the result,
 * and the Datum value is the function result.
 * 解析給定"econtext"在執行上下文中通過"state"標識的表達式.
 * *isnull用于設置結果是否為null,Datum是函數執行的結果.
 *
 * As a special case, return the dispatch table's address if state is NULL.
 * This is used by ExecInitInterpreter to set up the dispatch_table global.
 * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
 * 作為一個特別的情況,如state為NULL,返回分發器表的地址.
 * 這個情況用于ExecInitInterpreter配置dispatch_table.
 * (只是在定義了EEO_USE_COMPUTED_GOTO時才應用)
 */
static Datum
ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
{
    ExprEvalStep *op;
    TupleTableSlot *resultslot;
    TupleTableSlot *innerslot;
    TupleTableSlot *outerslot;
    TupleTableSlot *scanslot;
    /*
     * This array has to be in the same order as enum ExprEvalOp.
     * 該數組在枚舉類型ExprEvalOp中具有同樣的順序
     */
#if defined(EEO_USE_COMPUTED_GOTO)
    static const void *const dispatch_table[] = {
        &&CASE_EEOP_DONE,
        &&CASE_EEOP_INNER_FETCHSOME,
        &&CASE_EEOP_OUTER_FETCHSOME,
        &&CASE_EEOP_SCAN_FETCHSOME,
        &&CASE_EEOP_INNER_VAR,
        &&CASE_EEOP_OUTER_VAR,
        &&CASE_EEOP_SCAN_VAR,
        &&CASE_EEOP_INNER_SYSVAR,
        &&CASE_EEOP_OUTER_SYSVAR,
        &&CASE_EEOP_SCAN_SYSVAR,
        &&CASE_EEOP_WHOLEROW,
        &&CASE_EEOP_ASSIGN_INNER_VAR,
        &&CASE_EEOP_ASSIGN_OUTER_VAR,
        &&CASE_EEOP_ASSIGN_SCAN_VAR,
        &&CASE_EEOP_ASSIGN_TMP,
        &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
        &&CASE_EEOP_CONST,
        &&CASE_EEOP_FUNCEXPR,
        &&CASE_EEOP_FUNCEXPR_STRICT,
        &&CASE_EEOP_FUNCEXPR_FUSAGE,
        &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
        &&CASE_EEOP_BOOL_AND_STEP_FIRST,
        &&CASE_EEOP_BOOL_AND_STEP,
        &&CASE_EEOP_BOOL_AND_STEP_LAST,
        &&CASE_EEOP_BOOL_OR_STEP_FIRST,
        &&CASE_EEOP_BOOL_OR_STEP,
        &&CASE_EEOP_BOOL_OR_STEP_LAST,
        &&CASE_EEOP_BOOL_NOT_STEP,
        &&CASE_EEOP_QUAL,
        &&CASE_EEOP_JUMP,
        &&CASE_EEOP_JUMP_IF_NULL,
        &&CASE_EEOP_JUMP_IF_NOT_NULL,
        &&CASE_EEOP_JUMP_IF_NOT_TRUE,
        &&CASE_EEOP_NULLTEST_ISNULL,
        &&CASE_EEOP_NULLTEST_ISNOTNULL,
        &&CASE_EEOP_NULLTEST_ROWISNULL,
        &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
        &&CASE_EEOP_BOOLTEST_IS_TRUE,
        &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
        &&CASE_EEOP_BOOLTEST_IS_FALSE,
        &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
        &&CASE_EEOP_PARAM_EXEC,
        &&CASE_EEOP_PARAM_EXTERN,
        &&CASE_EEOP_PARAM_CALLBACK,
        &&CASE_EEOP_CASE_TESTVAL,
        &&CASE_EEOP_MAKE_READONLY,
        &&CASE_EEOP_IOCOERCE,
        &&CASE_EEOP_DISTINCT,
        &&CASE_EEOP_NOT_DISTINCT,
        &&CASE_EEOP_NULLIF,
        &&CASE_EEOP_SQLVALUEFUNCTION,
        &&CASE_EEOP_CURRENTOFEXPR,
        &&CASE_EEOP_NEXTVALUEEXPR,
        &&CASE_EEOP_ARRAYEXPR,
        &&CASE_EEOP_ARRAYCOERCE,
        &&CASE_EEOP_ROW,
        &&CASE_EEOP_ROWCOMPARE_STEP,
        &&CASE_EEOP_ROWCOMPARE_FINAL,
        &&CASE_EEOP_MINMAX,
        &&CASE_EEOP_FIELDSELECT,
        &&CASE_EEOP_FIELDSTORE_DEFORM,
        &&CASE_EEOP_FIELDSTORE_FORM,
        &&CASE_EEOP_ARRAYREF_SUBSCRIPT,
        &&CASE_EEOP_ARRAYREF_OLD,
        &&CASE_EEOP_ARRAYREF_ASSIGN,
        &&CASE_EEOP_ARRAYREF_FETCH,
        &&CASE_EEOP_DOMAIN_TESTVAL,
        &&CASE_EEOP_DOMAIN_NOTNULL,
        &&CASE_EEOP_DOMAIN_CHECK,
        &&CASE_EEOP_CONVERT_ROWTYPE,
        &&CASE_EEOP_SCALARARRAYOP,
        &&CASE_EEOP_XMLEXPR,
        &&CASE_EEOP_AGGREF,
        &&CASE_EEOP_GROUPING_FUNC,
        &&CASE_EEOP_WINDOW_FUNC,
        &&CASE_EEOP_SUBPLAN,
        &&CASE_EEOP_ALTERNATIVE_SUBPLAN,
        &&CASE_EEOP_AGG_STRICT_DESERIALIZE,
        &&CASE_EEOP_AGG_DESERIALIZE,
        &&CASE_EEOP_AGG_STRICT_INPUT_CHECK,
        &&CASE_EEOP_AGG_INIT_TRANS,
        &&CASE_EEOP_AGG_STRICT_TRANS_CHECK,
        &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
        &&CASE_EEOP_AGG_PLAIN_TRANS,
        &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM,
        &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE,
        &&CASE_EEOP_LAST
    };
    StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table),
                     "dispatch_table out of whack with ExprEvalOp");
    if (unlikely(state == NULL))
        //如state == NULL,則調用PointerGetDatum
        return PointerGetDatum(dispatch_table);
#else
    Assert(state != NULL);
#endif                          /* EEO_USE_COMPUTED_GOTO */
    /* setup state */
    //配置狀態變量
    op = state->steps;
    resultslot = state->resultslot;
    innerslot = econtext->ecxt_innertuple;
    outerslot = econtext->ecxt_outertuple;
    scanslot = econtext->ecxt_scantuple;
#if defined(EEO_USE_COMPUTED_GOTO)
    EEO_DISPATCH();//分發
#endif
    EEO_SWITCH()
    {
        EEO_CASE(EEOP_DONE)
        {
            goto out;
        }
        EEO_CASE(EEOP_INNER_FETCHSOME)
        {
            /* XXX: worthwhile to check tts_nvalid inline first? */
            slot_getsomeattrs(innerslot, op->d.fetch.last_var);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_OUTER_FETCHSOME)
        {
            slot_getsomeattrs(outerslot, op->d.fetch.last_var);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SCAN_FETCHSOME)
        {
            slot_getsomeattrs(scanslot, op->d.fetch.last_var);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_INNER_VAR)
        {
            int         attnum = op->d.var.attnum;
            /*
             * Since we already extracted all referenced columns from the
             * tuple with a FETCHSOME step, we can just grab the value
             * directly out of the slot's decomposed-data arrays.  But let's
             * have an Assert to check that that did happen.
             */
            Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
            *op->resvalue = innerslot->tts_values[attnum];
            *op->resnull = innerslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_OUTER_VAR)
        {
            int         attnum = op->d.var.attnum;
            /* See EEOP_INNER_VAR comments */
            Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
            *op->resvalue = outerslot->tts_values[attnum];
            *op->resnull = outerslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SCAN_VAR)
        {
            int         attnum = op->d.var.attnum;
            /* See EEOP_INNER_VAR comments */
            Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
            *op->resvalue = scanslot->tts_values[attnum];
            *op->resnull = scanslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_INNER_SYSVAR)
        {
            int         attnum = op->d.var.attnum;
            Datum       d;
            /* these asserts must match defenses in slot_getattr */
            Assert(innerslot->tts_tuple != NULL);
            Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
            /* heap_getsysattr has sufficient defenses against bad attnums */
            d = heap_getsysattr(innerslot->tts_tuple, attnum,
                                innerslot->tts_tupleDescriptor,
                                op->resnull);
            *op->resvalue = d;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_OUTER_SYSVAR)
        {
            int         attnum = op->d.var.attnum;
            Datum       d;
            /* these asserts must match defenses in slot_getattr */
            Assert(outerslot->tts_tuple != NULL);
            Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
            /* heap_getsysattr has sufficient defenses against bad attnums */
            d = heap_getsysattr(outerslot->tts_tuple, attnum,
                                outerslot->tts_tupleDescriptor,
                                op->resnull);
            *op->resvalue = d;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SCAN_SYSVAR)
        {
            int         attnum = op->d.var.attnum;
            Datum       d;
            /* these asserts must match defenses in slot_getattr */
            Assert(scanslot->tts_tuple != NULL);
            Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
            /* heap_getsysattr has sufficient defenses against bad attnums */
            d = heap_getsysattr(scanslot->tts_tuple, attnum,
                                scanslot->tts_tupleDescriptor,
                                op->resnull);
            *op->resvalue = d;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_WHOLEROW)
        {
            /* too complex for an inline implementation */
            ExecEvalWholeRowVar(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ASSIGN_INNER_VAR)
        {
            int         resultnum = op->d.assign_var.resultnum;
            int         attnum = op->d.assign_var.attnum;
            /*
             * We do not need CheckVarSlotCompatibility here; that was taken
             * care of at compilation time.  But see EEOP_INNER_VAR comments.
             */
            Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
            resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
            resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
        {
            int         resultnum = op->d.assign_var.resultnum;
            int         attnum = op->d.assign_var.attnum;
            /*
             * We do not need CheckVarSlotCompatibility here; that was taken
             * care of at compilation time.  But see EEOP_INNER_VAR comments.
             */
            Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
            resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
            resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
        {
            int         resultnum = op->d.assign_var.resultnum;
            int         attnum = op->d.assign_var.attnum;
            /*
             * We do not need CheckVarSlotCompatibility here; that was taken
             * care of at compilation time.  But see EEOP_INNER_VAR comments.
             */
            Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
            resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
            resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ASSIGN_TMP)
        {
            int         resultnum = op->d.assign_tmp.resultnum;
            resultslot->tts_values[resultnum] = state->resvalue;
            resultslot->tts_isnull[resultnum] = state->resnull;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
        {
            int         resultnum = op->d.assign_tmp.resultnum;
            resultslot->tts_isnull[resultnum] = state->resnull;
            if (!resultslot->tts_isnull[resultnum])
                resultslot->tts_values[resultnum] =
                    MakeExpandedObjectReadOnlyInternal(state->resvalue);
            else
                resultslot->tts_values[resultnum] = state->resvalue;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_CONST)
        {
            *op->resnull = op->d.constval.isnull;
            *op->resvalue = op->d.constval.value;
            EEO_NEXT();
        }
        /*
         * Function-call implementations. Arguments have previously been
         * evaluated directly into fcinfo->args.
         *
         * As both STRICT checks and function-usage are noticeable performance
         * wise, and function calls are a very hot-path (they also back
         * operators!), it's worth having so many separate opcodes.
         *
         * Note: the reason for using a temporary variable "d", here and in
         * other places, is that some compilers think "*op->resvalue = f();"
         * requires them to evaluate op->resvalue into a register before
         * calling f(), just in case f() is able to modify op->resvalue
         * somehow.  The extra line of code can save a useless register spill
         * and reload across the function call.
         */
        EEO_CASE(EEOP_FUNCEXPR)
        {
            FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
            Datum       d;
            fcinfo->isnull = false;
            d = op->d.func.fn_addr(fcinfo);
            *op->resvalue = d;
            *op->resnull = fcinfo->isnull;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FUNCEXPR_STRICT)
        {
            FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
            bool       *argnull = fcinfo->argnull;
            int         argno;
            Datum       d;
            /* strict function, so check for NULL args */
            for (argno = 0; argno < op->d.func.nargs; argno++)
            {
                if (argnull[argno])
                {
                    *op->resnull = true;
                    goto strictfail;
                }
            }
            fcinfo->isnull = false;
            d = op->d.func.fn_addr(fcinfo);
            *op->resvalue = d;
            *op->resnull = fcinfo->isnull;
    strictfail:
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
        {
            /* not common enough to inline */
            ExecEvalFuncExprFusage(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
        {
            /* not common enough to inline */
            ExecEvalFuncExprStrictFusage(state, op, econtext);
            EEO_NEXT();
        }
        /*
         * If any of its clauses is FALSE, an AND's result is FALSE regardless
         * of the states of the rest of the clauses, so we can stop evaluating
         * and return FALSE immediately.  If none are FALSE and one or more is
         * NULL, we return NULL; otherwise we return TRUE.  This makes sense
         * when you interpret NULL as "don't know": perhaps one of the "don't
         * knows" would have been FALSE if we'd known its value.  Only when
         * all the inputs are known to be TRUE can we state confidently that
         * the AND's result is TRUE.
         */
        EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
        {
            *op->d.boolexpr.anynull = false;
            /*
             * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
             * same as EEOP_BOOL_AND_STEP - so fall through to that.
             */
            /* FALL THROUGH */
        }
        EEO_CASE(EEOP_BOOL_AND_STEP)
        {
            if (*op->resnull)
            {
                *op->d.boolexpr.anynull = true;
            }
            else if (!DatumGetBool(*op->resvalue))
            {
                /* result is already set to FALSE, need not change it */
                /* bail out early */
                EEO_JUMP(op->d.boolexpr.jumpdone);
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
        {
            if (*op->resnull)
            {
                /* result is already set to NULL, need not change it */
            }
            else if (!DatumGetBool(*op->resvalue))
            {
                /* result is already set to FALSE, need not change it */
                /*
                 * No point jumping early to jumpdone - would be same target
                 * (as this is the last argument to the AND expression),
                 * except more expensive.
                 */
            }
            else if (*op->d.boolexpr.anynull)
            {
                *op->resvalue = (Datum) 0;
                *op->resnull = true;
            }
            else
            {
                /* result is already set to TRUE, need not change it */
            }
            EEO_NEXT();
        }
        /*
         * If any of its clauses is TRUE, an OR's result is TRUE regardless of
         * the states of the rest of the clauses, so we can stop evaluating
         * and return TRUE immediately.  If none are TRUE and one or more is
         * NULL, we return NULL; otherwise we return FALSE.  This makes sense
         * when you interpret NULL as "don't know": perhaps one of the "don't
         * knows" would have been TRUE if we'd known its value.  Only when all
         * the inputs are known to be FALSE can we state confidently that the
         * OR's result is FALSE.
         */
        EEO_CASE(EEOP_BOOL_OR_STEP_FIRST)
        {
            *op->d.boolexpr.anynull = false;
            /*
             * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
             * as EEOP_BOOL_OR_STEP - so fall through to that.
             */
            /* FALL THROUGH */
        }
        EEO_CASE(EEOP_BOOL_OR_STEP)
        {
            if (*op->resnull)
            {
                *op->d.boolexpr.anynull = true;
            }
            else if (DatumGetBool(*op->resvalue))
            {
                /* result is already set to TRUE, need not change it */
                /* bail out early */
                EEO_JUMP(op->d.boolexpr.jumpdone);
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
        {
            if (*op->resnull)
            {
                /* result is already set to NULL, need not change it */
            }
            else if (DatumGetBool(*op->resvalue))
            {
                /* result is already set to TRUE, need not change it */
                /*
                 * No point jumping to jumpdone - would be same target (as
                 * this is the last argument to the AND expression), except
                 * more expensive.
                 */
            }
            else if (*op->d.boolexpr.anynull)
            {
                *op->resvalue = (Datum) 0;
                *op->resnull = true;
            }
            else
            {
                /* result is already set to FALSE, need not change it */
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOL_NOT_STEP)
        {
            /*
             * Evaluation of 'not' is simple... if expr is false, then return
             * 'true' and vice versa.  It's safe to do this even on a
             * nominally null value, so we ignore resnull; that means that
             * NULL in produces NULL out, which is what we want.
             */
            *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
            EEO_NEXT();
        }
        EEO_CASE(EEOP_QUAL)
        {
            /* simplified version of BOOL_AND_STEP for use by ExecQual() */
            /* If argument (also result) is false or null ... */
            if (*op->resnull ||
                !DatumGetBool(*op->resvalue))
            {
                /* ... bail out early, returning FALSE */
                *op->resnull = false;
                *op->resvalue = BoolGetDatum(false);
                EEO_JUMP(op->d.qualexpr.jumpdone);
            }
            /*
             * Otherwise, leave the TRUE value in place, in case this is the
             * last qual.  Then, TRUE is the correct answer.
             */
            EEO_NEXT();
        }
        EEO_CASE(EEOP_JUMP)
        {
            /* Unconditionally jump to target step */
            EEO_JUMP(op->d.jump.jumpdone);
        }
        EEO_CASE(EEOP_JUMP_IF_NULL)
        {
            /* Transfer control if current result is null */
            if (*op->resnull)
                EEO_JUMP(op->d.jump.jumpdone);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
        {
            /* Transfer control if current result is non-null */
            if (!*op->resnull)
                EEO_JUMP(op->d.jump.jumpdone);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
        {
            /* Transfer control if current result is null or false */
            if (*op->resnull || !DatumGetBool(*op->resvalue))
                EEO_JUMP(op->d.jump.jumpdone);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NULLTEST_ISNULL)
        {
            *op->resvalue = BoolGetDatum(*op->resnull);
            *op->resnull = false;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
        {
            *op->resvalue = BoolGetDatum(!*op->resnull);
            *op->resnull = false;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NULLTEST_ROWISNULL)
        {
            /* out of line implementation: too large */
            ExecEvalRowNull(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
        {
            /* out of line implementation: too large */
            ExecEvalRowNotNull(state, op, econtext);
            EEO_NEXT();
        }
        /* BooleanTest implementations for all booltesttypes */
        EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
        {
            if (*op->resnull)
            {
                *op->resvalue = BoolGetDatum(false);
                *op->resnull = false;
            }
            /* else, input value is the correct output as well */
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
        {
            if (*op->resnull)
            {
                *op->resvalue = BoolGetDatum(true);
                *op->resnull = false;
            }
            else
                *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
        {
            if (*op->resnull)
            {
                *op->resvalue = BoolGetDatum(false);
                *op->resnull = false;
            }
            else
                *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
            EEO_NEXT();
        }
        EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
        {
            if (*op->resnull)
            {
                *op->resvalue = BoolGetDatum(true);
                *op->resnull = false;
            }
            /* else, input value is the correct output as well */
            EEO_NEXT();
        }
        EEO_CASE(EEOP_PARAM_EXEC)
        {
            /* out of line implementation: too large */
            ExecEvalParamExec(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_PARAM_EXTERN)
        {
            /* out of line implementation: too large */
            ExecEvalParamExtern(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_PARAM_CALLBACK)
        {
            /* allow an extension module to supply a PARAM_EXTERN value */
            op->d.cparam.paramfunc(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_CASE_TESTVAL)
        {
            /*
             * Normally upper parts of the expression tree have setup the
             * values to be returned here, but some parts of the system
             * currently misuse {caseValue,domainValue}_{datum,isNull} to set
             * run-time data.  So if no values have been set-up, use
             * ExprContext's.  This isn't pretty, but also not *that* ugly,
             * and this is unlikely to be performance sensitive enough to
             * worry about an extra branch.
             */
            if (op->d.casetest.value)
            {
                *op->resvalue = *op->d.casetest.value;
                *op->resnull = *op->d.casetest.isnull;
            }
            else
            {
                *op->resvalue = econtext->caseValue_datum;
                *op->resnull = econtext->caseValue_isNull;
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_DOMAIN_TESTVAL)
        {
            /*
             * See EEOP_CASE_TESTVAL comment.
             */
            if (op->d.casetest.value)
            {
                *op->resvalue = *op->d.casetest.value;
                *op->resnull = *op->d.casetest.isnull;
            }
            else
            {
                *op->resvalue = econtext->domainValue_datum;
                *op->resnull = econtext->domainValue_isNull;
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_MAKE_READONLY)
        {
            /*
             * Force a varlena value that might be read multiple times to R/O
             */
            if (!*op->d.make_readonly.isnull)
                *op->resvalue =
                    MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value);
            *op->resnull = *op->d.make_readonly.isnull;
            EEO_NEXT();
        }
        EEO_CASE(EEOP_IOCOERCE)
        {
            /*
             * Evaluate a CoerceViaIO node.  This can be quite a hot path, so
             * inline as much work as possible.  The source value is in our
             * result variable.
             */
            char       *str;
            /* call output function (similar to OutputFunctionCall) */
            if (*op->resnull)
            {
                /* output functions are not called on nulls */
                str = NULL;
            }
            else
            {
                FunctionCallInfo fcinfo_out;
                fcinfo_out = op->d.iocoerce.fcinfo_data_out;
                fcinfo_out->arg[0] = *op->resvalue;
                fcinfo_out->argnull[0] = false;
                fcinfo_out->isnull = false;
                str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
                /* OutputFunctionCall assumes result isn't null */
                Assert(!fcinfo_out->isnull);
            }
            /* call input function (similar to InputFunctionCall) */
            if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
            {
                FunctionCallInfo fcinfo_in;
                Datum       d;
                fcinfo_in = op->d.iocoerce.fcinfo_data_in;
                fcinfo_in->arg[0] = PointerGetDatum(str);
                fcinfo_in->argnull[0] = *op->resnull;
                /* second and third arguments are already set up */
                fcinfo_in->isnull = false;
                d = FunctionCallInvoke(fcinfo_in);
                *op->resvalue = d;
                /* Should get null result if and only if str is NULL */
                if (str == NULL)
                {
                    Assert(*op->resnull);
                    Assert(fcinfo_in->isnull);
                }
                else
                {
                    Assert(!*op->resnull);
                    Assert(!fcinfo_in->isnull);
                }
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_DISTINCT)
        {
            /*
             * IS DISTINCT FROM must evaluate arguments (already done into
             * fcinfo->arg/argnull) to determine whether they are NULL; if
             * either is NULL then the result is determined.  If neither is
             * NULL, then proceed to evaluate the comparison function, which
             * is just the type's standard equality operator.  We need not
             * care whether that function is strict.  Because the handling of
             * nulls is different, we can't just reuse EEOP_FUNCEXPR.
             */
            FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
            /* check function arguments for NULLness */
            if (fcinfo->argnull[0] && fcinfo->argnull[1])
            {
                /* Both NULL? Then is not distinct... */
                *op->resvalue = BoolGetDatum(false);
                *op->resnull = false;
            }
            else if (fcinfo->argnull[0] || fcinfo->argnull[1])
            {
                /* Only one is NULL? Then is distinct... */
                *op->resvalue = BoolGetDatum(true);
                *op->resnull = false;
            }
            else
            {
                /* Neither null, so apply the equality function */
                Datum       eqresult;
                fcinfo->isnull = false;
                eqresult = op->d.func.fn_addr(fcinfo);
                /* Must invert result of "="; safe to do even if null */
                *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
                *op->resnull = fcinfo->isnull;
            }
            EEO_NEXT();
        }
        /* see EEOP_DISTINCT for comments, this is just inverted */
        EEO_CASE(EEOP_NOT_DISTINCT)
        {
            FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
            if (fcinfo->argnull[0] && fcinfo->argnull[1])
            {
                *op->resvalue = BoolGetDatum(true);
                *op->resnull = false;
            }
            else if (fcinfo->argnull[0] || fcinfo->argnull[1])
            {
                *op->resvalue = BoolGetDatum(false);
                *op->resnull = false;
            }
            else
            {
                Datum       eqresult;
                fcinfo->isnull = false;
                eqresult = op->d.func.fn_addr(fcinfo);
                *op->resvalue = eqresult;
                *op->resnull = fcinfo->isnull;
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NULLIF)
        {
            /*
             * The arguments are already evaluated into fcinfo->arg/argnull.
             */
            FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
            /* if either argument is NULL they can't be equal */
            if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
            {
                Datum       result;
                fcinfo->isnull = false;
                result = op->d.func.fn_addr(fcinfo);
                /* if the arguments are equal return null */
                if (!fcinfo->isnull && DatumGetBool(result))
                {
                    *op->resvalue = (Datum) 0;
                    *op->resnull = true;
                    EEO_NEXT();
                }
            }
            /* Arguments aren't equal, so return the first one */
            *op->resvalue = fcinfo->arg[0];
            *op->resnull = fcinfo->argnull[0];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SQLVALUEFUNCTION)
        {
            /*
             * Doesn't seem worthwhile to have an inline implementation
             * efficiency-wise.
             */
            ExecEvalSQLValueFunction(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_CURRENTOFEXPR)
        {
            /* error invocation uses space, and shouldn't ever occur */
            ExecEvalCurrentOfExpr(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_NEXTVALUEEXPR)
        {
            /*
             * Doesn't seem worthwhile to have an inline implementation
             * efficiency-wise.
             */
            ExecEvalNextValueExpr(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ARRAYEXPR)
        {
            /* too complex for an inline implementation */
            ExecEvalArrayExpr(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ARRAYCOERCE)
        {
            /* too complex for an inline implementation */
            ExecEvalArrayCoerce(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ROW)
        {
            /* too complex for an inline implementation */
            ExecEvalRow(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ROWCOMPARE_STEP)
        {
            FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
            Datum       d;
            /* force NULL result if strict fn and NULL input */
            if (op->d.rowcompare_step.finfo->fn_strict &&
                (fcinfo->argnull[0] || fcinfo->argnull[1]))
            {
                *op->resnull = true;
                EEO_JUMP(op->d.rowcompare_step.jumpnull);
            }
            /* Apply comparison function */
            fcinfo->isnull = false;
            d = op->d.rowcompare_step.fn_addr(fcinfo);
            *op->resvalue = d;
            /* force NULL result if NULL function result */
            if (fcinfo->isnull)
            {
                *op->resnull = true;
                EEO_JUMP(op->d.rowcompare_step.jumpnull);
            }
            *op->resnull = false;
            /* If unequal, no need to compare remaining columns */
            if (DatumGetInt32(*op->resvalue) != 0)
            {
                EEO_JUMP(op->d.rowcompare_step.jumpdone);
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ROWCOMPARE_FINAL)
        {
            int32       cmpresult = DatumGetInt32(*op->resvalue);
            RowCompareType rctype = op->d.rowcompare_final.rctype;
            *op->resnull = false;
            switch (rctype)
            {
                    /* EQ and NE cases aren't allowed here */
                case ROWCOMPARE_LT:
                    *op->resvalue = BoolGetDatum(cmpresult < 0);
                    break;
                case ROWCOMPARE_LE:
                    *op->resvalue = BoolGetDatum(cmpresult <= 0);
                    break;
                case ROWCOMPARE_GE:
                    *op->resvalue = BoolGetDatum(cmpresult >= 0);
                    break;
                case ROWCOMPARE_GT:
                    *op->resvalue = BoolGetDatum(cmpresult > 0);
                    break;
                default:
                    Assert(false);
                    break;
            }
            EEO_NEXT();
        }
        EEO_CASE(EEOP_MINMAX)
        {
            /* too complex for an inline implementation */
            ExecEvalMinMax(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FIELDSELECT)
        {
            /* too complex for an inline implementation */
            ExecEvalFieldSelect(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FIELDSTORE_DEFORM)
        {
            /* too complex for an inline implementation */
            ExecEvalFieldStoreDeForm(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_FIELDSTORE_FORM)
        {
            /* too complex for an inline implementation */
            ExecEvalFieldStoreForm(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT)
        {
            /* Process an array subscript */
            /* too complex for an inline implementation */
            if (ExecEvalArrayRefSubscript(state, op))
            {
                EEO_NEXT();
            }
            else
            {
                /* Subscript is null, short-circuit ArrayRef to NULL */
                EEO_JUMP(op->d.arrayref_subscript.jumpdone);
            }
        }
        EEO_CASE(EEOP_ARRAYREF_OLD)
        {
            /*
             * Fetch the old value in an arrayref assignment, in case it's
             * referenced (via a CaseTestExpr) inside the assignment
             * expression.
             */
            /* too complex for an inline implementation */
            ExecEvalArrayRefOld(state, op);
            EEO_NEXT();
        }
        /*
         * Perform ArrayRef assignment
         */
        EEO_CASE(EEOP_ARRAYREF_ASSIGN)
        {
            /* too complex for an inline implementation */
            ExecEvalArrayRefAssign(state, op);
            EEO_NEXT();
        }
        /*
         * Fetch subset of an array.
         */
        EEO_CASE(EEOP_ARRAYREF_FETCH)
        {
            /* too complex for an inline implementation */
            ExecEvalArrayRefFetch(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_CONVERT_ROWTYPE)
        {
            /* too complex for an inline implementation */
            ExecEvalConvertRowtype(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SCALARARRAYOP)
        {
            /* too complex for an inline implementation */
            ExecEvalScalarArrayOp(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_DOMAIN_NOTNULL)
        {
            /* too complex for an inline implementation */
            ExecEvalConstraintNotNull(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_DOMAIN_CHECK)
        {
            /* too complex for an inline implementation */
            ExecEvalConstraintCheck(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_XMLEXPR)
        {
            /* too complex for an inline implementation */
            ExecEvalXmlExpr(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_AGGREF)
        {
            /*
             * Returns a Datum whose value is the precomputed aggregate value
             * found in the given expression context.
             */
            AggrefExprState *aggref = op->d.aggref.astate;
            Assert(econtext->ecxt_aggvalues != NULL);
            *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
            *op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_GROUPING_FUNC)
        {
            /* too complex/uncommon for an inline implementation */
            ExecEvalGroupingFunc(state, op);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_WINDOW_FUNC)
        {
            /*
             * Like Aggref, just return a precomputed value from the econtext.
             */
            WindowFuncExprState *wfunc = op->d.window_func.wfstate;
            Assert(econtext->ecxt_aggvalues != NULL);
            *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
            *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
            EEO_NEXT();
        }
        EEO_CASE(EEOP_SUBPLAN)
        {
            /* too complex for an inline implementation */
            ExecEvalSubPlan(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
        {
            /* too complex for an inline implementation */
            ExecEvalAlternativeSubPlan(state, op, econtext);
            EEO_NEXT();
        }
        /* evaluate a strict aggregate deserialization function */
        EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE)
        {
            bool       *argnull = op->d.agg_deserialize.fcinfo_data->argnull;
            /* Don't call a strict deserialization function with NULL input */
            if (argnull[0])
                EEO_JUMP(op->d.agg_deserialize.jumpnull);
            /* fallthrough */
        }
        /* evaluate aggregate deserialization function (non-strict portion) */
        EEO_CASE(EEOP_AGG_DESERIALIZE)
        {
            FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data;
            AggState   *aggstate = op->d.agg_deserialize.aggstate;
            MemoryContext oldContext;
            /*
             * We run the deserialization functions in per-input-tuple memory
             * context.
             */
            oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
            fcinfo->isnull = false;
            *op->resvalue = FunctionCallInvoke(fcinfo);
            *op->resnull = fcinfo->isnull;
            MemoryContextSwitchTo(oldContext);
            EEO_NEXT();
        }
        /*
         * Check that a strict aggregate transition / combination function's
         * input is not NULL.
         */
        EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK)
        {
            int         argno;
            bool       *nulls = op->d.agg_strict_input_check.nulls;
            int         nargs = op->d.agg_strict_input_check.nargs;
            for (argno = 0; argno < nargs; argno++)
            {
                if (nulls[argno])
                    EEO_JUMP(op->d.agg_strict_input_check.jumpnull);
            }
            EEO_NEXT();
        }
        /*
         * Initialize an aggregate's first value if necessary.
         */
        EEO_CASE(EEOP_AGG_INIT_TRANS)
        {
            AggState   *aggstate;
            AggStatePerGroup pergroup;
            aggstate = op->d.agg_init_trans.aggstate;
            pergroup = &aggstate->all_pergroups
                [op->d.agg_init_trans.setoff]
                [op->d.agg_init_trans.transno];
            /* If transValue has not yet been initialized, do so now. */
            if (pergroup->noTransValue)
            {
                AggStatePerTrans pertrans = op->d.agg_init_trans.pertrans;
                aggstate->curaggcontext = op->d.agg_init_trans.aggcontext;
                aggstate->current_set = op->d.agg_init_trans.setno;
                ExecAggInitGroup(aggstate, pertrans, pergroup);
                /* copied trans value from input, done this round */
                EEO_JUMP(op->d.agg_init_trans.jumpnull);
            }
            EEO_NEXT();
        }
        /* check that a strict aggregate's input isn't NULL */
        EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK)
        {
            AggState   *aggstate;
            AggStatePerGroup pergroup;
            aggstate = op->d.agg_strict_trans_check.aggstate;
            pergroup = &aggstate->all_pergroups
                [op->d.agg_strict_trans_check.setoff]
                [op->d.agg_strict_trans_check.transno];
            if (unlikely(pergroup->transValueIsNull))
                EEO_JUMP(op->d.agg_strict_trans_check.jumpnull);
            EEO_NEXT();
        }
        /*
         * Evaluate aggregate transition / combine function that has a
         * by-value transition type. That's a seperate case from the
         * by-reference implementation because it's a bit simpler.
         */
        EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL)
        {
            AggState   *aggstate;
            AggStatePerTrans pertrans;
            AggStatePerGroup pergroup;
            FunctionCallInfo fcinfo;
            MemoryContext oldContext;
            Datum       newVal;
            aggstate = op->d.agg_trans.aggstate;
            pertrans = op->d.agg_trans.pertrans;
            pergroup = &aggstate->all_pergroups
                [op->d.agg_trans.setoff]
                [op->d.agg_trans.transno];
            Assert(pertrans->transtypeByVal);
            fcinfo = &pertrans->transfn_fcinfo;
            /* cf. select_current_set() */
            aggstate->curaggcontext = op->d.agg_trans.aggcontext;
            aggstate->current_set = op->d.agg_trans.setno;
            /* set up aggstate->curpertrans for AggGetAggref() */
            aggstate->curpertrans = pertrans;
            /* invoke transition function in per-tuple context */
            oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
            fcinfo->arg[0] = pergroup->transValue;
            fcinfo->argnull[0] = pergroup->transValueIsNull;
            fcinfo->isnull = false; /* just in case transfn doesn't set it */
            newVal = FunctionCallInvoke(fcinfo);
            pergroup->transValue = newVal;
            pergroup->transValueIsNull = fcinfo->isnull;
            MemoryContextSwitchTo(oldContext);
            EEO_NEXT();
        }
        /*
         * Evaluate aggregate transition / combine function that has a
         * by-reference transition type.
         *
         * Could optimize a bit further by splitting off by-reference
         * fixed-length types, but currently that doesn't seem worth it.
         */
        EEO_CASE(EEOP_AGG_PLAIN_TRANS)
        {
            AggState   *aggstate;
            AggStatePerTrans pertrans;
            AggStatePerGroup pergroup;
            FunctionCallInfo fcinfo;
            MemoryContext oldContext;
            Datum       newVal;
            aggstate = op->d.agg_trans.aggstate;
            pertrans = op->d.agg_trans.pertrans;
            pergroup = &aggstate->all_pergroups
                [op->d.agg_trans.setoff]
                [op->d.agg_trans.transno];
            Assert(!pertrans->transtypeByVal);
            fcinfo = &pertrans->transfn_fcinfo;
            /* cf. select_current_set() */
            aggstate->curaggcontext = op->d.agg_trans.aggcontext;
            aggstate->current_set = op->d.agg_trans.setno;
            /* set up aggstate->curpertrans for AggGetAggref() */
            aggstate->curpertrans = pertrans;
            /* invoke transition function in per-tuple context */
            oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory);
            fcinfo->arg[0] = pergroup->transValue;
            fcinfo->argnull[0] = pergroup->transValueIsNull;
            fcinfo->isnull = false; /* just in case transfn doesn't set it */
            newVal = FunctionCallInvoke(fcinfo);
            /*
             * For pass-by-ref datatype, must copy the new value into
             * aggcontext and free the prior transValue.  But if transfn
             * returned a pointer to its first input, we don't need to do
             * anything.  Also, if transfn returned a pointer to a R/W
             * expanded object that is already a child of the aggcontext,
             * assume we can adopt that value without copying it.
             */
            if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue))
                newVal = ExecAggTransReparent(aggstate, pertrans,
                                              newVal, fcinfo->isnull,
                                              pergroup->transValue,
                                              pergroup->transValueIsNull);
            pergroup->transValue = newVal;
            pergroup->transValueIsNull = fcinfo->isnull;
            MemoryContextSwitchTo(oldContext);
            EEO_NEXT();
        }
        /* process single-column ordered aggregate datum */
        EEO_CASE(EEOP_AGG_ORDERED_TRANS_DATUM)
        {
            /* too complex for an inline implementation */
            ExecEvalAggOrderedTransDatum(state, op, econtext);
            EEO_NEXT();
        }
        /* process multi-column ordered aggregate tuple */
        EEO_CASE(EEOP_AGG_ORDERED_TRANS_TUPLE)
        {
            /* too complex for an inline implementation */
            ExecEvalAggOrderedTransTuple(state, op, econtext);
            EEO_NEXT();
        }
        EEO_CASE(EEOP_LAST)
        {
            /* unreachable */
            Assert(false);
            goto out;
        }
    }
out:
    *isnull = state->resnull;
    return state->resvalue;
}

三、跟蹤分析

測試腳本

testdb=# alter table t_expr add primary key(id);
ALTER TABLE
testdb=# select 1+id from t_expr where id < 3;

調用棧

(gdb) bt
#0  ExecInterpExpr (state=0x1e6baa8, econtext=0x1e6b6d8, isnull=0x7fffdbc3b877) at execExprInterp.c:402
#1  0x00000000006cd7ed in ExecInterpExprStillValid (state=0x1e6baa8, econtext=0x1e6b6d8, isNull=0x7fffdbc3b877)
    at execExprInterp.c:1786
#2  0x00000000006e1f7f in ExecEvalExprSwitchContext (state=0x1e6baa8, econtext=0x1e6b6d8, isNull=0x7fffdbc3b877)
    at ../../../src/include/executor/executor.h:313
#3  0x00000000006e1fe8 in ExecProject (projInfo=0x1e6baa0) at ../../../src/include/executor/executor.h:347
#4  0x00000000006e2358 in ExecScan (node=0x1e6b5c0, accessMtd=0x7103a9 <SeqNext>, recheckMtd=0x710474 <SeqRecheck>)
    at execScan.c:201
#5  0x00000000007104be in ExecSeqScan (pstate=0x1e6b5c0) at nodeSeqscan.c:129
#6  0x00000000006e05bb in ExecProcNodeFirst (node=0x1e6b5c0) at execProcnode.c:445
#7  0x00000000006d551e in ExecProcNode (node=0x1e6b5c0) at ../../../src/include/executor/executor.h:247
#8  0x00000000006d7d56 in ExecutePlan (estate=0x1e6b3a8, planstate=0x1e6b5c0, use_parallel_mode=false, 
    operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x1e5ff50, 
    execute_once=true) at execMain.c:1723
#9  0x00000000006d5ae8 in standard_ExecutorRun (queryDesc=0x1da77e8, direction=ForwardScanDirection, count=0, 
    execute_once=true) at execMain.c:364
#10 0x00000000006d5910 in ExecutorRun (queryDesc=0x1da77e8, direction=ForwardScanDirection, count=0, execute_once=true)
    at execMain.c:307
#11 0x00000000008c2206 in PortalRunSelect (portal=0x1df4608, forward=true, count=0, dest=0x1e5ff50) at pquery.c:932
#12 0x00000000008c1ea4 in PortalRun (portal=0x1df4608, count=9223372036854775807, isTopLevel=true, run_once=true, 
    dest=0x1e5ff50, altdest=0x1e5ff50, completionTag=0x7fffdbc3bc20 "") at pquery.c:773
#13 0x00000000008bbf06 in exec_simple_query (query_string=0x1d85d78 "select 1+id from t_expr;") at postgres.c:1145
#14 0x00000000008c0191 in PostgresMain (argc=1, argv=0x1db3cd8, dbname=0x1db3b40 "testdb", username=0x1db3b20 "xdb")
    at postgres.c:4182
#15 0x000000000081e06c in BackendRun (port=0x1da7ae0) at postmaster.c:4361
#16 0x000000000081d7df in BackendStartup (port=0x1da7ae0) at postmaster.c:4033
#17 0x0000000000819bd9 in ServerLoop () at postmaster.c:1706
---Type <return> to continue, or q <return> to quit---
#18 0x000000000081948f in PostmasterMain (argc=1, argv=0x1d80a50) at postmaster.c:1379
#19 0x0000000000742931 in main (argc=1, argv=0x1d80a50) at main.c:228

跟蹤分析
進入ExecInterpExpr

Breakpoint 1, ExecInterpExpr (state=0x1e67678, econtext=0x1e672a8, isnull=0x7fffdbc3b897) at execExprInterp.c:402
402     if (unlikely(state == NULL))
(gdb) c
Continuing.
Breakpoint 1, ExecInterpExpr (state=0x1e67678, econtext=0x1e672a8, isnull=0x7fffdbc3b877) at execExprInterp.c:402
402     if (unlikely(state == NULL))

獲取步驟數組和相關的slot

(gdb) n
409     op = state->steps;
(gdb) 
410     resultslot = state->resultslot;
(gdb) 
411     innerslot = econtext->ecxt_innertuple;
(gdb) 
412     outerslot = econtext->ecxt_outertuple;
(gdb) p *econtext
$21 = {type = T_ExprContext, ecxt_scantuple = 0x1e673a0, ecxt_innertuple = 0x0, ecxt_outertuple = 0x0, 
  ecxt_per_query_memory = 0x1e66e60, ecxt_per_tuple_memory = 0x1e6d290, ecxt_param_exec_vals = 0x0, 
  ecxt_param_list_info = 0x0, ecxt_aggvalues = 0x0, ecxt_aggnulls = 0x0, caseValue_datum = 0, caseValue_isNull = true, 
  domainValue_datum = 0, domainValue_isNull = true, ecxt_estate = 0x1e66f78, ecxt_callbacks = 0x0}
(gdb) n
413     scanslot = econtext->ecxt_scantuple;
(gdb) 
(gdb) p *scanslot
$22 = {type = T_TupleTableSlot, tts_isempty = false, tts_shouldFree = false, tts_shouldFreeMin = false, tts_slow = false, 
  tts_tuple = 0x1e683f0, tts_tupleDescriptor = 0x7fa4f307fab8, tts_mcxt = 0x1e66e60, tts_buffer = 98, tts_nvalid = 0, 
  tts_values = 0x1e67400, tts_isnull = 0x1e67408, tts_mintuple = 0x0, tts_minhdr = {t_len = 0, t_self = {ip_blkid = {
        bi_hi = 0, bi_lo = 0}, ip_posid = 0}, t_tableOid = 0, t_data = 0x0}, tts_off = 0, tts_fixedTupleDescriptor = true}

進行分發

416     EEO_DISPATCH();

首先是EEOP_SCAN_FETCHSOME(STEP 1),獲取結果slot

(gdb) n
443             slot_getsomeattrs(scanslot, op->d.fetch.last_var);
(gdb) n
445             EEO_NEXT();
(gdb)

跳轉到下一個步驟EEOP_SCAN_VAR(STEP 2),獲取掃描獲得的id列值

480             int         attnum = op->d.var.attnum;
(gdb) 
484             Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
(gdb) 
485             *op->resvalue = scanslot->tts_values[attnum];
(gdb) 
486             *op->resnull = scanslot->tts_isnull[attnum];
(gdb) 
488             EEO_NEXT();
(gdb)

跳轉到下一個步驟EEOP_FUNCEXPR_STRICT(STEP 3)
首先獲取函數調用信息(參數),然后根據參數個數循環判斷,接著調用實際的函數(fn_addr指向的函數),調用后賦值給聯合體d.

663             FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
(gdb) 
664             bool       *argnull = fcinfo->argnull;
(gdb) p fcinfo
$23 = (FunctionCallInfo) 0x1e67b78
(gdb) p *fcinfo
$24 = {flinfo = 0x1e67b20, context = 0x0, resultinfo = 0x0, fncollation = 0, isnull = false, nargs = 2, arg = {1, 1, 
    0 <repeats 98 times>}, argnull = {false <repeats 100 times>}}
(gdb) p *fcinfo->flinfo
$25 = {fn_addr = 0x93d60c <int4pl>, fn_oid = 177, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', 
  fn_extra = 0x0, fn_mcxt = 0x1e66e60, fn_expr = 0x1d87bf8}
(gdb) p *fcinfo->flinfo->fn_expr
$26 = {type = T_OpExpr}
(gdb) n
669             for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) 
671                 if (argnull[argno])
(gdb) 
669             for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) 
671                 if (argnull[argno])
(gdb) 
669             for (argno = 0; argno < op->d.func.nargs; argno++)
(gdb) 
677             fcinfo->isnull = false;
(gdb) 
678             d = op->d.func.fn_addr(fcinfo);
(gdb) 
679             *op->resvalue = d;
(gdb) 
680             *op->resnull = fcinfo->isnull;
(gdb) 
683             EEO_NEXT();
(gdb)

跳轉到下一個步驟EEOP_ASSIGN_TMP(STEP 4),獲取結果列所在的編號,賦值

603             int         resultnum = op->d.assign_tmp.resultnum;
(gdb) 
605             resultslot->tts_values[resultnum] = state->resvalue;
(gdb) 
606             resultslot->tts_isnull[resultnum] = state->resnull;
(gdb) p state->resvalue
$27 = 2
(gdb) n
608             EEO_NEXT();

跳轉到下一個步驟,EEO_DONE(STEP 5)

(gdb) 
423             goto out;

退出,返回結果值

(gdb) n
1764        *isnull = state->resnull;
(gdb) 
1765        return state->resvalue;
(gdb) 
1766    }
(gdb) 
ExecInterpExprStillValid (state=0x1e67678, econtext=0x1e672a8, isNull=0x7fffdbc3b877) at execExprInterp.c:1787
1787    }
(gdb) c
Continuing.

以上是“PostgreSQL如何解析查詢語句中的表達式列并計算得出該列的值”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

象山县| 仙游县| 松潘县| 时尚| 乐昌市| 宾阳县| 利川市| 章丘市| 彩票| 攀枝花市| 荆门市| 中超| 绍兴市| 昆明市| 巴林左旗| 巧家县| 汉阴县| 耒阳市| 昌都县| 甘孜| 家居| 石台县| 龙岩市| 铅山县| 偃师市| 潜山县| 招远市| 繁峙县| 崇礼县| 罗城| 荆门市| 泉州市| 且末县| 平昌县| 当阳市| 莱芜市| 武强县| 高密市| 盐城市| 定襄县| 孝昌县|