千家信息网

PostgreSQL 聚合函数的实现教程

发表于:2025-11-08 作者:千家信息网编辑
千家信息网最后更新 2025年11月08日,本篇内容主要讲解"PostgreSQL 聚合函数的实现教程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"PostgreSQL 聚合函数的实现教程"吧!一、
千家信息网最后更新 2025年11月08日PostgreSQL 聚合函数的实现教程

本篇内容主要讲解"PostgreSQL 聚合函数的实现教程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"PostgreSQL 聚合函数的实现教程"吧!

一、数据结构

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 voidadvance_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 FRONTENDstatic inline DatumExecEvalExprSwitchContext(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以便其他方法可以使用. */DatumExecInterpExprStillValid(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 DatumExecInterpExpr(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;}

到此,相信大家对"PostgreSQL 聚合函数的实现教程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0