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;
-}
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
&&CASE_EEOP_DISTINCT,
- &&CASE_EEOP_NOT_DISTINCT,
&&CASE_EEOP_NULLIF,
&&CASE_EEOP_SQLVALUEFUNCTION,
&&CASE_EEOP_CURRENTOFEXPR,
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)
{
/*
*****************************************************************************/
/*
- * 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!
+ */
+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.
*/
-ExprState *
-execTuplesMatchPrepare(TupleDesc desc,
- int numCols,
- AttrNumber *keyColIdx,
- Oid *eqOperators,
- PlanState *parent)
+bool
+execTuplesUnequal(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 = 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;
}
/*
* 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,
{
TupleHashTable hashtable;
Size entrysize = sizeof(TupleHashEntryData) + additionalsize;
- MemoryContext oldcontext;
- Oid *eqoids = (Oid *) palloc(numCols * sizeof(Oid));
- int i;
Assert(nbuckets > 0);
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;
}
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);
* 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)
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
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;
}
((oldIsNull && *isNull) ||
(!oldIsNull && !*isNull &&
oldAbbrevVal == newAbbrevVal &&
- DatumGetBool(FunctionCall2(&pertrans->equalfnOne,
+ DatumGetBool(FunctionCall2(&pertrans->equalfns[0],
oldVal, *newVal)))))
{
/* equal to prior, so forget this one */
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;
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]);
{
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++)
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);
}
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;
}
/*
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,
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 */
}
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);
* 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;
*/
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;
AggStatePerGroup *pergroups;
Plan *outerPlan;
ExprContext *econtext;
- TupleDesc scanDesc;
int numaggs,
transno,
aggno;
* 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.
*/
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;
*/
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;
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 **)
#include "executor/executor.h"
#include "executor/nodeGroup.h"
#include "miscadmin.h"
-#include "utils/memutils.h"
/*
{
GroupState *node = castNode(GroupState, pstate);
ExprContext *econtext;
+ int numCols;
+ AttrNumber *grpColIdx;
TupleTableSlot *firsttupleslot;
TupleTableSlot *outerslot;
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.
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.
*/
* 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;
}
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)));
/*
* 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;
}
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,
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);
}
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
/*
* 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.
PlanState *outerPlan;
int firstFlag;
bool in_first_rel PG_USED_FOR_ASSERTS_ONLY;
- ExprContext *econtext = setopstate->ps.ps_ExprContext;
/*
* get state info from node
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;
ExecInitSetOp(SetOp *node, EState *estate, int eflags)
{
SetOpState *setopstate;
- TupleDesc outerDesc;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
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
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
&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)
{
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));
}
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,
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,
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?
NULL);
tupDesc = ExecTypeFromTL(righttlist, false);
- sstate->descRight = tupDesc;
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projRight = ExecBuildProjectionInfo(righttlist,
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;
* 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;
}
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
/*
* 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;
}
/* clean up tuple table */
ExecClearTuple(node->ps.ps_ResultTupleSlot);
- ExecFreeExprContext(&node->ps);
+ MemoryContextDelete(node->tempContext);
ExecEndNode(outerPlanState(node));
}
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.
wfuncno,
numaggs,
aggno;
- TupleDesc scanDesc;
ListCell *l;
/* check for unsupported flags */
* 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);
/* 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.
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);
}
/*
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
-#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/tuplesort.h"
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;
Oid *sortCollations;
bool *sortNullsFirsts;
/* Equality operator call info, created only if needed: */
- ExprState *compareTuple;
+ FmgrInfo *equalfns;
/* These fields are used only when accumulating datums: */
Datum
hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
{
- ExprContext *econtext;
- ExprState *compareTuple;
int nargs = PG_NARGS() - 1;
int64 rank = 1;
int64 duplicate_count = 0;
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);
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)
*/
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;
-
- oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext);
- compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc,
- numDistinctCols,
- sortColIdx,
- osastate->qstate->eqOperators,
- NULL);
- MemoryContextSwitchTo(oldContext);
- osastate->qstate->compareTuple = compareTuple;
+ MemoryContext qcontext = osastate->qstate->qcontext;
+
+ 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);
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++;
/* evaluate assorted special-purpose expression types */
EEOP_IOCOERCE,
EEOP_DISTINCT,
- EEOP_NOT_DISTINCT,
EEOP_NULLIF,
EEOP_SQLVALUEFUNCTION,
EEOP_CURRENTOFEXPR,
/*
* 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,
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,
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
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 */
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;
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 */
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;
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 */
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;
/* ----------------
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 */