Do execGrouping.c via expression eval machinery.

This has a performance benefit on own, although not hugely so. The
primary benefit is that it will allow for to JIT tuple deforming and
comparator invocations.

Author: Andres Freund
Discussion: https://postgr.es/m/20171129080934.amqqkke2zjtekd4t@alap3.anarazel.de
This commit is contained in:
Andres Freund 2018-02-15 21:55:31 -08:00
parent 51db0d18fb
commit 773aec7aa9
15 changed files with 498 additions and 366 deletions

View File

@ -3193,3 +3193,121 @@ 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;
}

View File

@ -355,6 +355,7 @@ 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,
@ -1198,6 +1199,34 @@ 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)
{
/*

View File

@ -51,173 +51,34 @@ static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tup
* Utility routines for grouping tuples together
*****************************************************************************/
/*
* 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!
*/
bool
execTuplesMatch(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 = 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.
* Build expression that can be evaluated using ExecQual(), returning
* whether an ExprContext's inner/outer tuples are NOT DISTINCT
*/
FmgrInfo *
execTuplesMatchPrepare(int numCols,
Oid *eqOperators)
ExprState *
execTuplesMatchPrepare(TupleDesc desc,
int numCols,
AttrNumber *keyColIdx,
Oid *eqOperators,
PlanState *parent)
{
FmgrInfo *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
Oid *eqFunctions = (Oid *) palloc(numCols * sizeof(Oid));
int i;
ExprState *expr;
if (numCols == 0)
return NULL;
/* lookup equality functions */
for (i = 0; i < numCols; i++)
{
Oid eq_opr = eqOperators[i];
Oid eq_function;
eqFunctions[i] = get_opcode(eqOperators[i]);
eq_function = get_opcode(eq_opr);
fmgr_info(eq_function, &eqFunctions[i]);
}
/* build actual expression */
expr = ExecBuildGroupingEqual(desc, numCols, keyColIdx, eqFunctions,
parent);
return eqFunctions;
return expr;
}
/*
@ -288,7 +149,9 @@ execTuplesHashPrepare(int numCols,
* storage that will live as long as the hashtable does.
*/
TupleHashTable
BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
BuildTupleHashTable(PlanState *parent,
TupleDesc inputDesc,
int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
long nbuckets, Size additionalsize,
@ -297,6 +160,9 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
{
TupleHashTable hashtable;
Size entrysize = sizeof(TupleHashEntryData) + additionalsize;
MemoryContext oldcontext;
Oid *eqoids = (Oid *) palloc(numCols * sizeof(Oid));
int i;
Assert(nbuckets > 0);
@ -333,6 +199,26 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
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;
}
@ -357,22 +243,6 @@ 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);
@ -524,9 +394,6 @@ 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)
@ -534,6 +401,7 @@ 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
@ -548,13 +416,7 @@ TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const
slot2 = hashtable->inputslot;
/* For crosstype comparisons, the inputslot must be first */
if (execTuplesMatch(slot2,
slot1,
hashtable->numCols,
hashtable->keyColIdx,
hashtable->cur_eq_funcs,
hashtable->tempcxt))
return 0;
else
return 1;
econtext->ecxt_innertuple = slot1;
econtext->ecxt_outertuple = slot2;
return !ExecQualAndReset(hashtable->eq_func, econtext);
}

View File

