本篇内容主要讲解“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 void advance_aggregates(AggState *aggstate) { bool dummynull; ExecEvalExprSwitchContext(aggstate->phase->evaltrans, aggstate->tmpcontext, &dummynull); } /* * ExecEvalExprSwitchContext * * Same as ExecEvalExpr, but get into the right allocation context explicitly. * 与ExecEvalExpr一样,只是显式的进入内存上下文. */ #ifndef FRONTEND static inline Datum ExecEvalExprSwitchContext(ExprState *state, ExprContext *econtext, bool *isNull) { Datum retDatum; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); retDatum = state->evalfunc(state, econtext, isNull); MemoryContextSwitchTo(oldContext); return retDatum; } #endif /* * Expression evaluation callback that performs extra checks before executing * the expression. Declared extern so other methods of execution can use it * too. * 表达式解析回调函数,在执行表达式解析前执行额外的检查. * 声明为extern以便其他方法可以使用. */ Datum ExecInterpExprStillValid(ExprState *state, ExprContext *econtext, bool *isNull) { /* * First time through, check whether attribute matches Var. Might not be * ok anymore, due to schema changes. * 第一次,需检查属性是否与Var匹配. * 由于模式的变化,有可能会出问题. */ CheckExprStillValid(state, econtext); /* skip the check during further executions */ //在后续的执行中,跳过检查. state->evalfunc = (ExprStateEvalFunc) state->evalfunc_private; /* and actually execute */ //执行解析函数,获取结果 return state->evalfunc(state, econtext, isNull); } //evalfunc_private --> ExecInterpExpr
ExecInterpExpr
解析给定”econtext”在执行上下文中通过”state”标识的表达式.
//evalfunc_private --> ExecInterpExpr /* * Evaluate expression identified by "state" in the execution context * given by "econtext". *isnull is set to the is-null flag for the result, * and the Datum value is the function result. * 解析给定"econtext"在执行上下文中通过"state"标识的表达式. * *isnull用于设置结果是否为null,Datum是函数执行的结果. * * As a special case, return the dispatch table's address if state is NULL. * This is used by ExecInitInterpreter to set up the dispatch_table global. * (Only applies when EEO_USE_COMPUTED_GOTO is defined.) * 作为一个特别的情况,如state为NULL,返回分发器表的地址. * 这个情况用于ExecInitInterpreter配置dispatch_table. * (只是在定义了EEO_USE_COMPUTED_GOTO时才应用) */ static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) { ExprEvalStep *op; TupleTableSlot *resultslot; TupleTableSlot *innerslot; TupleTableSlot *outerslot; TupleTableSlot *scanslot; /* * This array has to be in the same order as enum ExprEvalOp. * 该数组在枚举类型ExprEvalOp中具有同样的顺序 */ #if defined(EEO_USE_COMPUTED_GOTO) static const void *const dispatch_table[] = { &&CASE_EEOP_DONE, &&CASE_EEOP_INNER_FETCHSOME, &&CASE_EEOP_OUTER_FETCHSOME, &&CASE_EEOP_SCAN_FETCHSOME, &&CASE_EEOP_INNER_VAR, &&CASE_EEOP_OUTER_VAR, &&CASE_EEOP_SCAN_VAR, &&CASE_EEOP_INNER_SYSVAR, &&CASE_EEOP_OUTER_SYSVAR, &&CASE_EEOP_SCAN_SYSVAR, &&CASE_EEOP_WHOLEROW, &&CASE_EEOP_ASSIGN_INNER_VAR, &&CASE_EEOP_ASSIGN_OUTER_VAR, &&CASE_EEOP_ASSIGN_SCAN_VAR, &&CASE_EEOP_ASSIGN_TMP, &&CASE_EEOP_ASSIGN_TMP_MAKE_RO, &&CASE_EEOP_CONST, &&CASE_EEOP_FUNCEXPR, &&CASE_EEOP_FUNCEXPR_STRICT, &&CASE_EEOP_FUNCEXPR_FUSAGE, &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE, &&CASE_EEOP_BOOL_AND_STEP_FIRST, &&CASE_EEOP_BOOL_AND_STEP, &&CASE_EEOP_BOOL_AND_STEP_LAST, &&CASE_EEOP_BOOL_OR_STEP_FIRST, &&CASE_EEOP_BOOL_OR_STEP, &&CASE_EEOP_BOOL_OR_STEP_LAST, &&CASE_EEOP_BOOL_NOT_STEP, &&CASE_EEOP_QUAL, &&CASE_EEOP_JUMP, &&CASE_EEOP_JUMP_IF_NULL, &&CASE_EEOP_JUMP_IF_NOT_NULL, &&CASE_EEOP_JUMP_IF_NOT_TRUE, &&CASE_EEOP_NULLTEST_ISNULL, &&CASE_EEOP_NULLTEST_ISNOTNULL, &&CASE_EEOP_NULLTEST_ROWISNULL, &&CASE_EEOP_NULLTEST_ROWISNOTNULL, &&CASE_EEOP_BOOLTEST_IS_TRUE, &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE, &&CASE_EEOP_BOOLTEST_IS_FALSE, &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE, &&CASE_EEOP_PARAM_EXEC, &&CASE_EEOP_PARAM_EXTERN, &&CASE_EEOP_PARAM_CALLBACK, &&CASE_EEOP_CASE_TESTVAL, &&CASE_EEOP_MAKE_READONLY, &&CASE_EEOP_IOCOERCE, &&CASE_EEOP_DISTINCT, &&CASE_EEOP_NOT_DISTINCT, &&CASE_EEOP_NULLIF, &&CASE_EEOP_SQLVALUEFUNCTION, &&CASE_EEOP_CURRENTOFEXPR, &&CASE_EEOP_NEXTVALUEEXPR, &&CASE_EEOP_ARRAYEXPR, &&CASE_EEOP_ARRAYCOERCE, &&CASE_EEOP_ROW, &&CASE_EEOP_ROWCOMPARE_STEP, &&CASE_EEOP_ROWCOMPARE_FINAL, &&CASE_EEOP_MINMAX, &&CASE_EEOP_FIELDSELECT, &&CASE_EEOP_FIELDSTORE_DEFORM, &&CASE_EEOP_FIELDSTORE_FORM, &&CASE_EEOP_ARRAYREF_SUBSCRIPT, &&CASE_EEOP_ARRAYREF_OLD, &&CASE_EEOP_ARRAYREF_ASSIGN, &&CASE_EEOP_ARRAYREF_FETCH, &&CASE_EEOP_DOMAIN_TESTVAL, &&CASE_EEOP_DOMAIN_NOTNULL, &&CASE_EEOP_DOMAIN_CHECK, &&CASE_EEOP_CONVERT_ROWTYPE, &&CASE_EEOP_SCALARARRAYOP, &&CASE_EEOP_XMLEXPR, &&CASE_EEOP_AGGREF, &&CASE_EEOP_GROUPING_FUNC, &&CASE_EEOP_WINDOW_FUNC, &&CASE_EEOP_SUBPLAN, &&CASE_EEOP_ALTERNATIVE_SUBPLAN, &&CASE_EEOP_AGG_STRICT_DESERIALIZE, &&CASE_EEOP_AGG_DESERIALIZE, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK, &&CASE_EEOP_AGG_INIT_TRANS, &&CASE_EEOP_AGG_STRICT_TRANS_CHECK, &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS, &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM, &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE, &&CASE_EEOP_LAST }; StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table), "dispatch_table out of whack with ExprEvalOp"); if (unlikely(state == NULL)) //如state == NULL,则调用PointerGetDatum return PointerGetDatum(dispatch_table); #else Assert(state != NULL); #endif /* EEO_USE_COMPUTED_GOTO */ /* setup state */ //配置状态变量 op = state->steps; resultslot = state->resultslot; innerslot = econtext->ecxt_innertuple; outerslot = econtext->ecxt_outertuple; scanslot = econtext->ecxt_scantuple; #if defined(EEO_USE_COMPUTED_GOTO) EEO_DISPATCH();//分发 #endif EEO_SWITCH() { EEO_CASE(EEOP_DONE) { goto out; } EEO_CASE(EEOP_INNER_FETCHSOME) { /* XXX: worthwhile to check tts_nvalid inline first? */ slot_getsomeattrs(innerslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_OUTER_FETCHSOME) { slot_getsomeattrs(outerslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_SCAN_FETCHSOME) { slot_getsomeattrs(scanslot, op->d.fetch.last_var); EEO_NEXT(); } EEO_CASE(EEOP_INNER_VAR) { int attnum = op->d.var.attnum; /* * Since we already extracted all referenced columns from the * tuple with a FETCHSOME step, we can just grab the value * directly out of the slot's decomposed-data arrays. But let's * have an Assert to check that that did happen. */ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); *op->resvalue = innerslot->tts_values[attnum]; *op->resnull = innerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_OUTER_VAR) { int attnum = op->d.var.attnum; /* See EEOP_INNER_VAR comments */ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); *op->resvalue = outerslot->tts_values[attnum]; *op->resnull = outerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_SCAN_VAR) { int attnum = op->d.var.attnum; /* See EEOP_INNER_VAR comments */ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); *op->resvalue = scanslot->tts_values[attnum]; *op->resnull = scanslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_INNER_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(innerslot->tts_tuple != NULL); Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(innerslot->tts_tuple, attnum, innerslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_OUTER_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(outerslot->tts_tuple != NULL); Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(outerslot->tts_tuple, attnum, outerslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_SCAN_SYSVAR) { int attnum = op->d.var.attnum; Datum d; /* these asserts must match defenses in slot_getattr */ Assert(scanslot->tts_tuple != NULL); Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr)); /* heap_getsysattr has sufficient defenses against bad attnums */ d = heap_getsysattr(scanslot->tts_tuple, attnum, scanslot->tts_tupleDescriptor, op->resnull); *op->resvalue = d; EEO_NEXT(); } EEO_CASE(EEOP_WHOLEROW) { /* too complex for an inline implementation */ ExecEvalWholeRowVar(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_INNER_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid); resultslot->tts_values[resultnum] = innerslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_OUTER_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid); resultslot->tts_values[resultnum] = outerslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_SCAN_VAR) { int resultnum = op->d.assign_var.resultnum; int attnum = op->d.assign_var.attnum; /* * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); resultslot->tts_values[resultnum] = scanslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum]; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_TMP) { int resultnum = op->d.assign_tmp.resultnum; resultslot->tts_values[resultnum] = state->resvalue; resultslot->tts_isnull[resultnum] = state->resnull; EEO_NEXT(); } EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO) { int resultnum = op->d.assign_tmp.resultnum; resultslot->tts_isnull[resultnum] = state->resnull; if (!resultslot->tts_isnull[resultnum]) resultslot->tts_values[resultnum] = MakeExpandedObjectReadOnlyInternal(state->resvalue); else resultslot->tts_values[resultnum] = state->resvalue; EEO_NEXT(); } EEO_CASE(EEOP_CONST) { *op->resnull = op->d.constval.isnull; *op->resvalue = op->d.constval.value; EEO_NEXT(); } /* * Function-call implementations. Arguments have previously been * evaluated directly into fcinfo->args. * * As both STRICT checks and function-usage are noticeable performance * wise, and function calls are a very hot-path (they also back * operators!), it's worth having so many separate opcodes. * * Note: the reason for using a temporary variable "d", here and in * other places, is that some compilers think "*op->resvalue = f();" * requires them to evaluate op->resvalue into a register before * calling f(), just in case f() is able to modify op->resvalue * somehow. The extra line of code can save a useless register spill * and reload across the function call. */ EEO_CASE(EEOP_FUNCEXPR) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; Datum d; fcinfo->isnull = false; d = op->d.func.fn_addr(fcinfo); *op->resvalue = d; *op->resnull = fcinfo->isnull; EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_STRICT) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; bool *argnull = fcinfo->argnull; int argno; Datum d; /* strict function, so check for NULL args */ for (argno = 0; argno < op->d.func.nargs; argno++) { if (argnull[argno]) { *op->resnull = true; goto strictfail; } } fcinfo->isnull = false; d = op->d.func.fn_addr(fcinfo); *op->resvalue = d; *op->resnull = fcinfo->isnull; strictfail: EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_FUSAGE) { /* not common enough to inline */ ExecEvalFuncExprFusage(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE) { /* not common enough to inline */ ExecEvalFuncExprStrictFusage(state, op, econtext); EEO_NEXT(); } /* * If any of its clauses is FALSE, an AND's result is FALSE regardless * of the states of the rest of the clauses, so we can stop evaluating * and return FALSE immediately. If none are FALSE and one or more is * NULL, we return NULL; otherwise we return TRUE. This makes sense * when you interpret NULL as "don't know": perhaps one of the "don't * knows" would have been FALSE if we'd known its value. Only when * all the inputs are known to be TRUE can we state confidently that * the AND's result is TRUE. */ EEO_CASE(EEOP_BOOL_AND_STEP_FIRST) { *op->d.boolexpr.anynull = false; /* * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the * same as EEOP_BOOL_AND_STEP - so fall through to that. */ /* FALL THROUGH */ } EEO_CASE(EEOP_BOOL_AND_STEP) { if (*op->resnull) { *op->d.boolexpr.anynull = true; } else if (!DatumGetBool(*op->resvalue)) { /* result is already set to FALSE, need not change it */ /* bail out early */ EEO_JUMP(op->d.boolexpr.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_AND_STEP_LAST) { if (*op->resnull) { /* result is already set to NULL, need not change it */ } else if (!DatumGetBool(*op->resvalue)) { /* result is already set to FALSE, need not change it */ /* * No point jumping early to jumpdone - would be same target * (as this is the last argument to the AND expression), * except more expensive. */ } else if (*op->d.boolexpr.anynull) { *op->resvalue = (Datum) 0; *op->resnull = true; } else { /* result is already set to TRUE, need not change it */ } EEO_NEXT(); } /* * If any of its clauses is TRUE, an OR's result is TRUE regardless of * the states of the rest of the clauses, so we can stop evaluating * and return TRUE immediately. If none are TRUE and one or more is * NULL, we return NULL; otherwise we return FALSE. This makes sense * when you interpret NULL as "don't know": perhaps one of the "don't * knows" would have been TRUE if we'd known its value. Only when all * the inputs are known to be FALSE can we state confidently that the * OR's result is FALSE. */ EEO_CASE(EEOP_BOOL_OR_STEP_FIRST) { *op->d.boolexpr.anynull = false; /* * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same * as EEOP_BOOL_OR_STEP - so fall through to that. */ /* FALL THROUGH */ } EEO_CASE(EEOP_BOOL_OR_STEP) { if (*op->resnull) { *op->d.boolexpr.anynull = true; } else if (DatumGetBool(*op->resvalue)) { /* result is already set to TRUE, need not change it */ /* bail out early */ EEO_JUMP(op->d.boolexpr.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_OR_STEP_LAST) { if (*op->resnull) { /* result is already set to NULL, need not change it */ } else if (DatumGetBool(*op->resvalue)) { /* result is already set to TRUE, need not change it */ /* * No point jumping to jumpdone - would be same target (as * this is the last argument to the AND expression), except * more expensive. */ } else if (*op->d.boolexpr.anynull) { *op->resvalue = (Datum) 0; *op->resnull = true; } else { /* result is already set to FALSE, need not change it */ } EEO_NEXT(); } EEO_CASE(EEOP_BOOL_NOT_STEP) { /* * Evaluation of 'not' is simple... if expr is false, then return * 'true' and vice versa. It's safe to do this even on a * nominally null value, so we ignore resnull; that means that * NULL in produces NULL out, which is what we want. */ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_QUAL) { /* simplified version of BOOL_AND_STEP for use by ExecQual() */ /* If argument (also result) is false or null ... */ if (*op->resnull || !DatumGetBool(*op->resvalue)) { /* ... bail out early, returning FALSE */ *op->resnull = false; *op->resvalue = BoolGetDatum(false); EEO_JUMP(op->d.qualexpr.jumpdone); } /* * Otherwise, leave the TRUE value in place, in case this is the * last qual. Then, TRUE is the correct answer. */ EEO_NEXT(); } EEO_CASE(EEOP_JUMP) { /* Unconditionally jump to target step */ EEO_JUMP(op->d.jump.jumpdone); } EEO_CASE(EEOP_JUMP_IF_NULL) { /* Transfer control if current result is null */ if (*op->resnull) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_JUMP_IF_NOT_NULL) { /* Transfer control if current result is non-null */ if (!*op->resnull) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_JUMP_IF_NOT_TRUE) { /* Transfer control if current result is null or false */ if (*op->resnull || !DatumGetBool(*op->resvalue)) EEO_JUMP(op->d.jump.jumpdone); EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ISNULL) { *op->resvalue = BoolGetDatum(*op->resnull); *op->resnull = false; EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ISNOTNULL) { *op->resvalue = BoolGetDatum(!*op->resnull); *op->resnull = false; EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ROWISNULL) { /* out of line implementation: too large */ ExecEvalRowNull(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL) { /* out of line implementation: too large */ ExecEvalRowNotNull(state, op, econtext); EEO_NEXT(); } /* BooleanTest implementations for all booltesttypes */ EEO_CASE(EEOP_BOOLTEST_IS_TRUE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } /* else, input value is the correct output as well */ EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_FALSE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue)); EEO_NEXT(); } EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE) { if (*op->resnull) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } /* else, input value is the correct output as well */ EEO_NEXT(); } EEO_CASE(EEOP_PARAM_EXEC) { /* out of line implementation: too large */ ExecEvalParamExec(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_PARAM_EXTERN) { /* out of line implementation: too large */ ExecEvalParamExtern(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_PARAM_CALLBACK) { /* allow an extension module to supply a PARAM_EXTERN value */ op->d.cparam.paramfunc(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_CASE_TESTVAL) { /* * Normally upper parts of the expression tree have setup the * values to be returned here, but some parts of the system * currently misuse {caseValue,domainValue}_{datum,isNull} to set * run-time data. So if no values have been set-up, use * ExprContext's. This isn't pretty, but also not *that* ugly, * and this is unlikely to be performance sensitive enough to * worry about an extra branch. */ if (op->d.casetest.value) { *op->resvalue = *op->d.casetest.value; *op->resnull = *op->d.casetest.isnull; } else { *op->resvalue = econtext->caseValue_datum; *op->resnull = econtext->caseValue_isNull; } EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_TESTVAL) { /* * See EEOP_CASE_TESTVAL comment. */ if (op->d.casetest.value) { *op->resvalue = *op->d.casetest.value; *op->resnull = *op->d.casetest.isnull; } else { *op->resvalue = econtext->domainValue_datum; *op->resnull = econtext->domainValue_isNull; } EEO_NEXT(); } EEO_CASE(EEOP_MAKE_READONLY) { /* * Force a varlena value that might be read multiple times to R/O */ if (!*op->d.make_readonly.isnull) *op->resvalue = MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value); *op->resnull = *op->d.make_readonly.isnull; EEO_NEXT(); } EEO_CASE(EEOP_IOCOERCE) { /* * Evaluate a CoerceViaIO node. This can be quite a hot path, so * inline as much work as possible. The source value is in our * result variable. */ char *str; /* call output function (similar to OutputFunctionCall) */ if (*op->resnull) { /* output functions are not called on nulls */ str = NULL; } else { FunctionCallInfo fcinfo_out; fcinfo_out = op->d.iocoerce.fcinfo_data_out; fcinfo_out->arg[0] = *op->resvalue; fcinfo_out->argnull[0] = false; fcinfo_out->isnull = false; str = DatumGetCString(FunctionCallInvoke(fcinfo_out)); /* OutputFunctionCall assumes result isn't null */ Assert(!fcinfo_out->isnull); } /* call input function (similar to InputFunctionCall) */ if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL) { FunctionCallInfo fcinfo_in; Datum d; fcinfo_in = op->d.iocoerce.fcinfo_data_in; fcinfo_in->arg[0] = PointerGetDatum(str); fcinfo_in->argnull[0] = *op->resnull; /* second and third arguments are already set up */ fcinfo_in->isnull = false; d = FunctionCallInvoke(fcinfo_in); *op->resvalue = d; /* Should get null result if and only if str is NULL */ if (str == NULL) { Assert(*op->resnull); Assert(fcinfo_in->isnull); } else { Assert(!*op->resnull); Assert(!fcinfo_in->isnull); } } EEO_NEXT(); } EEO_CASE(EEOP_DISTINCT) { /* * IS DISTINCT FROM must evaluate arguments (already done into * fcinfo->arg/argnull) to determine whether they are NULL; if * either is NULL then the result is determined. If neither is * NULL, then proceed to evaluate the comparison function, which * is just the type's standard equality operator. We need not * care whether that function is strict. Because the handling of * nulls is different, we can't just reuse EEOP_FUNCEXPR. */ FunctionCallInfo fcinfo = op->d.func.fcinfo_data; /* check function arguments for NULLness */ if (fcinfo->argnull[0] && fcinfo->argnull[1]) { /* Both NULL? Then is not distinct... */ *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { /* Only one is NULL? Then is distinct... */ *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else { /* Neither null, so apply the equality function */ Datum eqresult; fcinfo->isnull = false; eqresult = op->d.func.fn_addr(fcinfo); /* Must invert result of "="; safe to do even if null */ *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult)); *op->resnull = fcinfo->isnull; } EEO_NEXT(); } /* see EEOP_DISTINCT for comments, this is just inverted */ EEO_CASE(EEOP_NOT_DISTINCT) { FunctionCallInfo fcinfo = op->d.func.fcinfo_data; if (fcinfo->argnull[0] && fcinfo->argnull[1]) { *op->resvalue = BoolGetDatum(true); *op->resnull = false; } else if (fcinfo->argnull[0] || fcinfo->argnull[1]) { *op->resvalue = BoolGetDatum(false); *op->resnull = false; } else { Datum eqresult; fcinfo->isnull = false; eqresult = op->d.func.fn_addr(fcinfo); *op->resvalue = eqresult; *op->resnull = fcinfo->isnull; } EEO_NEXT(); } EEO_CASE(EEOP_NULLIF) { /* * The arguments are already evaluated into fcinfo->arg/argnull. */ FunctionCallInfo fcinfo = op->d.func.fcinfo_data; /* if either argument is NULL they can't be equal */ if (!fcinfo->argnull[0] && !fcinfo->argnull[1]) { Datum result; fcinfo->isnull = false; result = op->d.func.fn_addr(fcinfo); /* if the arguments are equal return null */ if (!fcinfo->isnull && DatumGetBool(result)) { *op->resvalue = (Datum) 0; *op->resnull = true; EEO_NEXT(); } } /* Arguments aren't equal, so return the first one */ *op->resvalue = fcinfo->arg[0]; *op->resnull = fcinfo->argnull[0]; EEO_NEXT(); } EEO_CASE(EEOP_SQLVALUEFUNCTION) { /* * Doesn't seem worthwhile to have an inline implementation * efficiency-wise. */ ExecEvalSQLValueFunction(state, op); EEO_NEXT(); } EEO_CASE(EEOP_CURRENTOFEXPR) { /* error invocation uses space, and shouldn't ever occur */ ExecEvalCurrentOfExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_NEXTVALUEEXPR) { /* * Doesn't seem worthwhile to have an inline implementation * efficiency-wise. */ ExecEvalNextValueExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYEXPR) { /* too complex for an inline implementation */ ExecEvalArrayExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYCOERCE) { /* too complex for an inline implementation */ ExecEvalArrayCoerce(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ROW) { /* too complex for an inline implementation */ ExecEvalRow(state, op); EEO_NEXT(); } EEO_CASE(EEOP_ROWCOMPARE_STEP) { FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data; Datum d; /* force NULL result if strict fn and NULL input */ if (op->d.rowcompare_step.finfo->fn_strict && (fcinfo->argnull[0] || fcinfo->argnull[1])) { *op->resnull = true; EEO_JUMP(op->d.rowcompare_step.jumpnull); } /* Apply comparison function */ fcinfo->isnull = false; d = op->d.rowcompare_step.fn_addr(fcinfo); *op->resvalue = d; /* force NULL result if NULL function result */ if (fcinfo->isnull) { *op->resnull = true; EEO_JUMP(op->d.rowcompare_step.jumpnull); } *op->resnull = false; /* If unequal, no need to compare remaining columns */ if (DatumGetInt32(*op->resvalue) != 0) { EEO_JUMP(op->d.rowcompare_step.jumpdone); } EEO_NEXT(); } EEO_CASE(EEOP_ROWCOMPARE_FINAL) { int32 cmpresult = DatumGetInt32(*op->resvalue); RowCompareType rctype = op->d.rowcompare_final.rctype; *op->resnull = false; switch (rctype) { /* EQ and NE cases aren't allowed here */ case ROWCOMPARE_LT: *op->resvalue = BoolGetDatum(cmpresult < 0); break; case ROWCOMPARE_LE: *op->resvalue = BoolGetDatum(cmpresult <= 0); break; case ROWCOMPARE_GE: *op->resvalue = BoolGetDatum(cmpresult >= 0); break; case ROWCOMPARE_GT: *op->resvalue = BoolGetDatum(cmpresult > 0); break; default: Assert(false); break; } EEO_NEXT(); } EEO_CASE(EEOP_MINMAX) { /* too complex for an inline implementation */ ExecEvalMinMax(state, op); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSELECT) { /* too complex for an inline implementation */ ExecEvalFieldSelect(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSTORE_DEFORM) { /* too complex for an inline implementation */ ExecEvalFieldStoreDeForm(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_FIELDSTORE_FORM) { /* too complex for an inline implementation */ ExecEvalFieldStoreForm(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT) { /* Process an array subscript */ /* too complex for an inline implementation */ if (ExecEvalArrayRefSubscript(state, op)) { EEO_NEXT(); } else { /* Subscript is null, short-circuit ArrayRef to NULL */ EEO_JUMP(op->d.arrayref_subscript.jumpdone); } } EEO_CASE(EEOP_ARRAYREF_OLD) { /* * Fetch the old value in an arrayref assignment, in case it's * referenced (via a CaseTestExpr) inside the assignment * expression. */ /* too complex for an inline implementation */ ExecEvalArrayRefOld(state, op); EEO_NEXT(); } /* * Perform ArrayRef assignment */ EEO_CASE(EEOP_ARRAYREF_ASSIGN) { /* too complex for an inline implementation */ ExecEvalArrayRefAssign(state, op); EEO_NEXT(); } /* * Fetch subset of an array. */ EEO_CASE(EEOP_ARRAYREF_FETCH) { /* too complex for an inline implementation */ ExecEvalArrayRefFetch(state, op); EEO_NEXT(); } EEO_CASE(EEOP_CONVERT_ROWTYPE) { /* too complex for an inline implementation */ ExecEvalConvertRowtype(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_SCALARARRAYOP) { /* too complex for an inline implementation */ ExecEvalScalarArrayOp(state, op); EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_NOTNULL) { /* too complex for an inline implementation */ ExecEvalConstraintNotNull(state, op); EEO_NEXT(); } EEO_CASE(EEOP_DOMAIN_CHECK) { /* too complex for an inline implementation */ ExecEvalConstraintCheck(state, op); EEO_NEXT(); } EEO_CASE(EEOP_XMLEXPR) { /* too complex for an inline implementation */ ExecEvalXmlExpr(state, op); EEO_NEXT(); } EEO_CASE(EEOP_AGGREF) { /* * Returns a Datum whose value is the precomputed aggregate value * found in the given expression context. */ AggrefExprState *aggref = op->d.aggref.astate; Assert(econtext->ecxt_aggvalues != NULL); *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno]; *op->resnull = econtext->ecxt_aggnulls[aggref->aggno]; EEO_NEXT(); } EEO_CASE(EEOP_GROUPING_FUNC) { /* too complex/uncommon for an inline implementation */ ExecEvalGroupingFunc(state, op); EEO_NEXT(); } EEO_CASE(EEOP_WINDOW_FUNC) { /* * Like Aggref, just return a precomputed value from the econtext. */ WindowFuncExprState *wfunc = op->d.window_func.wfstate; Assert(econtext->ecxt_aggvalues != NULL); *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno]; *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno]; EEO_NEXT(); } EEO_CASE(EEOP_SUBPLAN) { /* too complex for an inline implementation */ ExecEvalSubPlan(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN) { /* too complex for an inline implementation */ ExecEvalAlternativeSubPlan(state, op, econtext); EEO_NEXT(); } /* evaluate a strict aggregate deserialization function */ EEO_CASE(EEOP_AGG_STRICT_DESERIALIZE) { bool *argnull = op->d.agg_deserialize.fcinfo_data->argnull; /* Don't call a strict deserialization function with NULL input */ if (argnull[0]) EEO_JUMP(op->d.agg_deserialize.jumpnull); /* fallthrough */ } /* evaluate aggregate deserialization function (non-strict portion) */ EEO_CASE(EEOP_AGG_DESERIALIZE) { FunctionCallInfo fcinfo = op->d.agg_deserialize.fcinfo_data; AggState *aggstate = op->d.agg_deserialize.aggstate; MemoryContext oldContext; /* * We run the deserialization functions in per-input-tuple memory * context. */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->isnull = false; *op->resvalue = FunctionCallInvoke(fcinfo); *op->resnull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* * Check that a strict aggregate transition / combination function's * input is not NULL. */ EEO_CASE(EEOP_AGG_STRICT_INPUT_CHECK) { int argno; bool *nulls = op->d.agg_strict_input_check.nulls; int nargs = op->d.agg_strict_input_check.nargs; for (argno = 0; argno < nargs; argno++) { if (nulls[argno]) EEO_JUMP(op->d.agg_strict_input_check.jumpnull); } EEO_NEXT(); } /* * Initialize an aggregate's first value if necessary. */ EEO_CASE(EEOP_AGG_INIT_TRANS) { AggState *aggstate; AggStatePerGroup pergroup; aggstate = op->d.agg_init_trans.aggstate; pergroup = &aggstate->all_pergroups [op->d.agg_init_trans.setoff] [op->d.agg_init_trans.transno]; /* If transValue has not yet been initialized, do so now. */ if (pergroup->noTransValue) { AggStatePerTrans pertrans = op->d.agg_init_trans.pertrans; aggstate->curaggcontext = op->d.agg_init_trans.aggcontext; aggstate->current_set = op->d.agg_init_trans.setno; ExecAggInitGroup(aggstate, pertrans, pergroup); /* copied trans value from input, done this round */ EEO_JUMP(op->d.agg_init_trans.jumpnull); } EEO_NEXT(); } /* check that a strict aggregate's input isn't NULL */ EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK) { AggState *aggstate; AggStatePerGroup pergroup; aggstate = op->d.agg_strict_trans_check.aggstate; pergroup = &aggstate->all_pergroups [op->d.agg_strict_trans_check.setoff] [op->d.agg_strict_trans_check.transno]; if (unlikely(pergroup->transValueIsNull)) EEO_JUMP(op->d.agg_strict_trans_check.jumpnull); EEO_NEXT(); } /* * Evaluate aggregate transition / combine function that has a * by-value transition type. That's a seperate case from the * by-reference implementation because it's a bit simpler. */ EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL) { AggState *aggstate; AggStatePerTrans pertrans; AggStatePerGroup pergroup; FunctionCallInfo fcinfo; MemoryContext oldContext; Datum newVal; aggstate = op->d.agg_trans.aggstate; pertrans = op->d.agg_trans.pertrans; pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(pertrans->transtypeByVal); fcinfo = &pertrans->transfn_fcinfo; /* cf. select_current_set() */ aggstate->curaggcontext = op->d.agg_trans.aggcontext; aggstate->current_set = op->d.agg_trans.setno; /* set up aggstate->curpertrans for AggGetAggref() */ aggstate->curpertrans = pertrans; /* invoke transition function in per-tuple context */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->arg[0] = pergroup->transValue; fcinfo->argnull[0] = pergroup->transValueIsNull; fcinfo->isnull = false; /* just in case transfn doesn't set it */ newVal = FunctionCallInvoke(fcinfo); pergroup->transValue = newVal; pergroup->transValueIsNull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* * Evaluate aggregate transition / combine function that has a * by-reference transition type. * * Could optimize a bit further by splitting off by-reference * fixed-length types, but currently that doesn't seem worth it. */ EEO_CASE(EEOP_AGG_PLAIN_TRANS) { AggState *aggstate; AggStatePerTrans pertrans; AggStatePerGroup pergroup; FunctionCallInfo fcinfo; MemoryContext oldContext; Datum newVal; aggstate = op->d.agg_trans.aggstate; pertrans = op->d.agg_trans.pertrans; pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(!pertrans->transtypeByVal); fcinfo = &pertrans->transfn_fcinfo; /* cf. select_current_set() */ aggstate->curaggcontext = op->d.agg_trans.aggcontext; aggstate->current_set = op->d.agg_trans.setno; /* set up aggstate->curpertrans for AggGetAggref() */ aggstate->curpertrans = pertrans; /* invoke transition function in per-tuple context */ oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); fcinfo->arg[0] = pergroup->transValue; fcinfo->argnull[0] = pergroup->transValueIsNull; fcinfo->isnull = false; /* just in case transfn doesn't set it */ newVal = FunctionCallInvoke(fcinfo); /* * For pass-by-ref datatype, must copy the new value into * aggcontext and free the prior transValue. But if transfn * returned a pointer to its first input, we don't need to do * anything. Also, if transfn returned a pointer to a R/W * expanded object that is already a child of the aggcontext, * assume we can adopt that value without copying it. */ if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue)) newVal = ExecAggTransReparent(aggstate, pertrans, newVal, fcinfo->isnull, pergroup->transValue, pergroup->transValueIsNull); pergroup->transValue = newVal; pergroup->transValueIsNull = fcinfo->isnull; MemoryContextSwitchTo(oldContext); EEO_NEXT(); } /* process single-column ordered aggregate datum */ EEO_CASE(EEOP_AGG_ORDERED_TRANS_DATUM) { /* too complex for an inline implementation */ ExecEvalAggOrderedTransDatum(state, op, econtext); EEO_NEXT(); } /* process multi-column ordered aggregate tuple */ EEO_CASE(EEOP_AGG_ORDERED_TRANS_TUPLE) { /* too complex for an inline implementation */ ExecEvalAggOrderedTransTuple(state, op, econtext); EEO_NEXT(); } EEO_CASE(EEOP_LAST) { /* unreachable */ Assert(false); goto out; } } out: *isnull = state->resnull; return state->resvalue; }
到此,相信大家对“PostgreSQL 聚合函数的实现教程”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。