ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(186)- 查询#102(聚合函数#7-advance_aggregates)

PostgreSQL 源码解读(186)- 查询#102(聚合函数#7-advance_aggregates)

原创 PostgreSQL 作者:husthxd 时间:2019-05-09 16:50:30 0 删除 编辑

本节继续介绍聚合函数的实现,主要是函数ExecAgg->agg_fill_hash_table->advance_aggregates函数的处理逻辑.
该函数为每一个输入tuple推进每个聚合的转换状态,比如avg聚合,则把tuple中相应的列值加入到总和中,同时计数器加1,结果值会保存在内存上下文中,返回值是aggstate->phase->evaltrans->resvalue & aggstate->phase->evaltrans->resnull.

一、数据结构

AggState
聚合函数执行时状态结构体,内含AggStatePerAgg等结构体


/* ---------------------
 *    AggState information
 *
 *    ss.ss_ScanTupleSlot refers to output of underlying plan.
 *  ss.ss_ScanTupleSlot指的是基础计划的输出.
 *    (ss = ScanState,ps = PlanState)
 *
 *    Note: ss.ps.ps_ExprContext contains ecxt_aggvalues and
 *    ecxt_aggnulls arrays, which hold the computed agg values for the current
 *    input group during evaluation of an Agg node's output tuple(s).  We
 *    create a second ExprContext, tmpcontext, in which to evaluate input
 *    expressions and run the aggregate transition functions.
 *    注意:ss.ps.ps_ExprContext包含了ecxt_aggvalues和ecxt_aggnulls数组,
 *      这两个数组保存了在计算agg节点的输出元组时当前输入组已计算的agg值.
 * ---------------------
 */