@ -755,7 +755,7 @@ process_ordered_aggregate_single(AggState *aggstate,
((oldIsNull && *isNull) ||
(!oldIsNull && !*isNull &&
oldAbbrevVal == newAbbrevVal &&
DatumGetBool(FunctionCall2(&pertrans->equalfns[0],
DatumGetBool(FunctionCall2(&pertrans->equalfnOne,
oldVal, *newVal)))))
{
/* equal to prior, so forget this one */
@ -802,7 +802,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
AggStatePerTrans pertrans,
AggStatePerGroup pergroupstate)
{
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
ExprContext *tmpcontext = aggstate->tmpcontext;
FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
TupleTableSlot *slot1 = pertrans->sortslot;
TupleTableSlot *slot2 = pertrans->uniqslot;
@ -811,6 +811,7 @@ 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]);
@ -824,22 +825,20 @@ process_ordered_aggregate_multi(AggState *aggstate,
{
CHECK_FOR_INTERRUPTS();
/*
* 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);
tmpcontext->ecxt_outertuple = slot1;
tmpcontext->ecxt_innertuple = slot2;
if (numDistinctCols == 0 ||
!haveOldValue ||
newAbbrevVal != oldAbbrevVal ||
!execTuplesMatch(slot1, slot2,
numDistinctCols,
pertrans->sortColIdx,
pertrans->equalfns,
workcontext))
!ExecQual(pertrans->equalfnMulti, tmpcontext))
{
/*
* 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++)
@ -857,15 +856,14 @@ process_ordered_aggregate_multi(AggState *aggstate,
slot2 = slot1;
slot1 = tmpslot;
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
/* avoid ExecQual() calls by reusing abbreviated keys */
oldAbbrevVal = newAbbrevVal;
haveOldValue = true;
}
}
/* Reset context each time, unless execTuplesMatch did it for us */
if (numDistinctCols == 0)
MemoryContextReset(workcontext);
/* Reset context each time */
ResetExprContext(tmpcontext);
ExecClearTuple(slot1);
}
@ -875,6 +873,9 @@ 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;
}
/*
@ -1276,7 +1277,9 @@ build_hash_table(AggState *aggstate)
Assert(perhash->aggnode->numGroups > 0);
perhash->hashtable = BuildTupleHashTable(perhash->numCols,
perhash->hashtable = BuildTupleHashTable(&aggstate->ss.ps,
perhash->hashslot->tts_tupleDescriptor,
perhash->numCols,
perhash->hashGrpColIdxHash,
perhash->eqfunctions,
perhash->hashfunctions,
@ -1314,6 +1317,7 @@ 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 */
@ -1393,6 +1397,12 @@ 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);
@ -1694,17 +1704,14 @@ 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 &&
!execTuplesMatch(econtext->ecxt_outertuple,
tmpcontext->ecxt_outertuple,
nextSetSize,
node->grpColIdx,
aggstate->phase->eqfunctions,
tmpcontext->ecxt_per_tuple_memory)))
!ExecQualAndReset(aggstate->phase->eqfunctions[nextSetSize - 1],
tmpcontext)))
{
aggstate->projected_set += 1;
@ -1847,12 +1854,9 @@ agg_retrieve_direct(AggState *aggstate)
*/
if (node->aggstrategy != AGG_PLAIN)
{
if (!execTuplesMatch(firstSlot,
outerslot,
node->numCols,
node->grpColIdx,
aggstate->phase->eqfunctions,
tmpcontext->ecxt_per_tuple_memory))
tmpcontext->ecxt_innertuple = firstSlot;
if (!ExecQual(aggstate->phase->eqfunctions[node->numCols - 1],
tmpcontext))
{
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
break;
@ -2078,6 +2082,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
AggStatePerGroup *pergroups;
Plan *outerPlan;
ExprContext *econtext;
TupleDesc scanDesc;
int numaggs,
transno,
aggno;
@ -2233,9 +2238,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,
aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
ExecSetSlotDescriptor(aggstate->sort_slot, scanDesc);
/*
* Initialize result tuple type and projection info.
@ -2355,11 +2360,43 @@ 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 =
execTuplesMatchPrepare(aggnode->numCols,
aggnode->grpOperators);
(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);
}
}
phasedata->aggnode = aggnode;
@ -2412,16 +2449,6 @@ 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;
@ -3101,24 +3128,28 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
if (aggref->aggdistinct)
{
Assert(numArguments > 0);
Oid *ops;
/*
* We need the equal function for each DISTINCT comparison we will
* make.
*/
pertrans->equalfns =
(FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo));
Assert(numArguments > 0);
Assert(list_length(aggref->aggdistinct) == numDistinctCols);
ops = palloc(numDistinctCols * sizeof(Oid));
i = 0;
foreach(lc, aggref->aggdistinct)
{
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
ops[i++] = ((SortGroupClause *) lfirst(lc))->eqop;
fmgr_info(get_opcode(sortcl->eqop), &pertrans->equalfns[i]);
i++;
}
Assert(i == numDistinctCols);
/* 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);
}
pertrans->sortstates = (Tuplesortstate **)

View File

@ -25,6 +25,7 @@
#include "executor/executor.h"
#include "executor/nodeGroup.h"
#include "miscadmin.h"
#include "utils/memutils.h"
/*
@ -37,8 +38,6 @@ ExecGroup(PlanState *pstate)
{
GroupState *node = castNode(GroupState, pstate);
ExprContext *econtext;
int numCols;
AttrNumber *grpColIdx;
TupleTableSlot *firsttupleslot;
TupleTableSlot *outerslot;
@ -50,8 +49,6 @@ 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.
@ -59,7 +56,7 @@ ExecGroup(PlanState *pstate)
firsttupleslot = node->ss.ss_ScanTupleSlot;
/*
* We need not call ResetExprContext here because execTuplesMatch will
* We need not call ResetExprContext here because ExecQualAndReset() will
* reset the per-tuple memory context once per input tuple.
*/
@ -124,10 +121,9 @@ ExecGroup(PlanState *pstate)
* Compare with first tuple and see if this tuple is of the same
* group. If so, ignore it and keep scanning.
*/
if (!execTuplesMatch(firsttupleslot, outerslot,
numCols, grpColIdx,
node->eqfunctions,
econtext->ecxt_per_tuple_memory))
econtext->ecxt_innertuple = firsttupleslot;
econtext->ecxt_outertuple = outerslot;
if (!ExecQualAndReset(node->eqfunction, econtext))
break;
}
@ -166,6 +162,7 @@ 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)));
@ -215,9 +212,12 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
/*
* Precompute fmgr lookup data for inner loop
*/
grpstate->eqfunctions =
execTuplesMatchPrepare(node->numCols,
node->grpOperators);
grpstate->eqfunction =
execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
node->numCols,
grpColIdx,
node->grpOperators,
&grpstate->ss.ps);
return grpstate;
}

