mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +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