diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 47c1c1a49b..c6eb3ebacf 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -3193,121 +3193,3 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, as->d.agg_strict_trans_check.jumpnull = state->steps_len; } } - -/* - * Build equality expression that can be evaluated using ExecQual(), returning - * true if the expression context's inner/outer tuple are NOT DISTINCT. I.e - * two nulls match, a null and a not-null don't match. - * - * desc: tuple descriptor of the to-be-compared tuples - * numCols: the number of attributes to be examined - * keyColIdx: array of attribute column numbers - * eqFunctions: array of function oids of the equality functions to use - * parent: parent executor node - */ -ExprState * -ExecBuildGroupingEqual(TupleDesc desc, - int numCols, - AttrNumber *keyColIdx, - Oid *eqfunctions, - PlanState *parent) -{ - ExprState *state = makeNode(ExprState); - ExprEvalStep scratch = {0}; - int natt; - int maxatt = -1; - List *adjust_jumps = NIL; - ListCell *lc; - - /* - * When no columns are actually compared, the result's always true. See - * special case in ExecQual(). - */ - if (numCols == 0) - return NULL; - - state->expr = NULL; - state->flags = EEO_FLAG_IS_QUAL; - state->parent = parent; - - scratch.resvalue = &state->resvalue; - scratch.resnull = &state->resnull; - - /* compute max needed attribute */ - for (natt = 0; natt < numCols; natt++) - { - int attno = keyColIdx[natt]; - - if (attno > maxatt) - maxatt = attno; - } - Assert(maxatt >= 0); - - /* push deform steps */ - scratch.opcode = EEOP_INNER_FETCHSOME; - scratch.d.fetch.last_var = maxatt; - ExprEvalPushStep(state, &scratch); - - scratch.opcode = EEOP_OUTER_FETCHSOME; - scratch.d.fetch.last_var = maxatt; - ExprEvalPushStep(state, &scratch); - - /* - * Start comparing at the last field (least significant sort key). That's - * the most likely to be different if we are dealing with sorted input. - */ - for (natt = numCols; --natt >= 0;) - { - int attno = keyColIdx[natt]; - Form_pg_attribute att = TupleDescAttr(desc, attno - 1); - Var *larg, - *rarg; - List *args; - - /* - * Reusing ExecInitFunc() requires creating Vars, but still seems - * worth it from a code reuse perspective. - */ - - /* left arg */ - larg = makeVar(INNER_VAR, attno, att->atttypid, - att->atttypmod, InvalidOid, 0); - /* right arg */ - rarg = makeVar(OUTER_VAR, attno, att->atttypid, - att->atttypmod, InvalidOid, 0); - args = list_make2(larg, rarg); - - /* evaluate distinctness */ - ExecInitFunc(&scratch, NULL, - args, eqfunctions[natt], InvalidOid, - state); - scratch.opcode = EEOP_NOT_DISTINCT; - ExprEvalPushStep(state, &scratch); - - /* then emit EEOP_QUAL to detect if result is false (or null) */ - scratch.opcode = EEOP_QUAL; - scratch.d.qualexpr.jumpdone = -1; - ExprEvalPushStep(state, &scratch); - adjust_jumps = lappend_int(adjust_jumps, - state->steps_len - 1); - } - - /* adjust jump targets */ - foreach(lc, adjust_jumps) - { - ExprEvalStep *as = &state->steps[lfirst_int(lc)]; - - Assert(as->opcode == EEOP_QUAL); - Assert(as->d.qualexpr.jumpdone == -1); - as->d.qualexpr.jumpdone = state->steps_len; - } - - scratch.resvalue = NULL; - scratch.resnull = NULL; - scratch.opcode = EEOP_DONE; - ExprEvalPushStep(state, &scratch); - - ExecReadyExpr(state); - - return state; -} diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 771b7e3945..9c6c2b02e9 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -355,7 +355,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) &&CASE_EEOP_MAKE_READONLY, &&CASE_EEOP_IOCOERCE, &&CASE_EEOP_DISTINCT, - &&CASE_EEOP_NOT_DISTINCT, &&CASE_EEOP_NULLIF, &&CASE_EEOP_SQLVALUEFUNCTION, &&CASE_EEOP_CURRENTOFEXPR, @@ -1199,34 +1198,6 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *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) { /* diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c index 4f604fb286..8e8dbb1f20 100644 --- a/src/backend/executor/execGrouping.c +++ b/src/backend/executor/execGrouping.c @@ -52,33 +52,172 @@ static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tup *****************************************************************************/ /* - * execTuplesMatchPrepare - * Build expression that can be evaluated using ExecQual(), returning - * whether an ExprContext's inner/outer tuples are NOT DISTINCT + * execTuplesMatch + * Return true if two tuples match in all the indicated fields. + * + * This actually implements SQL's notion of "not distinct". Two nulls + * match, a null and a not-null don't match. + * + * slot1, slot2: the tuples to compare (must have same columns!) + * numCols: the number of attributes to be examined + * matchColIdx: array of attribute column numbers + * eqFunctions: array of fmgr lookup info for the equality functions to use + * evalContext: short-term memory context for executing the functions + * + * NB: evalContext is reset each time! */ -ExprState * -execTuplesMatchPrepare(TupleDesc desc, - int numCols, - AttrNumber *keyColIdx, - Oid *eqOperators, - PlanState *parent) +bool +execTuplesMatch(TupleTableSlot *slot1, + TupleTableSlot *slot2, + int numCols, + AttrNumber *matchColIdx, + FmgrInfo *eqfunctions, + MemoryContext evalContext) { - Oid *eqFunctions = (Oid *) palloc(numCols * sizeof(Oid)); + MemoryContext oldContext; + bool result; int i; - ExprState *expr; - if (numCols == 0) - return NULL; + /* Reset and switch into the temp context. */ + MemoryContextReset(evalContext); + oldContext = MemoryContextSwitchTo(evalContext); + + /* + * We cannot report a match without checking all the fields, but we can + * report a non-match as soon as we find unequal fields. So, start + * comparing at the last field (least significant sort key). That's the + * most likely to be different if we are dealing with sorted input. + */ + result = true; + + for (i = numCols; --i >= 0;) + { + AttrNumber att = matchColIdx[i]; + Datum attr1, + attr2; + bool isNull1, + isNull2; + + attr1 = slot_getattr(slot1, att, &isNull1); + + attr2 = slot_getattr(slot2, att, &isNull2); + + if (isNull1 != isNull2) + { + result = false; /* one null and one not; they aren't equal */ + break; + } + + if (isNull1) + continue; /* both are null, treat as equal */ + + /* Apply the type-specific equality function */ + + if (!DatumGetBool(FunctionCall2(&eqfunctions[i], + attr1, attr2))) + { + result = false; /* they aren't equal */ + break; + } + } + + MemoryContextSwitchTo(oldContext); + + return result; +} + +/* + * execTuplesUnequal + * Return true if two tuples are definitely unequal in the indicated + * fields. + * + * Nulls are neither equal nor unequal to anything else. A true result + * is obtained only if there are non-null fields that compare not-equal. + * + * Parameters are identical to execTuplesMatch. + */ +bool +execTuplesUnequal(TupleTableSlot *slot1, + TupleTableSlot *slot2, + int numCols, + AttrNumber *matchColIdx, + FmgrInfo *eqfunctions, + MemoryContext evalContext) +{ + MemoryContext oldContext; + bool result; + int i; + + /* Reset and switch into the temp context. */ + MemoryContextReset(evalContext); + oldContext = MemoryContextSwitchTo(evalContext); + + /* + * We cannot report a match without checking all the fields, but we can + * report a non-match as soon as we find unequal fields. So, start + * comparing at the last field (least significant sort key). That's the + * most likely to be different if we are dealing with sorted input. + */ + result = false; + + for (i = numCols; --i >= 0;) + { + AttrNumber att = matchColIdx[i]; + Datum attr1, + attr2; + bool isNull1, + isNull2; + + attr1 = slot_getattr(slot1, att, &isNull1); + + if (isNull1) + continue; /* can't prove anything here */ + + attr2 = slot_getattr(slot2, att, &isNull2); + + if (isNull2) + continue; /* can't prove anything here */ + + /* Apply the type-specific equality function */ + + if (!DatumGetBool(FunctionCall2(&eqfunctions[i], + attr1, attr2))) + { + result = true; /* they are unequal */ + break; + } + } + + MemoryContextSwitchTo(oldContext); + + return result; +} + + +/* + * execTuplesMatchPrepare + * Look up the equality functions needed for execTuplesMatch or + * execTuplesUnequal, given an array of equality operator OIDs. + * + * The result is a palloc'd array. + */ +FmgrInfo * +execTuplesMatchPrepare(int numCols, + Oid *eqOperators) +{ + FmgrInfo *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); + int i; - /* lookup equality functions */ for (i = 0; i < numCols; i++) - eqFunctions[i] = get_opcode(eqOperators[i]); + { + Oid eq_opr = eqOperators[i]; + Oid eq_function; - /* build actual expression */ - expr = ExecBuildGroupingEqual(desc, numCols, keyColIdx, eqFunctions, - parent); + eq_function = get_opcode(eq_opr); + fmgr_info(eq_function, &eqFunctions[i]); + } - return expr; + return eqFunctions; } /* @@ -149,9 +288,7 @@ execTuplesHashPrepare(int numCols, * storage that will live as long as the hashtable does. */ TupleHashTable -BuildTupleHashTable(PlanState *parent, - TupleDesc inputDesc, - int numCols, AttrNumber *keyColIdx, +BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, FmgrInfo *eqfunctions, FmgrInfo *hashfunctions, long nbuckets, Size additionalsize, @@ -160,9 +297,6 @@ BuildTupleHashTable(PlanState *parent, { TupleHashTable hashtable; Size entrysize = sizeof(TupleHashEntryData) + additionalsize; - MemoryContext oldcontext; - Oid *eqoids = (Oid *) palloc(numCols * sizeof(Oid)); - int i; Assert(nbuckets > 0); @@ -199,26 +333,6 @@ BuildTupleHashTable(PlanState *parent, hashtable->hashtab = tuplehash_create(tablecxt, nbuckets, hashtable); - oldcontext = MemoryContextSwitchTo(hashtable->tablecxt); - - /* - * We copy the input tuple descriptor just for safety --- we assume all - * input tuples will have equivalent descriptors. - */ - hashtable->tableslot = MakeSingleTupleTableSlot(CreateTupleDescCopy(inputDesc)); - - /* build comparator for all columns */ - for (i = 0; i < numCols; i++) - eqoids[i] = eqfunctions[i].fn_oid; - hashtable->eq_func = ExecBuildGroupingEqual(inputDesc, - numCols, - keyColIdx, eqoids, - parent); - - MemoryContextSwitchTo(oldcontext); - - hashtable->exprcontext = CreateExprContext(parent->state); - return hashtable; } @@ -243,6 +357,22 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot, bool found; MinimalTuple key; + /* If first time through, clone the input slot to make table slot */ + if (hashtable->tableslot == NULL) + { + TupleDesc tupdesc; + + oldContext = MemoryContextSwitchTo(hashtable->tablecxt); + + /* + * We copy the input tuple descriptor just for safety --- we assume + * all input tuples will have equivalent descriptors. + */ + tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor); + hashtable->tableslot = MakeSingleTupleTableSlot(tupdesc); + MemoryContextSwitchTo(oldContext); + } + /* Need to run the hash functions in short-lived context */ oldContext = MemoryContextSwitchTo(hashtable->tempcxt); @@ -394,6 +524,9 @@ TupleHashTableHash(struct tuplehash_hash *tb, const MinimalTuple tuple) * See whether two tuples (presumably of the same hash value) match * * As above, the passed pointers are pointers to TupleHashEntryData. + * + * Also, the caller must select an appropriate memory context for running + * the compare functions. (dynahash.c doesn't change CurrentMemoryContext.) */ static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const MinimalTuple tuple2) @@ -401,7 +534,6 @@ TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const TupleTableSlot *slot1; TupleTableSlot *slot2; TupleHashTable hashtable = (TupleHashTable) tb->private_data; - ExprContext *econtext = hashtable->exprcontext; /* * We assume that simplehash.h will only ever call us with the first @@ -416,7 +548,13 @@ TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const slot2 = hashtable->inputslot; /* For crosstype comparisons, the inputslot must be first */ - econtext->ecxt_innertuple = slot1; - econtext->ecxt_outertuple = slot2; - return !ExecQualAndReset(hashtable->eq_func, econtext); + if (execTuplesMatch(slot2, + slot1, + hashtable->numCols, + hashtable->keyColIdx, + hashtable->cur_eq_funcs, + hashtable->tempcxt)) + return 0; + else + return 1; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 467f8d896e..a86d4b68ea 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -755,7 +755,7 @@ process_ordered_aggregate_single(AggState *aggstate, ((oldIsNull && *isNull) || (!oldIsNull && !*isNull && oldAbbrevVal == newAbbrevVal && - DatumGetBool(FunctionCall2(&pertrans->equalfnOne, + DatumGetBool(FunctionCall2(&pertrans->equalfns[0], oldVal, *newVal))))) { /* equal to prior, so forget this one */ @@ -802,7 +802,7 @@ process_ordered_aggregate_multi(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroupstate) { - ExprContext *tmpcontext = aggstate->tmpcontext; + MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory; FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo; TupleTableSlot *slot1 = pertrans->sortslot; TupleTableSlot *slot2 = pertrans->uniqslot; @@ -811,7 +811,6 @@ process_ordered_aggregate_multi(AggState *aggstate, Datum newAbbrevVal = (Datum) 0; Datum oldAbbrevVal = (Datum) 0; bool haveOldValue = false; - TupleTableSlot *save = aggstate->tmpcontext->ecxt_outertuple; int i; tuplesort_performsort(pertrans->sortstates[aggstate->current_set]); @@ -825,20 +824,22 @@ process_ordered_aggregate_multi(AggState *aggstate, { CHECK_FOR_INTERRUPTS(); - tmpcontext->ecxt_outertuple = slot1; - tmpcontext->ecxt_innertuple = slot2; + /* + * Extract the first numTransInputs columns as datums to pass to the + * transfn. (This will help execTuplesMatch too, so we do it + * immediately.) + */ + slot_getsomeattrs(slot1, numTransInputs); if (numDistinctCols == 0 || !haveOldValue || newAbbrevVal != oldAbbrevVal || - !ExecQual(pertrans->equalfnMulti, tmpcontext)) + !execTuplesMatch(slot1, slot2, + numDistinctCols, + pertrans->sortColIdx, + pertrans->equalfns, + workcontext)) { - /* - * Extract the first numTransInputs columns as datums to pass to - * the transfn. - */ - slot_getsomeattrs(slot1, numTransInputs); - /* Load values into fcinfo */ /* Start from 1, since the 0th arg will be the transition value */ for (i = 0; i < numTransInputs; i++) @@ -856,14 +857,15 @@ process_ordered_aggregate_multi(AggState *aggstate, slot2 = slot1; slot1 = tmpslot; - /* avoid ExecQual() calls by reusing abbreviated keys */ + /* avoid execTuplesMatch() calls by reusing abbreviated keys */ oldAbbrevVal = newAbbrevVal; haveOldValue = true; } } - /* Reset context each time */ - ResetExprContext(tmpcontext); + /* Reset context each time, unless execTuplesMatch did it for us */ + if (numDistinctCols == 0) + MemoryContextReset(workcontext); ExecClearTuple(slot1); } @@ -873,9 +875,6 @@ process_ordered_aggregate_multi(AggState *aggstate, tuplesort_end(pertrans->sortstates[aggstate->current_set]); pertrans->sortstates[aggstate->current_set] = NULL; - - /* restore previous slot, potentially in use for grouping sets */ - tmpcontext->ecxt_outertuple = save; } /* @@ -1277,9 +1276,7 @@ build_hash_table(AggState *aggstate) Assert(perhash->aggnode->numGroups > 0); - perhash->hashtable = BuildTupleHashTable(&aggstate->ss.ps, - perhash->hashslot->tts_tupleDescriptor, - perhash->numCols, + perhash->hashtable = BuildTupleHashTable(perhash->numCols, perhash->hashGrpColIdxHash, perhash->eqfunctions, perhash->hashfunctions, @@ -1317,7 +1314,6 @@ find_hash_columns(AggState *aggstate) Bitmapset *base_colnos; List *outerTlist = outerPlanState(aggstate)->plan->targetlist; int numHashes = aggstate->num_hashes; - EState *estate = aggstate->ss.ps.state; int j; /* Find Vars that will be needed in tlist and qual */ @@ -1397,12 +1393,6 @@ find_hash_columns(AggState *aggstate) } hashDesc = ExecTypeFromTL(hashTlist, false); - - execTuplesHashPrepare(perhash->numCols, - perhash->aggnode->grpOperators, - &perhash->eqfunctions, - &perhash->hashfunctions); - perhash->hashslot = ExecAllocTableSlot(&estate->es_tupleTable); ExecSetSlotDescriptor(perhash->hashslot, hashDesc); list_free(hashTlist); @@ -1704,14 +1694,17 @@ agg_retrieve_direct(AggState *aggstate) * of the next grouping set *---------- */ - tmpcontext->ecxt_innertuple = econtext->ecxt_outertuple; if (aggstate->input_done || (node->aggstrategy != AGG_PLAIN && aggstate->projected_set != -1 && aggstate->projected_set < (numGroupingSets - 1) && nextSetSize > 0 && - !ExecQualAndReset(aggstate->phase->eqfunctions[nextSetSize - 1], - tmpcontext))) + !execTuplesMatch(econtext->ecxt_outertuple, + tmpcontext->ecxt_outertuple, + nextSetSize, + node->grpColIdx, + aggstate->phase->eqfunctions, + tmpcontext->ecxt_per_tuple_memory))) { aggstate->projected_set += 1; @@ -1854,9 +1847,12 @@ agg_retrieve_direct(AggState *aggstate) */ if (node->aggstrategy != AGG_PLAIN) { - tmpcontext->ecxt_innertuple = firstSlot; - if (!ExecQual(aggstate->phase->eqfunctions[node->numCols - 1], - tmpcontext)) + if (!execTuplesMatch(firstSlot, + outerslot, + node->numCols, + node->grpColIdx, + aggstate->phase->eqfunctions, + tmpcontext->ecxt_per_tuple_memory)) { aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot); break; @@ -2082,7 +2078,6 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) AggStatePerGroup *pergroups; Plan *outerPlan; ExprContext *econtext; - TupleDesc scanDesc; int numaggs, transno, aggno; @@ -2238,9 +2233,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) * initialize source tuple type. */ ExecAssignScanTypeFromOuterPlan(&aggstate->ss); - scanDesc = aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor; if (node->chain) - ExecSetSlotDescriptor(aggstate->sort_slot, scanDesc); + ExecSetSlotDescriptor(aggstate->sort_slot, + aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor); /* * Initialize result tuple type and projection info. @@ -2360,43 +2355,11 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) */ if (aggnode->aggstrategy == AGG_SORTED) { - int i = 0; - Assert(aggnode->numCols > 0); - /* - * Build a separate function for each subset of columns that - * need to be compared. - */ phasedata->eqfunctions = - (ExprState **) palloc0(aggnode->numCols * sizeof(ExprState *)); - - /* for each grouping set */ - for (i = 0; i < phasedata->numsets; i++) - { - int length = phasedata->gset_lengths[i]; - - if (phasedata->eqfunctions[length - 1] != NULL) - continue; - - phasedata->eqfunctions[length - 1] = - execTuplesMatchPrepare(scanDesc, - length, - aggnode->grpColIdx, - aggnode->grpOperators, - (PlanState *) aggstate); - } - - /* and for all grouped columns, unless already computed */ - if (phasedata->eqfunctions[aggnode->numCols - 1] == NULL) - { - phasedata->eqfunctions[aggnode->numCols - 1] = - execTuplesMatchPrepare(scanDesc, - aggnode->numCols, - aggnode->grpColIdx, - aggnode->grpOperators, - (PlanState *) aggstate); - } + execTuplesMatchPrepare(aggnode->numCols, + aggnode->grpOperators); } phasedata->aggnode = aggnode; @@ -2449,6 +2412,16 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) */ if (use_hashing) { + for (i = 0; i < numHashes; ++i) + { + aggstate->perhash[i].hashslot = ExecInitExtraTupleSlot(estate); + + execTuplesHashPrepare(aggstate->perhash[i].numCols, + aggstate->perhash[i].aggnode->grpOperators, + &aggstate->perhash[i].eqfunctions, + &aggstate->perhash[i].hashfunctions); + } + /* this is an array of pointers, not structures */ aggstate->hash_pergroup = pergroups; @@ -3128,28 +3101,24 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, if (aggref->aggdistinct) { - Oid *ops; - Assert(numArguments > 0); - Assert(list_length(aggref->aggdistinct) == numDistinctCols); - ops = palloc(numDistinctCols * sizeof(Oid)); + /* + * We need the equal function for each DISTINCT comparison we will + * make. + */ + pertrans->equalfns = + (FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo)); i = 0; foreach(lc, aggref->aggdistinct) - ops[i++] = ((SortGroupClause *) lfirst(lc))->eqop; + { + SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc); - /* lookup / build the necessary comparators */ - if (numDistinctCols == 1) - fmgr_info(get_opcode(ops[0]), &pertrans->equalfnOne); - else - pertrans->equalfnMulti = - execTuplesMatchPrepare(pertrans->sortdesc, - numDistinctCols, - pertrans->sortColIdx, - ops, - &aggstate->ss.ps); - pfree(ops); + fmgr_info(get_opcode(sortcl->eqop), &pertrans->equalfns[i]); + i++; + } + Assert(i == numDistinctCols); } pertrans->sortstates = (Tuplesortstate **) diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 8f7bf459ef..f1cdbaa4e6 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -25,7 +25,6 @@ #include "executor/executor.h" #include "executor/nodeGroup.h" #include "miscadmin.h" -#include "utils/memutils.h" /* @@ -38,6 +37,8 @@ ExecGroup(PlanState *pstate) { GroupState *node = castNode(GroupState, pstate); ExprContext *econtext; + int numCols; + AttrNumber *grpColIdx; TupleTableSlot *firsttupleslot; TupleTableSlot *outerslot; @@ -49,6 +50,8 @@ ExecGroup(PlanState *pstate) if (node->grp_done) return NULL; econtext = node->ss.ps.ps_ExprContext; + numCols = ((Group *) node->ss.ps.plan)->numCols; + grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* * The ScanTupleSlot holds the (copied) first tuple of each group. @@ -56,7 +59,7 @@ ExecGroup(PlanState *pstate) firsttupleslot = node->ss.ss_ScanTupleSlot; /* - * We need not call ResetExprContext here because ExecQualAndReset() will + * We need not call ResetExprContext here because execTuplesMatch will * reset the per-tuple memory context once per input tuple. */ @@ -121,9 +124,10 @@ ExecGroup(PlanState *pstate) * Compare with first tuple and see if this tuple is of the same * group. If so, ignore it and keep scanning. */ - econtext->ecxt_innertuple = firsttupleslot; - econtext->ecxt_outertuple = outerslot; - if (!ExecQualAndReset(node->eqfunction, econtext)) + if (!execTuplesMatch(firsttupleslot, outerslot, + numCols, grpColIdx, + node->eqfunctions, + econtext->ecxt_per_tuple_memory)) break; } @@ -162,7 +166,6 @@ GroupState * ExecInitGroup(Group *node, EState *estate, int eflags) { GroupState *grpstate; - AttrNumber *grpColIdx = grpColIdx = node->grpColIdx; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -212,12 +215,9 @@ ExecInitGroup(Group *node, EState *estate, int eflags) /* * Precompute fmgr lookup data for inner loop */ - grpstate->eqfunction = - execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)), - node->numCols, - grpColIdx, - node->grpOperators, - &grpstate->ss.ps); + grpstate->eqfunctions = + execTuplesMatchPrepare(node->numCols, + node->grpOperators); return grpstate; } diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c index c070338fdb..817749855f 100644 --- a/src/backend/executor/nodeRecursiveunion.c +++ b/src/backend/executor/nodeRecursiveunion.c @@ -32,14 +32,11 @@ static void build_hash_table(RecursiveUnionState *rustate) { RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan; - TupleDesc desc = ExecGetResultType(outerPlanState(rustate)); Assert(node->numCols > 0); Assert(node->numGroups > 0); - rustate->hashtable = BuildTupleHashTable(&rustate->ps, - desc, - node->numCols, + rustate->hashtable = BuildTupleHashTable(node->numCols, node->dupColIdx, rustate->eqfunctions, rustate->hashfunctions, diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c index ba2d3159c0..c91c3402d2 100644 --- a/src/backend/executor/nodeSetOp.c +++ b/src/backend/executor/nodeSetOp.c @@ -120,22 +120,18 @@ static void build_hash_table(SetOpState *setopstate) { SetOp *node = (SetOp *) setopstate->ps.plan; - ExprContext *econtext = setopstate->ps.ps_ExprContext; - TupleDesc desc = ExecGetResultType(outerPlanState(setopstate)); Assert(node->strategy == SETOP_HASHED); Assert(node->numGroups > 0); - setopstate->hashtable = BuildTupleHashTable(&setopstate->ps, - desc, - node->numCols, + setopstate->hashtable = BuildTupleHashTable(node->numCols, node->dupColIdx, setopstate->eqfunctions, setopstate->hashfunctions, node->numGroups, 0, setopstate->tableContext, - econtext->ecxt_per_tuple_memory, + setopstate->tempContext, false); } @@ -224,11 +220,11 @@ ExecSetOp(PlanState *pstate) static TupleTableSlot * setop_retrieve_direct(SetOpState *setopstate) { + SetOp *node = (SetOp *) setopstate->ps.plan; PlanState *outerPlan; SetOpStatePerGroup pergroup; TupleTableSlot *outerslot; TupleTableSlot *resultTupleSlot; - ExprContext *econtext = setopstate->ps.ps_ExprContext; /* * get state info from node @@ -296,10 +292,11 @@ setop_retrieve_direct(SetOpState *setopstate) /* * Check whether we've crossed a group boundary. */ - econtext->ecxt_outertuple = resultTupleSlot; - econtext->ecxt_innertuple = outerslot; - - if (!ExecQualAndReset(setopstate->eqfunction, econtext)) + if (!execTuplesMatch(resultTupleSlot, + outerslot, + node->numCols, node->dupColIdx, + setopstate->eqfunctions, + setopstate->tempContext)) { /* * Save the first input tuple of the next group. @@ -341,7 +338,6 @@ setop_fill_hash_table(SetOpState *setopstate) PlanState *outerPlan; int firstFlag; bool in_first_rel PG_USED_FOR_ASSERTS_ONLY; - ExprContext *econtext = setopstate->ps.ps_ExprContext; /* * get state info from node @@ -408,8 +404,8 @@ setop_fill_hash_table(SetOpState *setopstate) advance_counts((SetOpStatePerGroup) entry->additional, flag); } - /* Must reset expression context after each hashtable lookup */ - ResetExprContext(econtext); + /* Must reset temp context after each hashtable lookup */ + MemoryContextReset(setopstate->tempContext); } setopstate->table_filled = true; @@ -480,7 +476,6 @@ SetOpState * ExecInitSetOp(SetOp *node, EState *estate, int eflags) { SetOpState *setopstate; - TupleDesc outerDesc; /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); @@ -503,9 +498,16 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) setopstate->tableContext = NULL; /* - * create expression context + * Miscellaneous initialization + * + * SetOp nodes have no ExprContext initialization because they never call + * ExecQual or ExecProject. But they do need a per-tuple memory context + * anyway for calling execTuplesMatch. */ - ExecAssignExprContext(estate, &setopstate->ps); + setopstate->tempContext = + AllocSetContextCreate(CurrentMemoryContext, + "SetOp", + ALLOCSET_DEFAULT_SIZES); /* * If hashing, we also need a longer-lived context to store the hash @@ -532,7 +534,6 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) if (node->strategy == SETOP_HASHED) eflags &= ~EXEC_FLAG_REWIND; outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags); - outerDesc = ExecGetResultType(outerPlanState(setopstate)); /* * setop nodes do no projections, so initialize projection info for this @@ -552,12 +553,9 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags) &setopstate->eqfunctions, &setopstate->hashfunctions); else - setopstate->eqfunction = - execTuplesMatchPrepare(outerDesc, - node->numCols, - node->dupColIdx, - node->dupOperators, - &setopstate->ps); + setopstate->eqfunctions = + execTuplesMatchPrepare(node->numCols, + node->dupOperators); if (node->strategy == SETOP_HASHED) { @@ -587,9 +585,9 @@ ExecEndSetOp(SetOpState *node) ExecClearTuple(node->ps.ps_ResultTupleSlot); /* free subsidiary stuff including hashtable */ + MemoryContextDelete(node->tempContext); if (node->tableContext) MemoryContextDelete(node->tableContext); - ExecFreeExprContext(&node->ps); ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index fcf739b5e2..edf7d034bd 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -494,9 +494,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext) if (nbuckets < 1) nbuckets = 1; - node->hashtable = BuildTupleHashTable(node->parent, - node->descRight, - ncols, + node->hashtable = BuildTupleHashTable(ncols, node->keyColIdx, node->tab_eq_funcs, node->tab_hash_funcs, @@ -516,9 +514,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext) if (nbuckets < 1) nbuckets = 1; } - node->hashnulls = BuildTupleHashTable(node->parent, - node->descRight, - ncols, + node->hashnulls = BuildTupleHashTable(ncols, node->keyColIdx, node->tab_eq_funcs, node->tab_hash_funcs, @@ -602,78 +598,6 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext) MemoryContextSwitchTo(oldcontext); } - -/* - * execTuplesUnequal - * Return true if two tuples are definitely unequal in the indicated - * fields. - * - * Nulls are neither equal nor unequal to anything else. A true result - * is obtained only if there are non-null fields that compare not-equal. - * - * slot1, slot2: the tuples to compare (must have same columns!) - * numCols: the number of attributes to be examined - * matchColIdx: array of attribute column numbers - * eqFunctions: array of fmgr lookup info for the equality functions to use - * evalContext: short-term memory context for executing the functions - */ -static bool -execTuplesUnequal(TupleTableSlot *slot1, - TupleTableSlot *slot2, - int numCols, - AttrNumber *matchColIdx, - FmgrInfo *eqfunctions, - MemoryContext evalContext) -{ - MemoryContext oldContext; - bool result; - int i; - - /* Reset and switch into the temp context. */ - MemoryContextReset(evalContext); - oldContext = MemoryContextSwitchTo(evalContext); - - /* - * We cannot report a match without checking all the fields, but we can - * report a non-match as soon as we find unequal fields. So, start - * comparing at the last field (least significant sort key). That's the - * most likely to be different if we are dealing with sorted input. - */ - result = false; - - for (i = numCols; --i >= 0;) - { - AttrNumber att = matchColIdx[i]; - Datum attr1, - attr2; - bool isNull1, - isNull2; - - attr1 = slot_getattr(slot1, att, &isNull1); - - if (isNull1) - continue; /* can't prove anything here */ - - attr2 = slot_getattr(slot2, att, &isNull2); - - if (isNull2) - continue; /* can't prove anything here */ - - /* Apply the type-specific equality function */ - - if (!DatumGetBool(FunctionCall2(&eqfunctions[i], - attr1, attr2))) - { - result = true; /* they are unequal */ - break; - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * findPartialMatch: does the hashtable contain an entry that is not * provably distinct from the tuple? @@ -963,7 +887,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) NULL); tupDesc = ExecTypeFromTL(righttlist, false); - sstate->descRight = tupDesc; slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 9f823c58e1..e330650593 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -47,7 +47,7 @@ static TupleTableSlot * /* return: a tuple or NULL */ ExecUnique(PlanState *pstate) { UniqueState *node = castNode(UniqueState, pstate); - ExprContext *econtext = node->ps.ps_ExprContext; + Unique *plannode = (Unique *) node->ps.plan; TupleTableSlot *resultTupleSlot; TupleTableSlot *slot; PlanState *outerPlan; @@ -89,9 +89,10 @@ ExecUnique(PlanState *pstate) * If so then we loop back and fetch another new tuple from the * subplan. */ - econtext->ecxt_innertuple = slot; - econtext->ecxt_outertuple = resultTupleSlot; - if (!ExecQualAndReset(node->eqfunction, econtext)) + if (!execTuplesMatch(slot, resultTupleSlot, + plannode->numCols, plannode->uniqColIdx, + node->eqfunctions, + node->tempContext)) break; } @@ -128,9 +129,16 @@ ExecInitUnique(Unique *node, EState *estate, int eflags) uniquestate->ps.ExecProcNode = ExecUnique; /* - * create expression context + * Miscellaneous initialization + * + * Unique nodes have no ExprContext initialization because they never call + * ExecQual or ExecProject. But they do need a per-tuple memory context + * anyway for calling execTuplesMatch. */ - ExecAssignExprContext(estate, &uniquestate->ps); + uniquestate->tempContext = + AllocSetContextCreate(CurrentMemoryContext, + "Unique", + ALLOCSET_DEFAULT_SIZES); /* * Tuple table initialization @@ -152,12 +160,9 @@ ExecInitUnique(Unique *node, EState *estate, int eflags) /* * Precompute fmgr lookup data for inner loop */ - uniquestate->eqfunction = - execTuplesMatchPrepare(ExecGetResultType(outerPlanState(uniquestate)), - node->numCols, - node->uniqColIdx, - node->uniqOperators, - &uniquestate->ps); + uniquestate->eqfunctions = + execTuplesMatchPrepare(node->numCols, + node->uniqOperators); return uniquestate; } @@ -175,7 +180,7 @@ ExecEndUnique(UniqueState *node) /* clean up tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); - ExecFreeExprContext(&node->ps); + MemoryContextDelete(node->tempContext); ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 1c807a8292..f6412576f4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1272,13 +1272,12 @@ spool_tuples(WindowAggState *winstate, int64 pos) if (node->partNumCols > 0) { - ExprContext *econtext = winstate->tmpcontext; - - econtext->ecxt_innertuple = winstate->first_part_slot; - econtext->ecxt_outertuple = outerslot; - /* Check if this tuple still belongs to the current partition */ - if (!ExecQualAndReset(winstate->partEqfunction, econtext)) + if (!execTuplesMatch(winstate->first_part_slot, + outerslot, + node->partNumCols, node->partColIdx, + winstate->partEqfunctions, + winstate->tmpcontext->ecxt_per_tuple_memory)) { /* * end of partition; copy the tuple for the next cycle. @@ -2246,7 +2245,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) wfuncno, numaggs, aggno; - TupleDesc scanDesc; ListCell *l; /* check for unsupported flags */ @@ -2329,7 +2327,6 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * store in the tuplestore and use in all our working slots). */ ExecAssignScanTypeFromOuterPlan(&winstate->ss); - scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor; ExecSetSlotDescriptor(winstate->first_part_slot, winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor); @@ -2354,20 +2351,11 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) /* Set up data for comparing tuples */ if (node->partNumCols > 0) - winstate->partEqfunction = - execTuplesMatchPrepare(scanDesc, - node->partNumCols, - node->partColIdx, - node->partOperators, - &winstate->ss.ps); - + winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols, + node->partOperators); if (node->ordNumCols > 0) - winstate->ordEqfunction = - execTuplesMatchPrepare(scanDesc, - node->ordNumCols, - node->ordColIdx, - node->ordOperators, - &winstate->ss.ps); + winstate->ordEqfunctions = execTuplesMatchPrepare(node->ordNumCols, + node->ordOperators); /* * WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes. @@ -2891,15 +2879,15 @@ are_peers(WindowAggState *winstate, TupleTableSlot *slot1, TupleTableSlot *slot2) { WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan; - ExprContext *econtext = winstate->tmpcontext; /* If no ORDER BY, all rows are peers with each other */ if (node->ordNumCols == 0) return true; - econtext->ecxt_outertuple = slot1; - econtext->ecxt_innertuple = slot2; - return ExecQualAndReset(winstate->ordEqfunction, econtext); + return execTuplesMatch(slot1, slot2, + node->ordNumCols, node->ordColIdx, + winstate->ordEqfunctions, + winstate->tmpcontext->ecxt_per_tuple_memory); } /* diff --git a/src/backend/utils/adt/orderedsetaggs.c b/src/backend/utils/adt/orderedsetaggs.c index 50b34fcbc6..63d9c67027 100644 --- a/src/backend/utils/adt/orderedsetaggs.c +++ b/src/backend/utils/adt/orderedsetaggs.c @@ -27,7 +27,6 @@ #include "utils/array.h" #include "utils/builtins.h" #include "utils/lsyscache.h" -#include "utils/memutils.h" #include "utils/timestamp.h" #include "utils/tuplesort.h" @@ -55,8 +54,6 @@ typedef struct OSAPerQueryState Aggref *aggref; /* Memory context containing this struct and other per-query data: */ MemoryContext qcontext; - /* Context for expression evaluation */ - ExprContext *econtext; /* Do we expect multiple final-function calls within one group? */ bool rescan_needed; @@ -74,7 +71,7 @@ typedef struct OSAPerQueryState Oid *sortCollations; bool *sortNullsFirsts; /* Equality operator call info, created only if needed: */ - ExprState *compareTuple; + FmgrInfo *equalfns; /* These fields are used only when accumulating datums: */ @@ -1290,8 +1287,6 @@ hypothetical_cume_dist_final(PG_FUNCTION_ARGS) Datum hypothetical_dense_rank_final(PG_FUNCTION_ARGS) { - ExprContext *econtext; - ExprState *compareTuple; int nargs = PG_NARGS() - 1; int64 rank = 1; int64 duplicate_count = 0; @@ -1299,9 +1294,12 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) int numDistinctCols; Datum abbrevVal = (Datum) 0; Datum abbrevOld = (Datum) 0; + AttrNumber *sortColIdx; + FmgrInfo *equalfns; TupleTableSlot *slot; TupleTableSlot *extraslot; TupleTableSlot *slot2; + MemoryContext tmpcontext; int i; Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE); @@ -1311,9 +1309,6 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) PG_RETURN_INT64(rank); osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0); - econtext = osastate->qstate->econtext; - if (!econtext) - osastate->qstate->econtext = econtext = CreateStandaloneExprContext(); /* Adjust nargs to be the number of direct (or aggregated) args */ if (nargs % 2 != 0) @@ -1328,22 +1323,26 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) */ numDistinctCols = osastate->qstate->numSortCols - 1; - /* Build tuple comparator, if we didn't already */ - compareTuple = osastate->qstate->compareTuple; - if (compareTuple == NULL) + /* Look up the equality function(s), if we didn't already */ + equalfns = osastate->qstate->equalfns; + if (equalfns == NULL) { - AttrNumber *sortColIdx = osastate->qstate->sortColIdx; - MemoryContext oldContext; + MemoryContext qcontext = osastate->qstate->qcontext; - oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext); - compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc, - numDistinctCols, - sortColIdx, - osastate->qstate->eqOperators, - NULL); - MemoryContextSwitchTo(oldContext); - osastate->qstate->compareTuple = compareTuple; + equalfns = (FmgrInfo *) + MemoryContextAlloc(qcontext, numDistinctCols * sizeof(FmgrInfo)); + for (i = 0; i < numDistinctCols; i++) + { + fmgr_info_cxt(get_opcode(osastate->qstate->eqOperators[i]), + &equalfns[i], + qcontext); + } + osastate->qstate->equalfns = equalfns; } + sortColIdx = osastate->qstate->sortColIdx; + + /* Get short-term context we can use for execTuplesMatch */ + tmpcontext = AggGetTempMemoryContext(fcinfo); /* because we need a hypothetical row, we can't share transition state */ Assert(!osastate->sort_done); @@ -1386,18 +1385,19 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) break; /* count non-distinct tuples */ - econtext->ecxt_outertuple = slot; - econtext->ecxt_innertuple = slot2; - if (!TupIsNull(slot2) && abbrevVal == abbrevOld && - ExecQualAndReset(compareTuple, econtext)) + execTuplesMatch(slot, slot2, + numDistinctCols, + sortColIdx, + equalfns, + tmpcontext)) duplicate_count++; tmpslot = slot2; slot2 = slot; slot = tmpslot; - /* avoid ExecQual() calls by reusing abbreviated keys */ + /* avoid execTuplesMatch() calls by reusing abbreviated keys */ abbrevOld = abbrevVal; rank++; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 0cab431f65..117fc892f4 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -148,7 +148,6 @@ typedef enum ExprEvalOp /* evaluate assorted special-purpose expression types */ EEOP_IOCOERCE, EEOP_DISTINCT, - EEOP_NOT_DISTINCT, EEOP_NULLIF, EEOP_SQLVALUEFUNCTION, EEOP_CURRENTOFEXPR, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index f648af2789..1d824eff36 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -113,18 +113,25 @@ extern bool execCurrentOf(CurrentOfExpr *cexpr, /* * prototypes from functions in execGrouping.c */ -extern ExprState *execTuplesMatchPrepare(TupleDesc desc, - int numCols, - AttrNumber *keyColIdx, - Oid *eqOperators, - PlanState *parent); +extern bool execTuplesMatch(TupleTableSlot *slot1, + TupleTableSlot *slot2, + int numCols, + AttrNumber *matchColIdx, + FmgrInfo *eqfunctions, + MemoryContext evalContext); +extern bool execTuplesUnequal(TupleTableSlot *slot1, + TupleTableSlot *slot2, + int numCols, + AttrNumber *matchColIdx, + FmgrInfo *eqfunctions, + MemoryContext evalContext); +extern FmgrInfo *execTuplesMatchPrepare(int numCols, + Oid *eqOperators); extern void execTuplesHashPrepare(int numCols, Oid *eqOperators, FmgrInfo **eqFunctions, FmgrInfo **hashFunctions); -extern TupleHashTable BuildTupleHashTable(PlanState *parent, - TupleDesc inputDesc, - int numCols, AttrNumber *keyColIdx, +extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx, FmgrInfo *eqfunctions, FmgrInfo *hashfunctions, long nbuckets, Size additionalsize, @@ -250,11 +257,6 @@ extern ExprState *ExecInitCheck(List *qual, PlanState *parent); extern List *ExecInitExprList(List *nodes, PlanState *parent); extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase, bool doSort, bool doHash); -extern ExprState *ExecBuildGroupingEqual(TupleDesc desc, - int numCols, - AttrNumber *keyColIdx, - Oid *eqfunctions, - PlanState *parent); extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h index 24be7d2daa..3b06db86fd 100644 --- a/src/include/executor/nodeAgg.h +++ b/src/include/executor/nodeAgg.h @@ -102,12 +102,11 @@ typedef struct AggStatePerTransData bool *sortNullsFirst; /* - * Comparators for input columns --- only set/used when aggregate has - * DISTINCT flag. equalfnOne version is used for single-column - * commparisons, equalfnMulti for the case of multiple columns. + * fmgr lookup data for input columns' equality operators --- only + * set/used when aggregate has DISTINCT flag. Note that these are in + * order of sort column index, not parameter index. */ - FmgrInfo equalfnOne; - ExprState *equalfnMulti; + FmgrInfo *equalfns; /* array of length numDistinctCols */ /* * initial value from pg_aggregate entry @@ -271,8 +270,7 @@ typedef struct AggStatePerPhaseData int numsets; /* number of grouping sets (or 0) */ int *gset_lengths; /* lengths of grouping sets */ Bitmapset **grouped_cols; /* column groupings for rollup */ - ExprState **eqfunctions; /* expression returning equality, indexed by - * nr of cols to compare */ + FmgrInfo *eqfunctions; /* per-grouping-field equality fns */ Agg *aggnode; /* Agg node for phase data */ Sort *sortnode; /* Sort node for input ordering for phase */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 74c359901c..286d55be03 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -635,8 +635,6 @@ typedef struct TupleHashTableData FmgrInfo *in_hash_funcs; /* hash functions for input datatype(s) */ FmgrInfo *cur_eq_funcs; /* equality functions for input vs. table */ uint32 hash_iv; /* hash-function IV */ - ExprState *eq_func; /* tuple equality comparator */ - ExprContext *exprcontext; /* expression context */ } TupleHashTableData; typedef tuplehash_iterator TupleHashIterator; @@ -783,7 +781,6 @@ typedef struct SubPlanState HeapTuple curTuple; /* copy of most recent tuple from subplan */ Datum curArray; /* most recent array from ARRAY() subplan */ /* these are used when hashing the subselect's output: */ - TupleDesc descRight; /* subselect desc after projection */ ProjectionInfo *projLeft; /* for projecting lefthand exprs */ ProjectionInfo *projRight; /* for projecting subselect output */ TupleHashTable hashtable; /* hash table for no-nulls subselect rows */ @@ -1798,7 +1795,7 @@ typedef struct SortState typedef struct GroupState { ScanState ss; /* its first field is NodeTag */ - ExprState *eqfunction; /* equality function */ + FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */ bool grp_done; /* indicates completion of Group scan */ } GroupState; @@ -1888,8 +1885,8 @@ typedef struct WindowAggState WindowStatePerFunc perfunc; /* per-window-function information */ WindowStatePerAgg peragg; /* per-plain-aggregate information */ - ExprState *partEqfunction; /* equality funcs for partition columns */ - ExprState *ordEqfunction; /* equality funcs for ordering columns */ + FmgrInfo *partEqfunctions; /* equality funcs for partition columns */ + FmgrInfo *ordEqfunctions; /* equality funcs for ordering columns */ Tuplestorestate *buffer; /* stores rows of current partition */ int current_ptr; /* read pointer # for current row */ int framehead_ptr; /* read pointer # for frame head, if used */ @@ -1967,7 +1964,8 @@ typedef struct WindowAggState typedef struct UniqueState { PlanState ps; /* its first field is NodeTag */ - ExprState *eqfunction; /* tuple equality qual */ + FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */ + MemoryContext tempContext; /* short-term context for comparisons */ } UniqueState; /* ---------------- @@ -2081,11 +2079,11 @@ typedef struct SetOpStatePerGroupData *SetOpStatePerGroup; typedef struct SetOpState { PlanState ps; /* its first field is NodeTag */ - ExprState *eqfunction; /* equality comparator */ FmgrInfo *eqfunctions; /* per-grouping-field equality fns */ FmgrInfo *hashfunctions; /* per-grouping-field hash fns */ bool setop_done; /* indicates completion of output scan */ long numOutput; /* number of dups left to output */ + MemoryContext tempContext; /* short-term context for comparisons */ /* these fields are used in SETOP_SORTED mode: */ SetOpStatePerGroup pergroup; /* per-group working state */ HeapTuple grp_firstTuple; /* copy of first tuple of current group */