View File

@ -32,11 +32,14 @@ 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(node->numCols,
rustate->hashtable = BuildTupleHashTable(&rustate->ps,
desc,
node->numCols,
node->dupColIdx,
rustate->eqfunctions,
rustate->hashfunctions,

View File

@ -120,18 +120,22 @@ 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(node->numCols,
setopstate->hashtable = BuildTupleHashTable(&setopstate->ps,
desc,
node->numCols,
node->dupColIdx,
setopstate->eqfunctions,
setopstate->hashfunctions,
node->numGroups,
0,
setopstate->tableContext,
setopstate->tempContext,
econtext->ecxt_per_tuple_memory,
false);
}
@ -220,11 +224,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
@ -292,11 +296,10 @@ setop_retrieve_direct(SetOpState *setopstate)
/*
* Check whether we've crossed a group boundary.
*/
if (!execTuplesMatch(resultTupleSlot,
outerslot,
node->numCols, node->dupColIdx,
setopstate->eqfunctions,
setopstate->tempContext))
econtext->ecxt_outertuple = resultTupleSlot;
econtext->ecxt_innertuple = outerslot;
if (!ExecQualAndReset(setopstate->eqfunction, econtext))
{
/*
* Save the first input tuple of the next group.
@ -338,6 +341,7 @@ 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
@ -404,8 +408,8 @@ setop_fill_hash_table(SetOpState *setopstate)
advance_counts((SetOpStatePerGroup) entry->additional, flag);
}
/* Must reset temp context after each hashtable lookup */
MemoryContextReset(setopstate->tempContext);
/* Must reset expression context after each hashtable lookup */
ResetExprContext(econtext);
}
setopstate->table_filled = true;
@ -476,6 +480,7 @@ SetOpState *
ExecInitSetOp(SetOp *node, EState *estate, int eflags)
{
SetOpState *setopstate;
TupleDesc outerDesc;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@ -498,16 +503,9 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
setopstate->tableContext = NULL;
/*
* 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.
* create expression context
*/
setopstate->tempContext =
AllocSetContextCreate(CurrentMemoryContext,
"SetOp",
ALLOCSET_DEFAULT_SIZES);
ExecAssignExprContext(estate, &setopstate->ps);
/*
* If hashing, we also need a longer-lived context to store the hash
@ -534,6 +532,7 @@ 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
@ -553,9 +552,12 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
&setopstate->eqfunctions,
&setopstate->hashfunctions);
else
setopstate->eqfunctions =
execTuplesMatchPrepare(node->numCols,
node->dupOperators);
setopstate->eqfunction =
execTuplesMatchPrepare(outerDesc,
node->numCols,
node->dupColIdx,
node->dupOperators,
&setopstate->ps);
if (node->strategy == SETOP_HASHED)
{
@ -585,9 +587,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));
}

View File

@ -494,7 +494,9 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
if (nbuckets < 1)
nbuckets = 1;
node->hashtable = BuildTupleHashTable(ncols,
node->hashtable = BuildTupleHashTable(node->parent,
node->descRight,
ncols,
node->keyColIdx,
node->tab_eq_funcs,
node->tab_hash_funcs,
@ -514,7 +516,9 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
if (nbuckets < 1)
nbuckets = 1;
}
node->hashnulls = BuildTupleHashTable(ncols,
node->hashnulls = BuildTupleHashTable(node->parent,
node->descRight,
ncols,
node->keyColIdx,
node->tab_eq_funcs,
node->tab_hash_funcs,
@ -598,6 +602,78 @@ 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?
@ -887,6 +963,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
NULL);
tupDesc = ExecTypeFromTL(righttlist, false);
sstate->descRight = tupDesc;
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projRight = ExecBuildProjectionInfo(righttlist,

View File

@ -47,7 +47,7 @@ static TupleTableSlot * /* return: a tuple or NULL */
ExecUnique(PlanState *pstate)
{
UniqueState *node = castNode(UniqueState, pstate);
Unique *plannode = (Unique *) node->ps.plan;
ExprContext *econtext = node->ps.ps_ExprContext;
TupleTableSlot *resultTupleSlot;
TupleTableSlot *slot;
PlanState *outerPlan;
@ -89,10 +89,9 @@ ExecUnique(PlanState *pstate)
* If so then we loop back and fetch another new tuple from the
* subplan.
*/
if (!execTuplesMatch(slot, resultTupleSlot,
plannode->numCols, plannode->uniqColIdx,
node->eqfunctions,
node->tempContext))
econtext->ecxt_innertuple = slot;
econtext->ecxt_outertuple = resultTupleSlot;
if (!ExecQualAndReset(node->eqfunction, econtext))
break;
}
@ -129,16 +128,9 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
uniquestate->ps.ExecProcNode = ExecUnique;
/*
* 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.
* create expression context
*/
uniquestate->tempContext =
AllocSetContextCreate(CurrentMemoryContext,
"Unique",
ALLOCSET_DEFAULT_SIZES);
ExecAssignExprContext(estate, &uniquestate->ps);
/*
* Tuple table initialization
@ -160,9 +152,12 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
/*
* Precompute fmgr lookup data for inner loop
*/
uniquestate->eqfunctions =
execTuplesMatchPrepare(node->numCols,
node->uniqOperators);
uniquestate->eqfunction =
execTuplesMatchPrepare(ExecGetResultType(outerPlanState(uniquestate)),
node->numCols,
node->uniqColIdx,
node->uniqOperators,
&uniquestate->ps);
return uniquestate;
}
@ -180,7 +175,7 @@ ExecEndUnique(UniqueState *node)
/* clean up tuple table */
ExecClearTuple(node->ps.ps_ResultTupleSlot);
MemoryContextDelete(node->tempContext);
ExecFreeExprContext(&node->ps);
ExecEndNode(outerPlanState(node));
}

View File

@ -1272,12 +1272,13 @@ 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 (!execTuplesMatch(winstate->first_part_slot,
outerslot,
node->partNumCols, node->partColIdx,
winstate->partEqfunctions,
winstate->tmpcontext->ecxt_per_tuple_memory))
if (!ExecQualAndReset(winstate->partEqfunction, econtext))
{
/*
* end of partition; copy the tuple for the next cycle.
@ -2245,6 +2246,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
wfuncno,
numaggs,
aggno;
TupleDesc scanDesc;
ListCell *l;
/* check for unsupported flags */
@ -2327,6 +2329,7 @@ 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);
@ -2351,11 +2354,20 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
/* Set up data for comparing tuples */
if (node->partNumCols > 0)
winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols,
node->partOperators);
winstate->partEqfunction =
execTuplesMatchPrepare(scanDesc,
node->partNumCols,
node->partColIdx,
node->partOperators,
&winstate->ss.ps);
if (node->ordNumCols > 0)
winstate->ordEqfunctions = execTuplesMatchPrepare(node->ordNumCols,
node->ordOperators);
winstate->ordEqfunction =
execTuplesMatchPrepare(scanDesc,
node->ordNumCols,
node->ordColIdx,
node->ordOperators,
&winstate->ss.ps);
/*
* WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
@ -2879,15 +2891,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;
return execTuplesMatch(slot1, slot2,
node->ordNumCols, node->ordColIdx,
winstate->ordEqfunctions,
winstate->tmpcontext->ecxt_per_tuple_memory);
econtext->ecxt_outertuple = slot1;
econtext->ecxt_innertuple = slot2;
return ExecQualAndReset(winstate->ordEqfunction, econtext);
}
/*

View File

@ -27,6 +27,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/tuplesort.h"
@ -54,6 +55,8 @@ 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;
@ -71,7 +74,7 @@ typedef struct OSAPerQueryState
Oid *sortCollations;
bool *sortNullsFirsts;
/* Equality operator call info, created only if needed: */
FmgrInfo *equalfns;
ExprState *compareTuple;
/* These fields are used only when accumulating datums: */
@ -1287,6 +1290,8 @@ 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;
@ -1294,12 +1299,9 @@ 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);
@ -1309,6 +1311,9 @@ 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)
@ -1323,26 +1328,22 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
*/
numDistinctCols = osastate->qstate->numSortCols - 1;
/* Look up the equality function(s), if we didn't already */
equalfns = osastate->qstate->equalfns;
if (equalfns == NULL)
/* Build tuple comparator, if we didn't already */
compareTuple = osastate->qstate->compareTuple;
if (compareTuple == NULL)
{
MemoryContext qcontext = osastate->qstate->qcontext;
AttrNumber *sortColIdx = osastate->qstate->sortColIdx;
MemoryContext oldContext;
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;
oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext);
compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc,
numDistinctCols,
sortColIdx,
osastate->qstate->eqOperators,
NULL);
MemoryContextSwitchTo(oldContext);
osastate->qstate->compareTuple = compareTuple;
}
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);
@ -1385,19 +1386,18 @@ 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 &&
execTuplesMatch(slot, slot2,
numDistinctCols,
sortColIdx,
equalfns,
tmpcontext))
ExecQualAndReset(compareTuple, econtext))
duplicate_count++;
tmpslot = slot2;
slot2 = slot;
slot = tmpslot;
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
/* avoid ExecQual() calls by reusing abbreviated keys */
abbrevOld = abbrevVal;
rank++;