/* these structs are private in nodeAgg.c: */
//在nodeAgg.c中私有的结构体
typedef struct AggStatePerAggData *AggStatePerAgg;
typedef struct AggStatePerTransData *AggStatePerTrans;
typedef struct AggStatePerGroupData *AggStatePerGroup;
typedef struct AggStatePerPhaseData *AggStatePerPhase;
typedef struct AggStatePerHashData *AggStatePerHash;
typedef struct AggState
{
    //第一个字段是NodeTag(继承自ScanState)
    ScanState    ss;                /* its first field is NodeTag */
    //targetlist和quals中所有的Aggref
    List       *aggs;            /* all Aggref nodes in targetlist & quals */
    //链表的大小(可以为0)
    int            numaggs;        /* length of list (could be zero!) */
    //pertrans条目大小
    int            numtrans;        /* number of pertrans items */
    //Agg策略模式
    AggStrategy aggstrategy;    /* strategy mode */
    //agg-splitting模式,参见nodes.h
    AggSplit    aggsplit;        /* agg-splitting mode, see nodes.h */
    //指向当前步骤数据的指针
    AggStatePerPhase phase;        /* pointer to current phase data */
    //步骤数(包括0)
    int            numphases;        /* number of phases (including phase 0) */
    //当前步骤
    int            current_phase;    /* current phase number */
    //per-Aggref信息
    AggStatePerAgg peragg;        /* per-Aggref information */
    //per-Trans状态信息
    AggStatePerTrans pertrans;    /* per-Trans state information */
    //长生命周期数据的ExprContexts(hashtable)
    ExprContext *hashcontext;    /* econtexts for long-lived data (hashtable) */
    ////长生命周期数据的ExprContexts(每一个GS使用)
    ExprContext **aggcontexts;    /* econtexts for long-lived data (per GS) */
    //输入表达式的ExprContext
    ExprContext *tmpcontext;    /* econtext for input expressions */
#define FIELDNO_AGGSTATE_CURAGGCONTEXT 14
    //当前活跃的aggcontext
    ExprContext *curaggcontext; /* currently active aggcontext */
    //当前活跃的aggregate(如存在)
    AggStatePerAgg curperagg;    /* currently active aggregate, if any */
#define FIELDNO_AGGSTATE_CURPERTRANS 16
    //当前活跃的trans state
    AggStatePerTrans curpertrans;    /* currently active trans state, if any */
    //输入结束?
    bool        input_done;        /* indicates end of input */
    //Agg扫描结束?
    bool        agg_done;        /* indicates completion of Agg scan */
    //最后一个grouping set
    int            projected_set;    /* The last projected grouping set */
#define FIELDNO_AGGSTATE_CURRENT_SET 20
    //将要解析的当前grouping set
    int            current_set;    /* The current grouping set being evaluated */
    //当前投影操作的分组列
    Bitmapset  *grouped_cols;    /* grouped cols in current projection */
    //倒序的分组列链表
    List       *all_grouped_cols;    /* list of all grouped cols in DESC order */
    /* These fields are for grouping set phase data */
    //-------- 下面的列用于grouping set步骤数据
    //所有步骤中最大的sets大小
    int            maxsets;        /* The max number of sets in any phase */
    //所有步骤的数组
    AggStatePerPhase phases;    /* array of all phases */
    //对于phases > 1,已排序的输入信息
    Tuplesortstate *sort_in;    /* sorted input to phases > 1 */
    //对于下一个步骤,输入已拷贝
    Tuplesortstate *sort_out;    /* input is copied here for next phase */
    //排序结果的slot
    TupleTableSlot *sort_slot;    /* slot for sort results */
    /* these fields are used in AGG_PLAIN and AGG_SORTED modes: */
    //------- 下面的列用于AGG_PLAIN和AGG_SORTED模式:
    //per-group指针的grouping set编号数组
    AggStatePerGroup *pergroups;    /* grouping set indexed array of per-group
                                     * pointers */
    //当前组的第一个元组拷贝
    HeapTuple    grp_firstTuple; /* copy of first tuple of current group */
    /* these fields are used in AGG_HASHED and AGG_MIXED modes: */
    //--------- 下面的列用于AGG_HASHED和AGG_MIXED模式:
    //是否已填充hash表?
    bool        table_filled;    /* hash table filled yet? */
    //hash桶数?
    int            num_hashes;
    //相应的哈希表数据数组
    AggStatePerHash perhash;    /* array of per-hashtable data */
    //per-group指针的grouping set编号数组
    AggStatePerGroup *hash_pergroup;    /* grouping set indexed array of
                                         * per-group pointers */
    /* support for evaluation of agg input expressions: */
    //---------- agg输入表达式解析支持
#define FIELDNO_AGGSTATE_ALL_PERGROUPS 34
    //首先是->pergroups,然后是hash_pergroup
    AggStatePerGroup *all_pergroups;    /* array of first ->pergroups, than
                                         * ->hash_pergroup */
    //投影实现机制
    ProjectionInfo *combinedproj;    /* projection machinery */
} AggState;
/* Primitive options supported by nodeAgg.c: */
//nodeag .c支持的基本选项
#define AGGSPLITOP_COMBINE        0x01    /* substitute combinefn for transfn */
#define AGGSPLITOP_SKIPFINAL    0x02    /* skip finalfn, return state as-is */
#define AGGSPLITOP_SERIALIZE    0x04    /* apply serializefn to output */
#define AGGSPLITOP_DESERIALIZE    0x08    /* apply deserializefn to input */
/* Supported operating modes (i.e., useful combinations of these options): */
//支持的操作模式
typedef enum AggSplit
{
    /* Basic, non-split aggregation: */
    //基本 : 非split聚合
    AGGSPLIT_SIMPLE = 0,
    /* Initial phase of partial aggregation, with serialization: */
    //部分聚合的初始步骤,序列化
    AGGSPLIT_INITIAL_SERIAL = AGGSPLITOP_SKIPFINAL | AGGSPLITOP_SERIALIZE,
    /* Final phase of partial aggregation, with deserialization: */
    //部分聚合的最终步骤,反序列化
    AGGSPLIT_FINAL_DESERIAL = AGGSPLITOP_COMBINE | AGGSPLITOP_DESERIALIZE
} AggSplit;
/* Test whether an AggSplit value selects each primitive option: */
//测试AggSplit选择了哪些基本选项
#define DO_AGGSPLIT_COMBINE(as)        (((as) & AGGSPLITOP_COMBINE) != 0)
#define DO_AGGSPLIT_SKIPFINAL(as)    (((as) & AGGSPLITOP_SKIPFINAL) != 0)
#define DO_AGGSPLIT_SERIALIZE(as)    (((as) & AGGSPLITOP_SERIALIZE) != 0)
#define DO_AGGSPLIT_DESERIALIZE(as) (((as) & AGGSPLITOP_DESERIALIZE) != 0)

