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 --> ExecInterpExprExecInterpExpr
解析给定"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 聚合函数的实现教程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
步骤
函数
输入
数组
数据
模式
表达式
状态
结果
上下
上下文
结构
教程
信息
大小
指针
面的
排序
支持
检查
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
奥的斯服务器查看故障和清除方法
性价比好的sql分布式数据库
基于模型的汽车软件开发流程
怎么在整个数据库中模糊搜索
安全开源数据库管理系统
r720服务器sata硬盘
上海工程软件开发怎么样
网络安全执法检查情况结果通报
东莞人力资源软件开发
湖南web软件开发
保障数据库安全的方法与技术
企业数据库系统搭建6
git登录linux服务器命令
如何提高服务器的安全防护
pop邮件服务器一直连接不上
网络安全法四川
svn项目连不上数据库
网络安全有什么用
计算机网络技术 电子教案
ci 数据库前缀
松山湖软件开发
期货软件开发
数据库系统的结构有
网络数据库如何存储
某计算机游戏软件开发公司
网络安全十四五规划编制
华为aaa服务器认证失败
流媒体服务器做什么的
重庆惠普服务器备件
计算机网络技术需要打字好吗