View File

@ -148,6 +148,7 @@ typedef enum ExprEvalOp
/* evaluate assorted special-purpose expression types */
EEOP_IOCOERCE,
EEOP_DISTINCT,
EEOP_NOT_DISTINCT,
EEOP_NULLIF,
EEOP_SQLVALUEFUNCTION,
EEOP_CURRENTOFEXPR,

View File

@ -113,25 +113,18 @@ extern bool execCurrentOf(CurrentOfExpr *cexpr,
/*
* prototypes from functions in execGrouping.c
*/
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 ExprState *execTuplesMatchPrepare(TupleDesc desc,
int numCols,
AttrNumber *keyColIdx,
Oid *eqOperators,
PlanState *parent);
extern void execTuplesHashPrepare(int numCols,
Oid *eqOperators,
FmgrInfo **eqFunctions,
FmgrInfo **hashFunctions);
extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
extern TupleHashTable BuildTupleHashTable(PlanState *parent,
TupleDesc inputDesc,
int numCols, AttrNumber *keyColIdx,
FmgrInfo *eqfunctions,
FmgrInfo *hashfunctions,
long nbuckets, Size additionalsize,
@ -257,6 +250,11 @@ 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,

View File

@ -102,11 +102,12 @@ typedef struct AggStatePerTransData
bool *sortNullsFirst;
/*
* 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.
* 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.
*/
FmgrInfo *equalfns; /* array of length numDistinctCols */
FmgrInfo equalfnOne;
ExprState *equalfnMulti;
/*
* initial value from pg_aggregate entry
@ -270,7 +271,8 @@ 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 */
FmgrInfo *eqfunctions; /* per-grouping-field equality fns */
ExprState **eqfunctions; /* expression returning equality, indexed by
* nr of cols to compare */
Agg *aggnode; /* Agg node for phase data */
Sort *sortnode; /* Sort node for input ordering for phase */

View File

@ -635,6 +635,8 @@ 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;
@ -781,6 +783,7 @@ 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 */
@ -1795,7 +1798,7 @@ typedef struct SortState
typedef struct GroupState
{
ScanState ss; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
ExprState *eqfunction; /* equality function */
bool grp_done; /* indicates completion of Group scan */
} GroupState;
@ -1885,8 +1888,8 @@ typedef struct WindowAggState
WindowStatePerFunc perfunc; /* per-window-function information */
WindowStatePerAgg peragg; /* per-plain-aggregate information */
FmgrInfo *partEqfunctions; /* equality funcs for partition columns */
FmgrInfo *ordEqfunctions; /* equality funcs for ordering columns */
ExprState *partEqfunction; /* equality funcs for partition columns */
ExprState *ordEqfunction; /* 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 */
@ -1964,8 +1967,7 @@ typedef struct WindowAggState
typedef struct UniqueState
{
PlanState ps; /* its first field is NodeTag */
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
MemoryContext tempContext; /* short-term context for comparisons */
ExprState *eqfunction; /* tuple equality qual */
} UniqueState;
/* ----------------
@ -2079,11 +2081,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 */