二、源码解读

advance_aggregates函数最终会调用ExecInterpExpr函数(先前已介绍),通过一系列的步骤得到结果.


/*
 * Advance each aggregate transition state for one input tuple.  The input
 * tuple has been stored in tmpcontext->ecxt_outertuple, so that it is
 * accessible to ExecEvalExpr.
 * 为每一个输入tuple推进每个聚合转换状态.
 * 输入元组已存储在tmpcontext->ecxt_outertuple中,因此可访问ExecEvalExpr.
 *
 * We have two sets of transition states to handle: one for sorted aggregation
 * and one for hashed; we do them both here, to avoid multiple evaluation of
 * the inputs.
 * 我们有两个转换状态集合需要处理:一个是已排序聚合,一个是已哈希聚合.
 * 在这里同时进行处理,以避免输入的多种解析.
 *
 * When called, CurrentMemoryContext should be the per-query context.
 * 一旦完成调用,CurrentMemoryContext应为per-query上下文.
 */
static void
advance_aggregates(AggState *aggstate)
{
    bool        dummynull;
    ExecEvalExprSwitchContext(aggstate->phase->evaltrans,
                              aggstate->tmpcontext,
                              &dummynull);
}
/*
 * ExecEvalExprSwitchContext
 *
 * Same as ExecEvalExpr, but get into the right allocation context explicitly.
 * 与ExecEvalExpr一样,只是显式的进入内存上下文.
 */
#ifndef FRONTEND
static inline Datum
ExecEvalExprSwitchContext(ExprState *state,
                          ExprContext *econtext,
                          bool *isNull)
{
    Datum        retDatum;
    MemoryContext oldContext;
    oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
    retDatum = state->evalfunc(state, econtext, isNull);
    MemoryContextSwitchTo(oldContext);
    return retDatum;
}
#endif
/*
 * Expression evaluation callback that performs extra checks before executing
 * the expression. Declared extern so other methods of execution can use it
 * too.
 * 表达式解析回调函数,在执行表达式解析前执行额外的检查.
 * 声明为extern以便其他方法可以使用.
 */
Datum
ExecInterpExprStillValid(ExprState *state, ExprContext *econtext, bool *isNull)
{
    /*
     * First time through, check whether attribute matches Var.  Might not be
     * ok anymore, due to schema changes.
     * 第一次,需检查属性是否与Var匹配.
     * 由于模式的变化,有可能会出问题.
     */
    CheckExprStillValid(state, econtext);
    /* skip the check during further executions */
    //在后续的执行中,跳过检查.
    state->evalfunc = (ExprStateEvalFunc) state->evalfunc_private;
    /* and actually execute */
    //执行解析函数,获取结果
    return state->evalfunc(state, econtext, isNull);
}
//evalfunc_private --> ExecInterpExpr

ExecInterpExpr
解析给定”econtext”在执行上下文中通过”state”标识的表达式.


//evalfunc_private --> ExecInterpExpr
/*
 * 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;
}

三、跟踪分析

N/A

四、参考资料

PostgreSQL 源码解读(160)- 查询#80(如何实现表达式解析)

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2643822/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
长期从事政务、金融等行业产品研发和架构设计工作,ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。现就职于广州云图数据技术有限公司,系统架构师。

注册时间:2007-12-28

  • 博文量
    1268
  • 访问量
    3745784