mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
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:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user