1
0
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:
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

@ -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);
}