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:
parent
51db0d18fb
commit
773aec7aa9
|
@ -3193,3 +3193,121 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
|
||||||
as->d.agg_strict_trans_check.jumpnull = state->steps_len;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -355,6 +355,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||||
&&CASE_EEOP_MAKE_READONLY,
|
&&CASE_EEOP_MAKE_READONLY,
|
||||||
&&CASE_EEOP_IOCOERCE,
|
&&CASE_EEOP_IOCOERCE,
|
||||||
&&CASE_EEOP_DISTINCT,
|
&&CASE_EEOP_DISTINCT,
|
||||||
|
&&CASE_EEOP_NOT_DISTINCT,
|
||||||
&&CASE_EEOP_NULLIF,
|
&&CASE_EEOP_NULLIF,
|
||||||
&&CASE_EEOP_SQLVALUEFUNCTION,
|
&&CASE_EEOP_SQLVALUEFUNCTION,
|
||||||
&&CASE_EEOP_CURRENTOFEXPR,
|
&&CASE_EEOP_CURRENTOFEXPR,
|
||||||
|
@ -1198,6 +1199,34 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
|
||||||
EEO_NEXT();
|
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)
|
EEO_CASE(EEOP_NULLIF)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -51,173 +51,34 @@ static int TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tup
|
||||||
* Utility routines for grouping tuples together
|
* 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
|
* execTuplesMatchPrepare
|
||||||
* Look up the equality functions needed for execTuplesMatch or
|
* Build expression that can be evaluated using ExecQual(), returning
|
||||||
* execTuplesUnequal, given an array of equality operator OIDs.
|
* whether an ExprContext's inner/outer tuples are NOT DISTINCT
|
||||||
*
|
|
||||||
* The result is a palloc'd array.
|
|
||||||
*/
|
*/
|
||||||
FmgrInfo *
|
ExprState *
|
||||||
execTuplesMatchPrepare(int numCols,
|
execTuplesMatchPrepare(TupleDesc desc,
|
||||||
Oid *eqOperators)
|
int numCols,
|
||||||
|
AttrNumber *keyColIdx,
|
||||||
|
Oid *eqOperators,
|
||||||
|
PlanState *parent)
|
||||||
{
|
{
|
||||||
FmgrInfo *eqFunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
|
Oid *eqFunctions = (Oid *) palloc(numCols * sizeof(Oid));
|
||||||
int i;
|
int i;
|
||||||
|
ExprState *expr;
|
||||||
|
|
||||||
|
if (numCols == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* lookup equality functions */
|
||||||
for (i = 0; i < numCols; i++)
|
for (i = 0; i < numCols; i++)
|
||||||
{
|
eqFunctions[i] = get_opcode(eqOperators[i]);
|
||||||
Oid eq_opr = eqOperators[i];
|
|
||||||
Oid eq_function;
|
|
||||||
|
|
||||||
eq_function = get_opcode(eq_opr);
|
/* build actual expression */
|
||||||
fmgr_info(eq_function, &eqFunctions[i]);
|
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.
|
* storage that will live as long as the hashtable does.
|
||||||
*/
|
*/
|
||||||
TupleHashTable
|
TupleHashTable
|
||||||
BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
|
BuildTupleHashTable(PlanState *parent,
|
||||||
|
TupleDesc inputDesc,
|
||||||
|
int numCols, AttrNumber *keyColIdx,
|
||||||
FmgrInfo *eqfunctions,
|
FmgrInfo *eqfunctions,
|
||||||
FmgrInfo *hashfunctions,
|
FmgrInfo *hashfunctions,
|
||||||
long nbuckets, Size additionalsize,
|
long nbuckets, Size additionalsize,
|
||||||
|
@ -297,6 +160,9 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
|
||||||
{
|
{
|
||||||
TupleHashTable hashtable;
|
TupleHashTable hashtable;
|
||||||
Size entrysize = sizeof(TupleHashEntryData) + additionalsize;
|
Size entrysize = sizeof(TupleHashEntryData) + additionalsize;
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
Oid *eqoids = (Oid *) palloc(numCols * sizeof(Oid));
|
||||||
|
int i;
|
||||||
|
|
||||||
Assert(nbuckets > 0);
|
Assert(nbuckets > 0);
|
||||||
|
|
||||||
|
@ -333,6 +199,26 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
|
||||||
|
|
||||||
hashtable->hashtab = tuplehash_create(tablecxt, nbuckets, hashtable);
|
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;
|
return hashtable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,22 +243,6 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
|
||||||
bool found;
|
bool found;
|
||||||
MinimalTuple key;
|
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 */
|
/* Need to run the hash functions in short-lived context */
|
||||||
oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
|
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
|
* See whether two tuples (presumably of the same hash value) match
|
||||||
*
|
*
|
||||||
* As above, the passed pointers are pointers to TupleHashEntryData.
|
* 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
|
static int
|
||||||
TupleHashTableMatch(struct tuplehash_hash *tb, const MinimalTuple tuple1, const MinimalTuple tuple2)
|
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 *slot1;
|
||||||
TupleTableSlot *slot2;
|
TupleTableSlot *slot2;
|
||||||
TupleHashTable hashtable = (TupleHashTable) tb->private_data;
|
TupleHashTable hashtable = (TupleHashTable) tb->private_data;
|
||||||
|
ExprContext *econtext = hashtable->exprcontext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We assume that simplehash.h will only ever call us with the first
|
* 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;
|
slot2 = hashtable->inputslot;
|
||||||
|
|
||||||
/* For crosstype comparisons, the inputslot must be first */
|
/* For crosstype comparisons, the inputslot must be first */
|
||||||
if (execTuplesMatch(slot2,
|
econtext->ecxt_innertuple = slot1;
|
||||||
slot1,
|
econtext->ecxt_outertuple = slot2;
|
||||||
hashtable->numCols,
|
return !ExecQualAndReset(hashtable->eq_func, econtext);
|
||||||
hashtable->keyColIdx,
|
|
||||||
hashtable->cur_eq_funcs,
|
|
||||||
hashtable->tempcxt))
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -755,7 +755,7 @@ process_ordered_aggregate_single(AggState *aggstate,
|
||||||
((oldIsNull && *isNull) ||
|
((oldIsNull && *isNull) ||
|
||||||
(!oldIsNull && !*isNull &&
|
(!oldIsNull && !*isNull &&
|
||||||
oldAbbrevVal == newAbbrevVal &&
|
oldAbbrevVal == newAbbrevVal &&
|
||||||
DatumGetBool(FunctionCall2(&pertrans->equalfns[0],
|
DatumGetBool(FunctionCall2(&pertrans->equalfnOne,
|
||||||
oldVal, *newVal)))))
|
oldVal, *newVal)))))
|
||||||
{
|
{
|
||||||
/* equal to prior, so forget this one */
|
/* equal to prior, so forget this one */
|
||||||
|
@ -802,7 +802,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
|
||||||
AggStatePerTrans pertrans,
|
AggStatePerTrans pertrans,
|
||||||
AggStatePerGroup pergroupstate)
|
AggStatePerGroup pergroupstate)
|
||||||
{
|
{
|
||||||
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
|
ExprContext *tmpcontext = aggstate->tmpcontext;
|
||||||
FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
|
FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
|
||||||
TupleTableSlot *slot1 = pertrans->sortslot;
|
TupleTableSlot *slot1 = pertrans->sortslot;
|
||||||
TupleTableSlot *slot2 = pertrans->uniqslot;
|
TupleTableSlot *slot2 = pertrans->uniqslot;
|
||||||
|
@ -811,6 +811,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
|
||||||
Datum newAbbrevVal = (Datum) 0;
|
Datum newAbbrevVal = (Datum) 0;
|
||||||
Datum oldAbbrevVal = (Datum) 0;
|
Datum oldAbbrevVal = (Datum) 0;
|
||||||
bool haveOldValue = false;
|
bool haveOldValue = false;
|
||||||
|
TupleTableSlot *save = aggstate->tmpcontext->ecxt_outertuple;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
|
tuplesort_performsort(pertrans->sortstates[aggstate->current_set]);
|
||||||
|
@ -824,22 +825,20 @@ process_ordered_aggregate_multi(AggState *aggstate,
|
||||||
{
|
{
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/*
|
tmpcontext->ecxt_outertuple = slot1;
|
||||||
* Extract the first numTransInputs columns as datums to pass to the
|
tmpcontext->ecxt_innertuple = slot2;
|
||||||
* transfn. (This will help execTuplesMatch too, so we do it
|
|
||||||
* immediately.)
|
|
||||||
*/
|
|
||||||
slot_getsomeattrs(slot1, numTransInputs);
|
|
||||||
|
|
||||||
if (numDistinctCols == 0 ||
|
if (numDistinctCols == 0 ||
|
||||||
!haveOldValue ||
|
!haveOldValue ||
|
||||||
newAbbrevVal != oldAbbrevVal ||
|
newAbbrevVal != oldAbbrevVal ||
|
||||||
!execTuplesMatch(slot1, slot2,
|
!ExecQual(pertrans->equalfnMulti, tmpcontext))
|
||||||
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 */
|
/* Load values into fcinfo */
|
||||||
/* Start from 1, since the 0th arg will be the transition value */
|
/* Start from 1, since the 0th arg will be the transition value */
|
||||||
for (i = 0; i < numTransInputs; i++)
|
for (i = 0; i < numTransInputs; i++)
|
||||||
|
@ -857,15 +856,14 @@ process_ordered_aggregate_multi(AggState *aggstate,
|
||||||
|
|
||||||
slot2 = slot1;
|
slot2 = slot1;
|
||||||
slot1 = tmpslot;
|
slot1 = tmpslot;
|
||||||
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
|
/* avoid ExecQual() calls by reusing abbreviated keys */
|
||||||
oldAbbrevVal = newAbbrevVal;
|
oldAbbrevVal = newAbbrevVal;
|
||||||
haveOldValue = true;
|
haveOldValue = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset context each time, unless execTuplesMatch did it for us */
|
/* Reset context each time */
|
||||||
if (numDistinctCols == 0)
|
ResetExprContext(tmpcontext);
|
||||||
MemoryContextReset(workcontext);
|
|
||||||
|
|
||||||
ExecClearTuple(slot1);
|
ExecClearTuple(slot1);
|
||||||
}
|
}
|
||||||
|
@ -875,6 +873,9 @@ process_ordered_aggregate_multi(AggState *aggstate,
|
||||||
|
|
||||||
tuplesort_end(pertrans->sortstates[aggstate->current_set]);
|
tuplesort_end(pertrans->sortstates[aggstate->current_set]);
|
||||||
pertrans->sortstates[aggstate->current_set] = NULL;
|
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);
|
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->hashGrpColIdxHash,
|
||||||
perhash->eqfunctions,
|
perhash->eqfunctions,
|
||||||
perhash->hashfunctions,
|
perhash->hashfunctions,
|
||||||
|
@ -1314,6 +1317,7 @@ find_hash_columns(AggState *aggstate)
|
||||||
Bitmapset *base_colnos;
|
Bitmapset *base_colnos;
|
||||||
List *outerTlist = outerPlanState(aggstate)->plan->targetlist;
|
List *outerTlist = outerPlanState(aggstate)->plan->targetlist;
|
||||||
int numHashes = aggstate->num_hashes;
|
int numHashes = aggstate->num_hashes;
|
||||||
|
EState *estate = aggstate->ss.ps.state;
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
/* Find Vars that will be needed in tlist and qual */
|
/* Find Vars that will be needed in tlist and qual */
|
||||||
|
@ -1393,6 +1397,12 @@ find_hash_columns(AggState *aggstate)
|
||||||
}
|
}
|
||||||
|
|
||||||
hashDesc = ExecTypeFromTL(hashTlist, false);
|
hashDesc = ExecTypeFromTL(hashTlist, false);
|
||||||
|
|
||||||
|
execTuplesHashPrepare(perhash->numCols,
|
||||||
|
perhash->aggnode->grpOperators,
|
||||||
|
&perhash->eqfunctions,
|
||||||
|
&perhash->hashfunctions);
|
||||||
|
perhash->hashslot = ExecAllocTableSlot(&estate->es_tupleTable);
|
||||||
ExecSetSlotDescriptor(perhash->hashslot, hashDesc);
|
ExecSetSlotDescriptor(perhash->hashslot, hashDesc);
|
||||||
|
|
||||||
list_free(hashTlist);
|
list_free(hashTlist);
|
||||||
|
@ -1694,17 +1704,14 @@ agg_retrieve_direct(AggState *aggstate)
|
||||||
* of the next grouping set
|
* of the next grouping set
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
|
tmpcontext->ecxt_innertuple = econtext->ecxt_outertuple;
|
||||||
if (aggstate->input_done ||
|
if (aggstate->input_done ||
|
||||||
(node->aggstrategy != AGG_PLAIN &&
|
(node->aggstrategy != AGG_PLAIN &&
|
||||||
aggstate->projected_set != -1 &&
|
aggstate->projected_set != -1 &&
|
||||||
aggstate->projected_set < (numGroupingSets - 1) &&
|
aggstate->projected_set < (numGroupingSets - 1) &&
|
||||||
nextSetSize > 0 &&
|
nextSetSize > 0 &&
|
||||||
!execTuplesMatch(econtext->ecxt_outertuple,
|
!ExecQualAndReset(aggstate->phase->eqfunctions[nextSetSize - 1],
|
||||||
tmpcontext->ecxt_outertuple,
|
tmpcontext)))
|
||||||
nextSetSize,
|
|
||||||
node->grpColIdx,
|
|
||||||
aggstate->phase->eqfunctions,
|
|
||||||
tmpcontext->ecxt_per_tuple_memory)))
|
|
||||||
{
|
{
|
||||||
aggstate->projected_set += 1;
|
aggstate->projected_set += 1;
|
||||||
|
|
||||||
|
@ -1847,12 +1854,9 @@ agg_retrieve_direct(AggState *aggstate)
|
||||||
*/
|
*/
|
||||||
if (node->aggstrategy != AGG_PLAIN)
|
if (node->aggstrategy != AGG_PLAIN)
|
||||||
{
|
{
|
||||||
if (!execTuplesMatch(firstSlot,
|
tmpcontext->ecxt_innertuple = firstSlot;
|
||||||
outerslot,
|
if (!ExecQual(aggstate->phase->eqfunctions[node->numCols - 1],
|
||||||
node->numCols,
|
tmpcontext))
|
||||||
node->grpColIdx,
|
|
||||||
aggstate->phase->eqfunctions,
|
|
||||||
tmpcontext->ecxt_per_tuple_memory))
|
|
||||||
{
|
{
|
||||||
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
aggstate->grp_firstTuple = ExecCopySlotTuple(outerslot);
|
||||||
break;
|
break;
|
||||||
|
@ -2078,6 +2082,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||||
AggStatePerGroup *pergroups;
|
AggStatePerGroup *pergroups;
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
|
TupleDesc scanDesc;
|
||||||
int numaggs,
|
int numaggs,
|
||||||
transno,
|
transno,
|
||||||
aggno;
|
aggno;
|
||||||
|
@ -2233,9 +2238,9 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||||
* initialize source tuple type.
|
* initialize source tuple type.
|
||||||
*/
|
*/
|
||||||
ExecAssignScanTypeFromOuterPlan(&aggstate->ss);
|
ExecAssignScanTypeFromOuterPlan(&aggstate->ss);
|
||||||
|
scanDesc = aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
|
||||||
if (node->chain)
|
if (node->chain)
|
||||||
ExecSetSlotDescriptor(aggstate->sort_slot,
|
ExecSetSlotDescriptor(aggstate->sort_slot, scanDesc);
|
||||||
aggstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize result tuple type and projection info.
|
* Initialize result tuple type and projection info.
|
||||||
|
@ -2355,11 +2360,43 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||||
*/
|
*/
|
||||||
if (aggnode->aggstrategy == AGG_SORTED)
|
if (aggnode->aggstrategy == AGG_SORTED)
|
||||||
{
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
Assert(aggnode->numCols > 0);
|
Assert(aggnode->numCols > 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a separate function for each subset of columns that
|
||||||
|
* need to be compared.
|
||||||
|
*/
|
||||||
phasedata->eqfunctions =
|
phasedata->eqfunctions =
|
||||||
execTuplesMatchPrepare(aggnode->numCols,
|
(ExprState **) palloc0(aggnode->numCols * sizeof(ExprState *));
|
||||||
aggnode->grpOperators);
|
|
||||||
|
/* 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;
|
phasedata->aggnode = aggnode;
|
||||||
|
@ -2412,16 +2449,6 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
||||||
*/
|
*/
|
||||||
if (use_hashing)
|
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 */
|
/* this is an array of pointers, not structures */
|
||||||
aggstate->hash_pergroup = pergroups;
|
aggstate->hash_pergroup = pergroups;
|
||||||
|
|
||||||
|
@ -3101,24 +3128,28 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
|
||||||
|
|
||||||
if (aggref->aggdistinct)
|
if (aggref->aggdistinct)
|
||||||
{
|
{
|
||||||
Assert(numArguments > 0);
|
Oid *ops;
|
||||||
|
|
||||||
/*
|
Assert(numArguments > 0);
|
||||||
* We need the equal function for each DISTINCT comparison we will
|
Assert(list_length(aggref->aggdistinct) == numDistinctCols);
|
||||||
* make.
|
|
||||||
*/
|
ops = palloc(numDistinctCols * sizeof(Oid));
|
||||||
pertrans->equalfns =
|
|
||||||
(FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo));
|
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
foreach(lc, aggref->aggdistinct)
|
foreach(lc, aggref->aggdistinct)
|
||||||
{
|
ops[i++] = ((SortGroupClause *) lfirst(lc))->eqop;
|
||||||
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
|
|
||||||
|
|
||||||
fmgr_info(get_opcode(sortcl->eqop), &pertrans->equalfns[i]);
|
/* lookup / build the necessary comparators */
|
||||||
i++;
|
if (numDistinctCols == 1)
|
||||||
}
|
fmgr_info(get_opcode(ops[0]), &pertrans->equalfnOne);
|
||||||
Assert(i == numDistinctCols);
|
else
|
||||||
|
pertrans->equalfnMulti =
|
||||||
|
execTuplesMatchPrepare(pertrans->sortdesc,
|
||||||
|
numDistinctCols,
|
||||||
|
pertrans->sortColIdx,
|
||||||
|
ops,
|
||||||
|
&aggstate->ss.ps);
|
||||||
|
pfree(ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
pertrans->sortstates = (Tuplesortstate **)
|
pertrans->sortstates = (Tuplesortstate **)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/nodeGroup.h"
|
#include "executor/nodeGroup.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -37,8 +38,6 @@ ExecGroup(PlanState *pstate)
|
||||||
{
|
{
|
||||||
GroupState *node = castNode(GroupState, pstate);
|
GroupState *node = castNode(GroupState, pstate);
|
||||||
ExprContext *econtext;
|
ExprContext *econtext;
|
||||||
int numCols;
|
|
||||||
AttrNumber *grpColIdx;
|
|
||||||
TupleTableSlot *firsttupleslot;
|
TupleTableSlot *firsttupleslot;
|
||||||
TupleTableSlot *outerslot;
|
TupleTableSlot *outerslot;
|
||||||
|
|
||||||
|
@ -50,8 +49,6 @@ ExecGroup(PlanState *pstate)
|
||||||
if (node->grp_done)
|
if (node->grp_done)
|
||||||
return NULL;
|
return NULL;
|
||||||
econtext = node->ss.ps.ps_ExprContext;
|
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.
|
* The ScanTupleSlot holds the (copied) first tuple of each group.
|
||||||
|
@ -59,7 +56,7 @@ ExecGroup(PlanState *pstate)
|
||||||
firsttupleslot = node->ss.ss_ScanTupleSlot;
|
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.
|
* 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
|
* Compare with first tuple and see if this tuple is of the same
|
||||||
* group. If so, ignore it and keep scanning.
|
* group. If so, ignore it and keep scanning.
|
||||||
*/
|
*/
|
||||||
if (!execTuplesMatch(firsttupleslot, outerslot,
|
econtext->ecxt_innertuple = firsttupleslot;
|
||||||
numCols, grpColIdx,
|
econtext->ecxt_outertuple = outerslot;
|
||||||
node->eqfunctions,
|
if (!ExecQualAndReset(node->eqfunction, econtext))
|
||||||
econtext->ecxt_per_tuple_memory))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +162,7 @@ GroupState *
|
||||||
ExecInitGroup(Group *node, EState *estate, int eflags)
|
ExecInitGroup(Group *node, EState *estate, int eflags)
|
||||||
{
|
{
|
||||||
GroupState *grpstate;
|
GroupState *grpstate;
|
||||||
|
AttrNumber *grpColIdx = grpColIdx = node->grpColIdx;
|
||||||
|
|
||||||
/* check for unsupported flags */
|
/* check for unsupported flags */
|
||||||
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
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
|
* Precompute fmgr lookup data for inner loop
|
||||||
*/
|
*/
|
||||||
grpstate->eqfunctions =
|
grpstate->eqfunction =
|
||||||
execTuplesMatchPrepare(node->numCols,
|
execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
|
||||||
node->grpOperators);
|
node->numCols,
|
||||||
|
grpColIdx,
|
||||||
|
node->grpOperators,
|
||||||
|
&grpstate->ss.ps);
|
||||||
|
|
||||||
return grpstate;
|
return grpstate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,14 @@ static void
|
||||||
build_hash_table(RecursiveUnionState *rustate)
|
build_hash_table(RecursiveUnionState *rustate)
|
||||||
{
|
{
|
||||||
RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
|
RecursiveUnion *node = (RecursiveUnion *) rustate->ps.plan;
|
||||||
|
TupleDesc desc = ExecGetResultType(outerPlanState(rustate));
|
||||||
|
|
||||||
Assert(node->numCols > 0);
|
Assert(node->numCols > 0);
|
||||||
Assert(node->numGroups > 0);
|
Assert(node->numGroups > 0);
|
||||||
|
|
||||||
rustate->hashtable = BuildTupleHashTable(node->numCols,
|
rustate->hashtable = BuildTupleHashTable(&rustate->ps,
|
||||||
|
desc,
|
||||||
|
node->numCols,
|
||||||
node->dupColIdx,
|
node->dupColIdx,
|
||||||
rustate->eqfunctions,
|
rustate->eqfunctions,
|
||||||
rustate->hashfunctions,
|
rustate->hashfunctions,
|
||||||
|
|
|
@ -120,18 +120,22 @@ static void
|
||||||
build_hash_table(SetOpState *setopstate)
|
build_hash_table(SetOpState *setopstate)
|
||||||
{
|
{
|
||||||
SetOp *node = (SetOp *) setopstate->ps.plan;
|
SetOp *node = (SetOp *) setopstate->ps.plan;
|
||||||
|
ExprContext *econtext = setopstate->ps.ps_ExprContext;
|
||||||
|
TupleDesc desc = ExecGetResultType(outerPlanState(setopstate));
|
||||||
|
|
||||||
Assert(node->strategy == SETOP_HASHED);
|
Assert(node->strategy == SETOP_HASHED);
|
||||||
Assert(node->numGroups > 0);
|
Assert(node->numGroups > 0);
|
||||||
|
|
||||||
setopstate->hashtable = BuildTupleHashTable(node->numCols,
|
setopstate->hashtable = BuildTupleHashTable(&setopstate->ps,
|
||||||
|
desc,
|
||||||
|
node->numCols,
|
||||||
node->dupColIdx,
|
node->dupColIdx,
|
||||||
setopstate->eqfunctions,
|
setopstate->eqfunctions,
|
||||||
setopstate->hashfunctions,
|
setopstate->hashfunctions,
|
||||||
node->numGroups,
|
node->numGroups,
|
||||||
0,
|
0,
|
||||||
setopstate->tableContext,
|
setopstate->tableContext,
|
||||||
setopstate->tempContext,
|
econtext->ecxt_per_tuple_memory,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,11 +224,11 @@ ExecSetOp(PlanState *pstate)
|
||||||
static TupleTableSlot *
|
static TupleTableSlot *
|
||||||
setop_retrieve_direct(SetOpState *setopstate)
|
setop_retrieve_direct(SetOpState *setopstate)
|
||||||
{
|
{
|
||||||
SetOp *node = (SetOp *) setopstate->ps.plan;
|
|
||||||
PlanState *outerPlan;
|
PlanState *outerPlan;
|
||||||
SetOpStatePerGroup pergroup;
|
SetOpStatePerGroup pergroup;
|
||||||
TupleTableSlot *outerslot;
|
TupleTableSlot *outerslot;
|
||||||
TupleTableSlot *resultTupleSlot;
|
TupleTableSlot *resultTupleSlot;
|
||||||
|
ExprContext *econtext = setopstate->ps.ps_ExprContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get state info from node
|
* get state info from node
|
||||||
|
@ -292,11 +296,10 @@ setop_retrieve_direct(SetOpState *setopstate)
|
||||||
/*
|
/*
|
||||||
* Check whether we've crossed a group boundary.
|
* Check whether we've crossed a group boundary.
|
||||||
*/
|
*/
|
||||||
if (!execTuplesMatch(resultTupleSlot,
|
econtext->ecxt_outertuple = resultTupleSlot;
|
||||||
outerslot,
|
econtext->ecxt_innertuple = outerslot;
|
||||||
node->numCols, node->dupColIdx,
|
|
||||||
setopstate->eqfunctions,
|
if (!ExecQualAndReset(setopstate->eqfunction, econtext))
|
||||||
setopstate->tempContext))
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Save the first input tuple of the next group.
|
* Save the first input tuple of the next group.
|
||||||
|
@ -338,6 +341,7 @@ setop_fill_hash_table(SetOpState *setopstate)
|
||||||
PlanState *outerPlan;
|
PlanState *outerPlan;
|
||||||
int firstFlag;
|
int firstFlag;
|
||||||
bool in_first_rel PG_USED_FOR_ASSERTS_ONLY;
|
bool in_first_rel PG_USED_FOR_ASSERTS_ONLY;
|
||||||
|
ExprContext *econtext = setopstate->ps.ps_ExprContext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* get state info from node
|
* get state info from node
|
||||||
|
@ -404,8 +408,8 @@ setop_fill_hash_table(SetOpState *setopstate)
|
||||||
advance_counts((SetOpStatePerGroup) entry->additional, flag);
|
advance_counts((SetOpStatePerGroup) entry->additional, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must reset temp context after each hashtable lookup */
|
/* Must reset expression context after each hashtable lookup */
|
||||||
MemoryContextReset(setopstate->tempContext);
|
ResetExprContext(econtext);
|
||||||
}
|
}
|
||||||
|
|
||||||
setopstate->table_filled = true;
|
setopstate->table_filled = true;
|
||||||
|
@ -476,6 +480,7 @@ SetOpState *
|
||||||
ExecInitSetOp(SetOp *node, EState *estate, int eflags)
|
ExecInitSetOp(SetOp *node, EState *estate, int eflags)
|
||||||
{
|
{
|
||||||
SetOpState *setopstate;
|
SetOpState *setopstate;
|
||||||
|
TupleDesc outerDesc;
|
||||||
|
|
||||||
/* check for unsupported flags */
|
/* check for unsupported flags */
|
||||||
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
|
||||||
|
@ -498,16 +503,9 @@ ExecInitSetOp(SetOp *node, EState *estate, int eflags)
|
||||||
setopstate->tableContext = NULL;
|
setopstate->tableContext = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Miscellaneous initialization
|
* create expression context
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
setopstate->tempContext =
|
ExecAssignExprContext(estate, &setopstate->ps);
|
||||||
AllocSetContextCreate(CurrentMemoryContext,
|
|
||||||
"SetOp",
|
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If hashing, we also need a longer-lived context to store the hash
|
* 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)
|
if (node->strategy == SETOP_HASHED)
|
||||||
eflags &= ~EXEC_FLAG_REWIND;
|
eflags &= ~EXEC_FLAG_REWIND;
|
||||||
outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);
|
outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate, eflags);
|
||||||
|
outerDesc = ExecGetResultType(outerPlanState(setopstate));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* setop nodes do no projections, so initialize projection info for this
|
* 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->eqfunctions,
|
||||||
&setopstate->hashfunctions);
|
&setopstate->hashfunctions);
|
||||||
else
|
else
|
||||||
setopstate->eqfunctions =
|
setopstate->eqfunction =
|
||||||
execTuplesMatchPrepare(node->numCols,
|
execTuplesMatchPrepare(outerDesc,
|
||||||
node->dupOperators);
|
node->numCols,
|
||||||
|
node->dupColIdx,
|
||||||
|
node->dupOperators,
|
||||||
|
&setopstate->ps);
|
||||||
|
|
||||||
if (node->strategy == SETOP_HASHED)
|
if (node->strategy == SETOP_HASHED)
|
||||||
{
|
{
|
||||||
|
@ -585,9 +587,9 @@ ExecEndSetOp(SetOpState *node)
|
||||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||||
|
|
||||||
/* free subsidiary stuff including hashtable */
|
/* free subsidiary stuff including hashtable */
|
||||||
MemoryContextDelete(node->tempContext);
|
|
||||||
if (node->tableContext)
|
if (node->tableContext)
|
||||||
MemoryContextDelete(node->tableContext);
|
MemoryContextDelete(node->tableContext);
|
||||||
|
ExecFreeExprContext(&node->ps);
|
||||||
|
|
||||||
ExecEndNode(outerPlanState(node));
|
ExecEndNode(outerPlanState(node));
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,7 +494,9 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||||
if (nbuckets < 1)
|
if (nbuckets < 1)
|
||||||
nbuckets = 1;
|
nbuckets = 1;
|
||||||
|
|
||||||
node->hashtable = BuildTupleHashTable(ncols,
|
node->hashtable = BuildTupleHashTable(node->parent,
|
||||||
|
node->descRight,
|
||||||
|
ncols,
|
||||||
node->keyColIdx,
|
node->keyColIdx,
|
||||||
node->tab_eq_funcs,
|
node->tab_eq_funcs,
|
||||||
node->tab_hash_funcs,
|
node->tab_hash_funcs,
|
||||||
|
@ -514,7 +516,9 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||||
if (nbuckets < 1)
|
if (nbuckets < 1)
|
||||||
nbuckets = 1;
|
nbuckets = 1;
|
||||||
}
|
}
|
||||||
node->hashnulls = BuildTupleHashTable(ncols,
|
node->hashnulls = BuildTupleHashTable(node->parent,
|
||||||
|
node->descRight,
|
||||||
|
ncols,
|
||||||
node->keyColIdx,
|
node->keyColIdx,
|
||||||
node->tab_eq_funcs,
|
node->tab_eq_funcs,
|
||||||
node->tab_hash_funcs,
|
node->tab_hash_funcs,
|
||||||
|
@ -598,6 +602,78 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
|
||||||
MemoryContextSwitchTo(oldcontext);
|
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
|
* findPartialMatch: does the hashtable contain an entry that is not
|
||||||
* provably distinct from the tuple?
|
* provably distinct from the tuple?
|
||||||
|
@ -887,6 +963,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
tupDesc = ExecTypeFromTL(righttlist, false);
|
tupDesc = ExecTypeFromTL(righttlist, false);
|
||||||
|
sstate->descRight = tupDesc;
|
||||||
slot = ExecInitExtraTupleSlot(estate);
|
slot = ExecInitExtraTupleSlot(estate);
|
||||||
ExecSetSlotDescriptor(slot, tupDesc);
|
ExecSetSlotDescriptor(slot, tupDesc);
|
||||||
sstate->projRight = ExecBuildProjectionInfo(righttlist,
|
sstate->projRight = ExecBuildProjectionInfo(righttlist,
|
||||||
|
|
|
@ -47,7 +47,7 @@ static TupleTableSlot * /* return: a tuple or NULL */
|
||||||
ExecUnique(PlanState *pstate)
|
ExecUnique(PlanState *pstate)
|
||||||
{
|
{
|
||||||
UniqueState *node = castNode(UniqueState, pstate);
|
UniqueState *node = castNode(UniqueState, pstate);
|
||||||
Unique *plannode = (Unique *) node->ps.plan;
|
ExprContext *econtext = node->ps.ps_ExprContext;
|
||||||
TupleTableSlot *resultTupleSlot;
|
TupleTableSlot *resultTupleSlot;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
PlanState *outerPlan;
|
PlanState *outerPlan;
|
||||||
|
@ -89,10 +89,9 @@ ExecUnique(PlanState *pstate)
|
||||||
* If so then we loop back and fetch another new tuple from the
|
* If so then we loop back and fetch another new tuple from the
|
||||||
* subplan.
|
* subplan.
|
||||||
*/
|
*/
|
||||||
if (!execTuplesMatch(slot, resultTupleSlot,
|
econtext->ecxt_innertuple = slot;
|
||||||
plannode->numCols, plannode->uniqColIdx,
|
econtext->ecxt_outertuple = resultTupleSlot;
|
||||||
node->eqfunctions,
|
if (!ExecQualAndReset(node->eqfunction, econtext))
|
||||||
node->tempContext))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,16 +128,9 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
|
||||||
uniquestate->ps.ExecProcNode = ExecUnique;
|
uniquestate->ps.ExecProcNode = ExecUnique;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Miscellaneous initialization
|
* create expression context
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
uniquestate->tempContext =
|
ExecAssignExprContext(estate, &uniquestate->ps);
|
||||||
AllocSetContextCreate(CurrentMemoryContext,
|
|
||||||
"Unique",
|
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tuple table initialization
|
* Tuple table initialization
|
||||||
|
@ -160,9 +152,12 @@ ExecInitUnique(Unique *node, EState *estate, int eflags)
|
||||||
/*
|
/*
|
||||||
* Precompute fmgr lookup data for inner loop
|
* Precompute fmgr lookup data for inner loop
|
||||||
*/
|
*/
|
||||||
uniquestate->eqfunctions =
|
uniquestate->eqfunction =
|
||||||
execTuplesMatchPrepare(node->numCols,
|
execTuplesMatchPrepare(ExecGetResultType(outerPlanState(uniquestate)),
|
||||||
node->uniqOperators);
|
node->numCols,
|
||||||
|
node->uniqColIdx,
|
||||||
|
node->uniqOperators,
|
||||||
|
&uniquestate->ps);
|
||||||
|
|
||||||
return uniquestate;
|
return uniquestate;
|
||||||
}
|
}
|
||||||
|
@ -180,7 +175,7 @@ ExecEndUnique(UniqueState *node)
|
||||||
/* clean up tuple table */
|
/* clean up tuple table */
|
||||||
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
||||||
|
|
||||||
MemoryContextDelete(node->tempContext);
|
ExecFreeExprContext(&node->ps);
|
||||||
|
|
||||||
ExecEndNode(outerPlanState(node));
|
ExecEndNode(outerPlanState(node));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1272,12 +1272,13 @@ spool_tuples(WindowAggState *winstate, int64 pos)
|
||||||
|
|
||||||
if (node->partNumCols > 0)
|
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 */
|
/* Check if this tuple still belongs to the current partition */
|
||||||
if (!execTuplesMatch(winstate->first_part_slot,
|
if (!ExecQualAndReset(winstate->partEqfunction, econtext))
|
||||||
outerslot,
|
|
||||||
node->partNumCols, node->partColIdx,
|
|
||||||
winstate->partEqfunctions,
|
|
||||||
winstate->tmpcontext->ecxt_per_tuple_memory))
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* end of partition; copy the tuple for the next cycle.
|
* end of partition; copy the tuple for the next cycle.
|
||||||
|
@ -2245,6 +2246,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
|
||||||
wfuncno,
|
wfuncno,
|
||||||
numaggs,
|
numaggs,
|
||||||
aggno;
|
aggno;
|
||||||
|
TupleDesc scanDesc;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
|
||||||
/* check for unsupported flags */
|
/* 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).
|
* store in the tuplestore and use in all our working slots).
|
||||||
*/
|
*/
|
||||||
ExecAssignScanTypeFromOuterPlan(&winstate->ss);
|
ExecAssignScanTypeFromOuterPlan(&winstate->ss);
|
||||||
|
scanDesc = winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
|
||||||
|
|
||||||
ExecSetSlotDescriptor(winstate->first_part_slot,
|
ExecSetSlotDescriptor(winstate->first_part_slot,
|
||||||
winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
|
winstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor);
|
||||||
|
@ -2351,11 +2354,20 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
|
||||||
|
|
||||||
/* Set up data for comparing tuples */
|
/* Set up data for comparing tuples */
|
||||||
if (node->partNumCols > 0)
|
if (node->partNumCols > 0)
|
||||||
winstate->partEqfunctions = execTuplesMatchPrepare(node->partNumCols,
|
winstate->partEqfunction =
|
||||||
node->partOperators);
|
execTuplesMatchPrepare(scanDesc,
|
||||||
|
node->partNumCols,
|
||||||
|
node->partColIdx,
|
||||||
|
node->partOperators,
|
||||||
|
&winstate->ss.ps);
|
||||||
|
|
||||||
if (node->ordNumCols > 0)
|
if (node->ordNumCols > 0)
|
||||||
winstate->ordEqfunctions = execTuplesMatchPrepare(node->ordNumCols,
|
winstate->ordEqfunction =
|
||||||
node->ordOperators);
|
execTuplesMatchPrepare(scanDesc,
|
||||||
|
node->ordNumCols,
|
||||||
|
node->ordColIdx,
|
||||||
|
node->ordOperators,
|
||||||
|
&winstate->ss.ps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
|
* WindowAgg nodes use aggvalues and aggnulls as well as Agg nodes.
|
||||||
|
@ -2879,15 +2891,15 @@ are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
|
||||||
TupleTableSlot *slot2)
|
TupleTableSlot *slot2)
|
||||||
{
|
{
|
||||||
WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
|
WindowAgg *node = (WindowAgg *) winstate->ss.ps.plan;
|
||||||
|
ExprContext *econtext = winstate->tmpcontext;
|
||||||
|
|
||||||
/* If no ORDER BY, all rows are peers with each other */
|
/* If no ORDER BY, all rows are peers with each other */
|
||||||
if (node->ordNumCols == 0)
|
if (node->ordNumCols == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return execTuplesMatch(slot1, slot2,
|
econtext->ecxt_outertuple = slot1;
|
||||||
node->ordNumCols, node->ordColIdx,
|
econtext->ecxt_innertuple = slot2;
|
||||||
winstate->ordEqfunctions,
|
return ExecQualAndReset(winstate->ordEqfunction, econtext);
|
||||||
winstate->tmpcontext->ecxt_per_tuple_memory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
#include "utils/timestamp.h"
|
#include "utils/timestamp.h"
|
||||||
#include "utils/tuplesort.h"
|
#include "utils/tuplesort.h"
|
||||||
|
|
||||||
|
@ -54,6 +55,8 @@ typedef struct OSAPerQueryState
|
||||||
Aggref *aggref;
|
Aggref *aggref;
|
||||||
/* Memory context containing this struct and other per-query data: */
|
/* Memory context containing this struct and other per-query data: */
|
||||||
MemoryContext qcontext;
|
MemoryContext qcontext;
|
||||||
|
/* Context for expression evaluation */
|
||||||
|
ExprContext *econtext;
|
||||||
/* Do we expect multiple final-function calls within one group? */
|
/* Do we expect multiple final-function calls within one group? */
|
||||||
bool rescan_needed;
|
bool rescan_needed;
|
||||||
|
|
||||||
|
@ -71,7 +74,7 @@ typedef struct OSAPerQueryState
|
||||||
Oid *sortCollations;
|
Oid *sortCollations;
|
||||||
bool *sortNullsFirsts;
|
bool *sortNullsFirsts;
|
||||||
/* Equality operator call info, created only if needed: */
|
/* Equality operator call info, created only if needed: */
|
||||||
FmgrInfo *equalfns;
|
ExprState *compareTuple;
|
||||||
|
|
||||||
/* These fields are used only when accumulating datums: */
|
/* These fields are used only when accumulating datums: */
|
||||||
|
|
||||||
|
@ -1287,6 +1290,8 @@ hypothetical_cume_dist_final(PG_FUNCTION_ARGS)
|
||||||
Datum
|
Datum
|
||||||
hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
|
ExprContext *econtext;
|
||||||
|
ExprState *compareTuple;
|
||||||
int nargs = PG_NARGS() - 1;
|
int nargs = PG_NARGS() - 1;
|
||||||
int64 rank = 1;
|
int64 rank = 1;
|
||||||
int64 duplicate_count = 0;
|
int64 duplicate_count = 0;
|
||||||
|
@ -1294,12 +1299,9 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
||||||
int numDistinctCols;
|
int numDistinctCols;
|
||||||
Datum abbrevVal = (Datum) 0;
|
Datum abbrevVal = (Datum) 0;
|
||||||
Datum abbrevOld = (Datum) 0;
|
Datum abbrevOld = (Datum) 0;
|
||||||
AttrNumber *sortColIdx;
|
|
||||||
FmgrInfo *equalfns;
|
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
TupleTableSlot *extraslot;
|
TupleTableSlot *extraslot;
|
||||||
TupleTableSlot *slot2;
|
TupleTableSlot *slot2;
|
||||||
MemoryContext tmpcontext;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
|
Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
|
||||||
|
@ -1309,6 +1311,9 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_INT64(rank);
|
PG_RETURN_INT64(rank);
|
||||||
|
|
||||||
osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
|
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 */
|
/* Adjust nargs to be the number of direct (or aggregated) args */
|
||||||
if (nargs % 2 != 0)
|
if (nargs % 2 != 0)
|
||||||
|
@ -1323,26 +1328,22 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
numDistinctCols = osastate->qstate->numSortCols - 1;
|
numDistinctCols = osastate->qstate->numSortCols - 1;
|
||||||
|
|
||||||
/* Look up the equality function(s), if we didn't already */
|
/* Build tuple comparator, if we didn't already */
|
||||||
equalfns = osastate->qstate->equalfns;
|
compareTuple = osastate->qstate->compareTuple;
|
||||||
if (equalfns == NULL)
|
if (compareTuple == NULL)
|
||||||
{
|
{
|
||||||
MemoryContext qcontext = osastate->qstate->qcontext;
|
AttrNumber *sortColIdx = osastate->qstate->sortColIdx;
|
||||||
|
MemoryContext oldContext;
|
||||||
|
|
||||||
equalfns = (FmgrInfo *)
|
oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext);
|
||||||
MemoryContextAlloc(qcontext, numDistinctCols * sizeof(FmgrInfo));
|
compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc,
|
||||||
for (i = 0; i < numDistinctCols; i++)
|
numDistinctCols,
|
||||||
{
|
sortColIdx,
|
||||||
fmgr_info_cxt(get_opcode(osastate->qstate->eqOperators[i]),
|
osastate->qstate->eqOperators,
|
||||||
&equalfns[i],
|
NULL);
|
||||||
qcontext);
|
MemoryContextSwitchTo(oldContext);
|
||||||
|
osastate->qstate->compareTuple = compareTuple;
|
||||||
}
|
}
|
||||||
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 */
|
/* because we need a hypothetical row, we can't share transition state */
|
||||||
Assert(!osastate->sort_done);
|
Assert(!osastate->sort_done);
|
||||||
|
@ -1385,19 +1386,18 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* count non-distinct tuples */
|
/* count non-distinct tuples */
|
||||||
|
econtext->ecxt_outertuple = slot;
|
||||||
|
econtext->ecxt_innertuple = slot2;
|
||||||
|
|
||||||
if (!TupIsNull(slot2) &&
|
if (!TupIsNull(slot2) &&
|
||||||
abbrevVal == abbrevOld &&
|
abbrevVal == abbrevOld &&
|
||||||
execTuplesMatch(slot, slot2,
|
ExecQualAndReset(compareTuple, econtext))
|
||||||
numDistinctCols,
|
|
||||||
sortColIdx,
|
|
||||||
equalfns,
|
|
||||||
tmpcontext))
|
|
||||||
duplicate_count++;
|
duplicate_count++;
|
||||||
|
|
||||||
tmpslot = slot2;
|
tmpslot = slot2;
|
||||||
slot2 = slot;
|
slot2 = slot;
|
||||||
slot = tmpslot;
|
slot = tmpslot;
|
||||||
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
|
/* avoid ExecQual() calls by reusing abbreviated keys */
|
||||||
abbrevOld = abbrevVal;
|
abbrevOld = abbrevVal;
|
||||||
|
|
||||||
rank++;
|
rank++;
|
||||||
|
|
|
@ -148,6 +148,7 @@ typedef enum ExprEvalOp
|
||||||
/* evaluate assorted special-purpose expression types */
|
/* evaluate assorted special-purpose expression types */
|
||||||
EEOP_IOCOERCE,
|
EEOP_IOCOERCE,
|
||||||
EEOP_DISTINCT,
|
EEOP_DISTINCT,
|
||||||
|
EEOP_NOT_DISTINCT,
|
||||||
EEOP_NULLIF,
|
EEOP_NULLIF,
|
||||||
EEOP_SQLVALUEFUNCTION,
|
EEOP_SQLVALUEFUNCTION,
|
||||||
EEOP_CURRENTOFEXPR,
|
EEOP_CURRENTOFEXPR,
|
||||||
|
|
|
@ -113,25 +113,18 @@ extern bool execCurrentOf(CurrentOfExpr *cexpr,
|
||||||
/*
|
/*
|
||||||
* prototypes from functions in execGrouping.c
|
* prototypes from functions in execGrouping.c
|
||||||
*/
|
*/
|
||||||
extern bool execTuplesMatch(TupleTableSlot *slot1,
|
extern ExprState *execTuplesMatchPrepare(TupleDesc desc,
|
||||||
TupleTableSlot *slot2,
|
|
||||||
int numCols,
|
int numCols,
|
||||||
AttrNumber *matchColIdx,
|
AttrNumber *keyColIdx,
|
||||||
FmgrInfo *eqfunctions,
|
Oid *eqOperators,
|
||||||
MemoryContext evalContext);
|
PlanState *parent);
|
||||||
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,
|
extern void execTuplesHashPrepare(int numCols,
|
||||||
Oid *eqOperators,
|
Oid *eqOperators,
|
||||||
FmgrInfo **eqFunctions,
|
FmgrInfo **eqFunctions,
|
||||||
FmgrInfo **hashFunctions);
|
FmgrInfo **hashFunctions);
|
||||||
extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
|
extern TupleHashTable BuildTupleHashTable(PlanState *parent,
|
||||||
|
TupleDesc inputDesc,
|
||||||
|
int numCols, AttrNumber *keyColIdx,
|
||||||
FmgrInfo *eqfunctions,
|
FmgrInfo *eqfunctions,
|
||||||
FmgrInfo *hashfunctions,
|
FmgrInfo *hashfunctions,
|
||||||
long nbuckets, Size additionalsize,
|
long nbuckets, Size additionalsize,
|
||||||
|
@ -257,6 +250,11 @@ extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
|
||||||
extern List *ExecInitExprList(List *nodes, PlanState *parent);
|
extern List *ExecInitExprList(List *nodes, PlanState *parent);
|
||||||
extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase,
|
extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase,
|
||||||
bool doSort, bool doHash);
|
bool doSort, bool doHash);
|
||||||
|
extern ExprState *ExecBuildGroupingEqual(TupleDesc desc,
|
||||||
|
int numCols,
|
||||||
|
AttrNumber *keyColIdx,
|
||||||
|
Oid *eqfunctions,
|
||||||
|
PlanState *parent);
|
||||||
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
|
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
TupleTableSlot *slot,
|
TupleTableSlot *slot,
|
||||||
|
|
|
@ -102,11 +102,12 @@ typedef struct AggStatePerTransData
|
||||||
bool *sortNullsFirst;
|
bool *sortNullsFirst;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fmgr lookup data for input columns' equality operators --- only
|
* Comparators for input columns --- only set/used when aggregate has
|
||||||
* set/used when aggregate has DISTINCT flag. Note that these are in
|
* DISTINCT flag. equalfnOne version is used for single-column
|
||||||
* order of sort column index, not parameter index.
|
* commparisons, equalfnMulti for the case of multiple columns.
|
||||||
*/
|
*/
|
||||||
FmgrInfo *equalfns; /* array of length numDistinctCols */
|
FmgrInfo equalfnOne;
|
||||||
|
ExprState *equalfnMulti;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* initial value from pg_aggregate entry
|
* initial value from pg_aggregate entry
|
||||||
|
@ -270,7 +271,8 @@ typedef struct AggStatePerPhaseData
|
||||||
int numsets; /* number of grouping sets (or 0) */
|
int numsets; /* number of grouping sets (or 0) */
|
||||||
int *gset_lengths; /* lengths of grouping sets */
|
int *gset_lengths; /* lengths of grouping sets */
|
||||||
Bitmapset **grouped_cols; /* column groupings for rollup */
|
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 */
|
Agg *aggnode; /* Agg node for phase data */
|
||||||
Sort *sortnode; /* Sort node for input ordering for phase */
|
Sort *sortnode; /* Sort node for input ordering for phase */
|
||||||
|
|
||||||
|
|
|
@ -635,6 +635,8 @@ typedef struct TupleHashTableData
|
||||||
FmgrInfo *in_hash_funcs; /* hash functions for input datatype(s) */
|
FmgrInfo *in_hash_funcs; /* hash functions for input datatype(s) */
|
||||||
FmgrInfo *cur_eq_funcs; /* equality functions for input vs. table */
|
FmgrInfo *cur_eq_funcs; /* equality functions for input vs. table */
|
||||||
uint32 hash_iv; /* hash-function IV */
|
uint32 hash_iv; /* hash-function IV */
|
||||||
|
ExprState *eq_func; /* tuple equality comparator */
|
||||||
|
ExprContext *exprcontext; /* expression context */
|
||||||
} TupleHashTableData;
|
} TupleHashTableData;
|
||||||
|
|
||||||
typedef tuplehash_iterator TupleHashIterator;
|
typedef tuplehash_iterator TupleHashIterator;
|
||||||
|
@ -781,6 +783,7 @@ typedef struct SubPlanState
|
||||||
HeapTuple curTuple; /* copy of most recent tuple from subplan */
|
HeapTuple curTuple; /* copy of most recent tuple from subplan */
|
||||||
Datum curArray; /* most recent array from ARRAY() subplan */
|
Datum curArray; /* most recent array from ARRAY() subplan */
|
||||||
/* these are used when hashing the subselect's output: */
|
/* these are used when hashing the subselect's output: */
|
||||||
|
TupleDesc descRight; /* subselect desc after projection */
|
||||||
ProjectionInfo *projLeft; /* for projecting lefthand exprs */
|
ProjectionInfo *projLeft; /* for projecting lefthand exprs */
|
||||||
ProjectionInfo *projRight; /* for projecting subselect output */
|
ProjectionInfo *projRight; /* for projecting subselect output */
|
||||||
TupleHashTable hashtable; /* hash table for no-nulls subselect rows */
|
TupleHashTable hashtable; /* hash table for no-nulls subselect rows */
|
||||||
|
@ -1795,7 +1798,7 @@ typedef struct SortState
|
||||||
typedef struct GroupState
|
typedef struct GroupState
|
||||||
{
|
{
|
||||||
ScanState ss; /* its first field is NodeTag */
|
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 */
|
bool grp_done; /* indicates completion of Group scan */
|
||||||
} GroupState;
|
} GroupState;
|
||||||
|
|
||||||
|
@ -1885,8 +1888,8 @@ typedef struct WindowAggState
|
||||||
|
|
||||||
WindowStatePerFunc perfunc; /* per-window-function information */
|
WindowStatePerFunc perfunc; /* per-window-function information */
|
||||||
WindowStatePerAgg peragg; /* per-plain-aggregate information */
|
WindowStatePerAgg peragg; /* per-plain-aggregate information */
|
||||||
FmgrInfo *partEqfunctions; /* equality funcs for partition columns */
|
ExprState *partEqfunction; /* equality funcs for partition columns */
|
||||||
FmgrInfo *ordEqfunctions; /* equality funcs for ordering columns */
|
ExprState *ordEqfunction; /* equality funcs for ordering columns */
|
||||||
Tuplestorestate *buffer; /* stores rows of current partition */
|
Tuplestorestate *buffer; /* stores rows of current partition */
|
||||||
int current_ptr; /* read pointer # for current row */
|
int current_ptr; /* read pointer # for current row */
|
||||||
int framehead_ptr; /* read pointer # for frame head, if used */
|
int framehead_ptr; /* read pointer # for frame head, if used */
|
||||||
|
@ -1964,8 +1967,7 @@ typedef struct WindowAggState
|
||||||
typedef struct UniqueState
|
typedef struct UniqueState
|
||||||
{
|
{
|
||||||
PlanState ps; /* its first field is NodeTag */
|
PlanState ps; /* its first field is NodeTag */
|
||||||
FmgrInfo *eqfunctions; /* per-field lookup data for equality fns */
|
ExprState *eqfunction; /* tuple equality qual */
|
||||||
MemoryContext tempContext; /* short-term context for comparisons */
|
|
||||||
} UniqueState;
|
} UniqueState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -2079,11 +2081,11 @@ typedef struct SetOpStatePerGroupData *SetOpStatePerGroup;
|
||||||
typedef struct SetOpState
|
typedef struct SetOpState
|
||||||
{
|
{
|
||||||
PlanState ps; /* its first field is NodeTag */
|
PlanState ps; /* its first field is NodeTag */
|
||||||
|
ExprState *eqfunction; /* equality comparator */
|
||||||
FmgrInfo *eqfunctions; /* per-grouping-field equality fns */
|
FmgrInfo *eqfunctions; /* per-grouping-field equality fns */
|
||||||
FmgrInfo *hashfunctions; /* per-grouping-field hash fns */
|
FmgrInfo *hashfunctions; /* per-grouping-field hash fns */
|
||||||
bool setop_done; /* indicates completion of output scan */
|
bool setop_done; /* indicates completion of output scan */
|
||||||
long numOutput; /* number of dups left to output */
|
long numOutput; /* number of dups left to output */
|
||||||
MemoryContext tempContext; /* short-term context for comparisons */
|
|
||||||
/* these fields are used in SETOP_SORTED mode: */
|
/* these fields are used in SETOP_SORTED mode: */
|
||||||
SetOpStatePerGroup pergroup; /* per-group working state */
|
SetOpStatePerGroup pergroup; /* per-group working state */
|
||||||
HeapTuple grp_firstTuple; /* copy of first tuple of current group */
|
HeapTuple grp_firstTuple; /* copy of first tuple of current group */
|
||||||
|
|
Loading…
Reference in New Issue