/*
* Generic support for ordered-set aggregates
+ *
+ * The state for an ordered-set aggregate is divided into a per-group struct
+ * (which is the internal-type transition state datum returned to nodeAgg.c)
+ * and a per-query struct, which contains data and sub-objects that we can
+ * create just once per query because they will not change across groups.
+ * The per-query struct and subsidiary data live in the executor's per-query
+ * memory context, and go away implicitly at ExecutorEnd().
*/
-typedef struct OrderedSetAggState
+typedef struct OSAPerQueryState
{
/* Aggref for this aggregate: */
Aggref *aggref;
- /* Sort object we're accumulating data in: */
- Tuplesortstate *sortstate;
- /* Number of normal rows inserted into sortstate: */
- int64 number_of_rows;
+ /* Memory context containing this struct and other per-query data: */
+ MemoryContext qcontext;
+ /* Memory context containing per-group data: */
+ MemoryContext gcontext;
+ /* Agg plan node's output econtext: */
+ ExprContext *peraggecontext;
/* These fields are used only when accumulating tuples: */
TupleDesc tupdesc;
/* Tuple slot we can use for inserting/extracting tuples: */
TupleTableSlot *tupslot;
+ /* Per-sort-column sorting information */
+ int numSortCols;
+ AttrNumber *sortColIdx;
+ Oid *sortOperators;
+ Oid *eqOperators;
+ Oid *sortCollations;
+ bool *sortNullsFirsts;
+ /* Equality operator call info, created only if needed: */
+ FmgrInfo *equalfns;
/* These fields are used only when accumulating datums: */
/* Info about datatype of datums being sorted: */
- Oid datumtype;
+ Oid sortColType;
int16 typLen;
bool typByVal;
char typAlign;
- /* Info about equality operator associated with sort operator: */
+ /* Info about sort ordering: */
+ Oid sortOperator;
Oid eqOperator;
-} OrderedSetAggState;
+ Oid sortCollation;
+ bool sortNullsFirst;
+ /* Equality operator call info, created only if needed: */
+ FmgrInfo equalfn;
+} OSAPerQueryState;
+
+typedef struct OSAPerGroupState
+{
+ /* Link to the per-query state for this aggregate: */
+ OSAPerQueryState *qstate;
+ /* Sort object we're accumulating data in: */
+ Tuplesortstate *sortstate;
+ /* Number of normal rows inserted into sortstate: */
+ int64 number_of_rows;
+} OSAPerGroupState;
static void ordered_set_shutdown(Datum arg);
/*
* Set up working state for an ordered-set aggregate
*/
-static OrderedSetAggState *
+static OSAPerGroupState *
ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
{
- OrderedSetAggState *osastate;
- Aggref *aggref;
- ExprContext *peraggecontext;
- MemoryContext aggcontext;
+ OSAPerGroupState *osastate;
+ OSAPerQueryState *qstate;
MemoryContext oldcontext;
- List *sortlist;
- int numSortCols;
- /* Must be called as aggregate; get the Agg node's query-lifespan context */
- if (AggCheckCallContext(fcinfo, &aggcontext) != AGG_CONTEXT_AGGREGATE)
- elog(ERROR, "ordered-set aggregate called in non-aggregate context");
- /* Need the Aggref as well */
- aggref = AggGetAggref(fcinfo);
- if (!aggref)
- elog(ERROR, "ordered-set aggregate called in non-aggregate context");
- if (!AGGKIND_IS_ORDERED_SET(aggref->aggkind))
- elog(ERROR, "ordered-set aggregate support function called for non-ordered-set aggregate");
- /* Also get output exprcontext so we can register shutdown callback */
- peraggecontext = AggGetPerAggEContext(fcinfo);
- if (!peraggecontext)
- elog(ERROR, "ordered-set aggregate called in non-aggregate context");
+ /*
+ * We keep a link to the per-query state in fn_extra; if it's not there,
+ * create it, and do the per-query setup we need.
+ */
+ qstate = (OSAPerQueryState *) fcinfo->flinfo->fn_extra;
+ if (qstate == NULL)
+ {
+ Aggref *aggref;
+ MemoryContext qcontext;
+ MemoryContext gcontext;
+ ExprContext *peraggecontext;
+ List *sortlist;
+ int numSortCols;
- /* Initialize working-state object in the aggregate-lifespan context */
- osastate = (OrderedSetAggState *)
- MemoryContextAllocZero(aggcontext, sizeof(OrderedSetAggState));
- osastate->aggref = aggref;
+ /*
+ * Check we're called as aggregate, and get the Agg node's
+ * group-lifespan context
+ */
+ if (AggCheckCallContext(fcinfo, &gcontext) != AGG_CONTEXT_AGGREGATE)
+ elog(ERROR, "ordered-set aggregate called in non-aggregate context");
+ /* Need the Aggref as well */
+ aggref = AggGetAggref(fcinfo);
+ if (!aggref)
+ elog(ERROR, "ordered-set aggregate called in non-aggregate context");
+ if (!AGGKIND_IS_ORDERED_SET(aggref->aggkind))
+ elog(ERROR, "ordered-set aggregate support function called for non-ordered-set aggregate");
+ /* Also get output exprcontext so we can register shutdown callback */
+ peraggecontext = AggGetPerAggEContext(fcinfo);
+ if (!peraggecontext)
+ elog(ERROR, "ordered-set aggregate called in non-aggregate context");
- /* Extract the sort information */
- sortlist = aggref->aggorder;
- numSortCols = list_length(sortlist);
+ /*
+ * Prepare per-query structures in the fn_mcxt, which we assume is the
+ * executor's per-query context; in any case it's the right place to
+ * keep anything found via fn_extra.
+ */
+ qcontext = fcinfo->flinfo->fn_mcxt;
+ oldcontext = MemoryContextSwitchTo(qcontext);
- if (use_tuples)
- {
- bool ishypothetical = (aggref->aggkind == AGGKIND_HYPOTHETICAL);
- AttrNumber *sortColIdx;
- Oid *sortOperators;
- Oid *sortCollations;
- bool *sortNullsFirst;
- ListCell *lc;
- int i;
-
- if (ishypothetical)
- numSortCols++; /* make space for flag column */
- /* these arrays are made in short-lived context */
- sortColIdx = (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
- sortOperators = (Oid *) palloc(numSortCols * sizeof(Oid));
- sortCollations = (Oid *) palloc(numSortCols * sizeof(Oid));
- sortNullsFirst = (bool *) palloc(numSortCols * sizeof(bool));
-
- i = 0;
- foreach(lc, sortlist)
+ qstate = (OSAPerQueryState *) palloc0(sizeof(OSAPerQueryState));
+ qstate->aggref = aggref;
+ qstate->qcontext = qcontext;
+ qstate->gcontext = gcontext;
+ qstate->peraggecontext = peraggecontext;
+
+ /* Extract the sort information */
+ sortlist = aggref->aggorder;
+ numSortCols = list_length(sortlist);
+
+ if (use_tuples)
{
- SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
- TargetEntry *tle = get_sortgroupclause_tle(sortcl, aggref->args);
+ bool ishypothetical = (aggref->aggkind == AGGKIND_HYPOTHETICAL);
+ AttrNumber *sortColIdx;
+ Oid *sortOperators;
+ Oid *eqOperators;
+ Oid *sortCollations;
+ bool *sortNullsFirsts;
+ ListCell *lc;
+ int i;
+
+ if (ishypothetical)
+ numSortCols++; /* make space for flag column */
+ qstate->numSortCols = numSortCols;
+ qstate->sortColIdx = sortColIdx =
+ (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
+ qstate->sortOperators = sortOperators =
+ (Oid *) palloc(numSortCols * sizeof(Oid));
+ qstate->eqOperators = eqOperators =
+ (Oid *) palloc(numSortCols * sizeof(Oid));
+ qstate->sortCollations = sortCollations =
+ (Oid *) palloc(numSortCols * sizeof(Oid));
+ qstate->sortNullsFirsts = sortNullsFirsts =
+ (bool *) palloc(numSortCols * sizeof(bool));
+
+ i = 0;
+ foreach(lc, sortlist)
+ {
+ SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
+ TargetEntry *tle = get_sortgroupclause_tle(sortcl,
+ aggref->args);
+
+ /* the parser should have made sure of this */
+ Assert(OidIsValid(sortcl->sortop));
+
+ sortColIdx[i] = tle->resno;
+ sortOperators[i] = sortcl->sortop;
+ eqOperators[i] = sortcl->eqop;
+ sortCollations[i] = exprCollation((Node *) tle->expr);
+ sortNullsFirsts[i] = sortcl->nulls_first;
+ i++;
+ }
- /* the parser should have made sure of this */
- Assert(OidIsValid(sortcl->sortop));
+ if (ishypothetical)
+ {
+ /* Add an integer flag column as the last sort column */
+ sortColIdx[i] = list_length(aggref->args) + 1;
+ sortOperators[i] = Int4LessOperator;
+ eqOperators[i] = Int4EqualOperator;
+ sortCollations[i] = InvalidOid;
+ sortNullsFirsts[i] = false;
+ i++;
+ }
- sortColIdx[i] = tle->resno;
- sortOperators[i] = sortcl->sortop;
- sortCollations[i] = exprCollation((Node *) tle->expr);
- sortNullsFirst[i] = sortcl->nulls_first;
- i++;
- }
+ Assert(i == numSortCols);
- if (ishypothetical)
- {
- /* Add an integer flag column as the last sort column */
- sortColIdx[i] = list_length(aggref->args) + 1;
- sortOperators[i] = Int4LessOperator;
- sortCollations[i] = InvalidOid;
- sortNullsFirst[i] = false;
- i++;
+ /*
+ * Get a tupledesc corresponding to the aggregated inputs
+ * (including sort expressions) of the agg.
+ */
+ qstate->tupdesc = ExecTypeFromTL(aggref->args, false);
+
+ /* If we need a flag column, hack the tupledesc to include that */
+ if (ishypothetical)
+ {
+ TupleDesc newdesc;
+ int natts = qstate->tupdesc->natts;
+
+ newdesc = CreateTemplateTupleDesc(natts + 1, false);
+ for (i = 1; i <= natts; i++)
+ TupleDescCopyEntry(newdesc, i, qstate->tupdesc, i);
+
+ TupleDescInitEntry(newdesc,
+ (AttrNumber) ++natts,
+ "flag",
+ INT4OID,
+ -1,
+ 0);
+
+ FreeTupleDesc(qstate->tupdesc);
+ qstate->tupdesc = newdesc;
+ }
+
+ /* Create slot we'll use to store/retrieve rows */
+ qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc);
}
+ else
+ {
+ /* Sort single datums */
+ SortGroupClause *sortcl;
+ TargetEntry *tle;
- Assert(i == numSortCols);
+ if (numSortCols != 1 || aggref->aggkind == AGGKIND_HYPOTHETICAL)
+ elog(ERROR, "ordered-set aggregate support function does not support multiple aggregated columns");
- /* Now build the stuff we need in aggregate-lifespan context */
- oldcontext = MemoryContextSwitchTo(aggcontext);
+ sortcl = (SortGroupClause *) linitial(sortlist);
+ tle = get_sortgroupclause_tle(sortcl, aggref->args);
- /*
- * Get a tupledesc corresponding to the aggregated inputs (including
- * sort expressions) of the agg.
- */
- osastate->tupdesc = ExecTypeFromTL(aggref->args, false);
+ /* the parser should have made sure of this */
+ Assert(OidIsValid(sortcl->sortop));
- /* If we need a flag column, hack the tupledesc to include that */
- if (ishypothetical)
- {
- TupleDesc newdesc;
- int natts = osastate->tupdesc->natts;
-
- newdesc = CreateTemplateTupleDesc(natts + 1, false);
- for (i = 1; i <= natts; i++)
- TupleDescCopyEntry(newdesc, i, osastate->tupdesc, i);
-
- TupleDescInitEntry(newdesc,
- (AttrNumber) ++natts,
- "flag",
- INT4OID,
- -1,
- 0);
-
- FreeTupleDesc(osastate->tupdesc);
- osastate->tupdesc = newdesc;
+ /* Save sort ordering info */
+ qstate->sortColType = exprType((Node *) tle->expr);
+ qstate->sortOperator = sortcl->sortop;
+ qstate->eqOperator = sortcl->eqop;
+ qstate->sortCollation = exprCollation((Node *) tle->expr);
+ qstate->sortNullsFirst = sortcl->nulls_first;
+
+ /* Save datatype info */
+ get_typlenbyvalalign(qstate->sortColType,
+ &qstate->typLen,
+ &qstate->typByVal,
+ &qstate->typAlign);
}
- /* Initialize tuplesort object */
- osastate->sortstate = tuplesort_begin_heap(osastate->tupdesc,
- numSortCols,
- sortColIdx,
- sortOperators,
- sortCollations,
- sortNullsFirst,
- work_mem, false);
+ fcinfo->flinfo->fn_extra = (void *) qstate;
- /* Create slot we'll use to store/retrieve rows */
- osastate->tupslot = MakeSingleTupleTableSlot(osastate->tupdesc);
+ MemoryContextSwitchTo(oldcontext);
}
+
+ /* Now build the stuff we need in group-lifespan context */
+ oldcontext = MemoryContextSwitchTo(qstate->gcontext);
+
+ osastate = (OSAPerGroupState *) palloc(sizeof(OSAPerGroupState));
+ osastate->qstate = qstate;
+
+ /* Initialize tuplesort object */
+ if (use_tuples)
+ osastate->sortstate = tuplesort_begin_heap(qstate->tupdesc,
+ qstate->numSortCols,
+ qstate->sortColIdx,
+ qstate->sortOperators,
+ qstate->sortCollations,
+ qstate->sortNullsFirsts,
+ work_mem, false);
else
- {
- /* Sort single datums */
- SortGroupClause *sortcl;
- TargetEntry *tle;
- Oid sortColType;
- Oid sortOperator;
- Oid eqOperator;
- Oid sortCollation;
- bool sortNullsFirst;
-
- if (numSortCols != 1 || aggref->aggkind == AGGKIND_HYPOTHETICAL)
- elog(ERROR, "ordered-set aggregate support function does not support multiple aggregated columns");
-
- sortcl = (SortGroupClause *) linitial(sortlist);
- tle = get_sortgroupclause_tle(sortcl, aggref->args);
-
- /* the parser should have made sure of this */
- Assert(OidIsValid(sortcl->sortop));
-
- sortColType = exprType((Node *) tle->expr);
- sortOperator = sortcl->sortop;
- eqOperator = sortcl->eqop;
- sortCollation = exprCollation((Node *) tle->expr);
- sortNullsFirst = sortcl->nulls_first;
-
- /* Save datatype info */
- osastate->datumtype = sortColType;
- get_typlenbyvalalign(sortColType,
- &osastate->typLen,
- &osastate->typByVal,
- &osastate->typAlign);
- osastate->eqOperator = eqOperator;
-
- /* Now build the stuff we need in aggregate-lifespan context */
- oldcontext = MemoryContextSwitchTo(aggcontext);
-
- /* Initialize tuplesort object */
- osastate->sortstate = tuplesort_begin_datum(sortColType,
- sortOperator,
- sortCollation,
- sortNullsFirst,
+ osastate->sortstate = tuplesort_begin_datum(qstate->sortColType,
+ qstate->sortOperator,
+ qstate->sortCollation,
+ qstate->sortNullsFirst,
work_mem, false);
- }
- /* Now register a shutdown callback to clean it all up */
- RegisterExprContextCallback(peraggecontext,
+ osastate->number_of_rows = 0;
+
+ /* Now register a shutdown callback to clean things up */
+ RegisterExprContextCallback(qstate->peraggecontext,
ordered_set_shutdown,
PointerGetDatum(osastate));
/*
* Clean up when evaluation of an ordered-set aggregate is complete.
*
- * We don't need to bother freeing objects in the aggcontext memory context,
- * since that will get reset anyway by nodeAgg.c, but we should take care to
- * release any potential non-memory resources.
+ * We don't need to bother freeing objects in the per-group memory context,
+ * since that will get reset anyway by nodeAgg.c; nor should we free anything
+ * in the per-query context, which will get cleared (if this was the last
+ * group) by ExecutorEnd. But we must take care to release any potential
+ * non-memory resources.
*
* This callback is arguably unnecessary, since we don't support use of
* ordered-set aggs in AGG_HASHED mode and there is currently no non-error
static void
ordered_set_shutdown(Datum arg)
{
- OrderedSetAggState *osastate = (OrderedSetAggState *) DatumGetPointer(arg);
+ OSAPerGroupState *osastate = (OSAPerGroupState *) DatumGetPointer(arg);
/* Tuplesort object might have temp files. */
if (osastate->sortstate)
tuplesort_end(osastate->sortstate);
osastate->sortstate = NULL;
/* The tupleslot probably can't be holding a pin, but let's be safe. */
- if (osastate->tupslot)
- ExecDropSingleTupleTableSlot(osastate->tupslot);
- osastate->tupslot = NULL;
+ if (osastate->qstate->tupslot)
+ ExecClearTuple(osastate->qstate->tupslot);
}
Datum
ordered_set_transition(PG_FUNCTION_ARGS)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
/* If first call, create the transition state workspace */
if (PG_ARGISNULL(0))
/* safety check */
if (AggCheckCallContext(fcinfo, NULL) != AGG_CONTEXT_AGGREGATE)
elog(ERROR, "ordered-set aggregate called in non-aggregate context");
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
}
/* Load the datum into the tuplesort object, but only if it's not null */
Datum
ordered_set_transition_multi(PG_FUNCTION_ARGS)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
TupleTableSlot *slot;
int nargs;
int i;
/* safety check */
if (AggCheckCallContext(fcinfo, NULL) != AGG_CONTEXT_AGGREGATE)
elog(ERROR, "ordered-set aggregate called in non-aggregate context");
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
}
/* Form a tuple from all the other inputs besides the transition value */
- slot = osastate->tupslot;
+ slot = osastate->qstate->tupslot;
ExecClearTuple(slot);
nargs = PG_NARGS() - 1;
for (i = 0; i < nargs; i++)
slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
}
- if (osastate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
+ if (osastate->qstate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
{
/* Add a zero flag value to mark this row as a normal input row */
slot->tts_values[i] = Int32GetDatum(0);
Datum
percentile_disc_final(PG_FUNCTION_ARGS)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
double percentile;
Datum val;
bool isnull;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* number_of_rows could be zero if we only saw NULL input values */
if (osastate->number_of_rows == 0)
Oid expect_type,
LerpFunc lerpfunc)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
double percentile;
int64 first_row = 0;
int64 second_row = 0;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* number_of_rows could be zero if we only saw NULL input values */
if (osastate->number_of_rows == 0)
PG_RETURN_NULL();
- Assert(expect_type == osastate->datumtype);
+ Assert(expect_type == osastate->qstate->sortColType);
/* Finish the sort */
tuplesort_performsort(osastate->sortstate);
Datum
percentile_disc_multi_final(PG_FUNCTION_ARGS)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
ArrayType *param;
Datum *percentiles_datum;
bool *percentiles_null;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* number_of_rows could be zero if we only saw NULL input values */
if (osastate->number_of_rows == 0)
&num_percentiles);
if (num_percentiles == 0)
- PG_RETURN_POINTER(construct_empty_array(osastate->datumtype));
+ PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
pct_info = setup_pct_info(num_percentiles,
percentiles_datum,
ARR_NDIM(param),
ARR_DIMS(param),
ARR_LBOUND(param),
- osastate->datumtype,
- osastate->typLen,
- osastate->typByVal,
- osastate->typAlign));
+ osastate->qstate->sortColType,
+ osastate->qstate->typLen,
+ osastate->qstate->typByVal,
+ osastate->qstate->typAlign));
}
/*
int16 typLen, bool typByVal, char typAlign,
LerpFunc lerpfunc)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
ArrayType *param;
Datum *percentiles_datum;
bool *percentiles_null;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* number_of_rows could be zero if we only saw NULL input values */
if (osastate->number_of_rows == 0)
PG_RETURN_NULL();
- Assert(expect_type == osastate->datumtype);
+ Assert(expect_type == osastate->qstate->sortColType);
/* Deconstruct the percentile-array input */
if (PG_ARGISNULL(1))
&num_percentiles);
if (num_percentiles == 0)
- PG_RETURN_POINTER(construct_empty_array(osastate->datumtype));
+ PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
pct_info = setup_pct_info(num_percentiles,
percentiles_datum,
Datum
mode_final(PG_FUNCTION_ARGS)
{
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
Datum val;
bool isnull;
Datum mode_val = (Datum) 0;
Datum last_val = (Datum) 0;
int64 last_val_freq = 0;
bool last_val_is_mode = false;
- FmgrInfo equalfn;
+ FmgrInfo *equalfn;
bool shouldfree;
/* safety check */
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* number_of_rows could be zero if we only saw NULL input values */
if (osastate->number_of_rows == 0)
PG_RETURN_NULL();
- /* Look up the equality function for the datatype */
- fmgr_info(get_opcode(osastate->eqOperator), &equalfn);
+ /* Look up the equality function for the datatype, if we didn't already */
+ equalfn = &(osastate->qstate->equalfn);
+ if (!OidIsValid(equalfn->fn_oid))
+ fmgr_info_cxt(get_opcode(osastate->qstate->eqOperator), equalfn,
+ osastate->qstate->qcontext);
- shouldfree = !(osastate->typByVal);
+ shouldfree = !(osastate->qstate->typByVal);
/* Finish the sort */
tuplesort_performsort(osastate->sortstate);
mode_freq = last_val_freq = 1;
last_val_is_mode = true;
}
- else if (DatumGetBool(FunctionCall2(&equalfn, val, last_val)))
+ else if (DatumGetBool(FunctionCall2(equalfn, val, last_val)))
{
/* value equal to previous value, count it */
if (last_val_is_mode)
{
int nargs = PG_NARGS() - 1;
int64 rank = 1;
- OrderedSetAggState *osastate;
+ OSAPerGroupState *osastate;
TupleTableSlot *slot;
int i;
return 1;
}
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
*number_of_rows = osastate->number_of_rows;
/* Adjust nargs to be the number of direct (or aggregated) args */
elog(ERROR, "wrong number of arguments in hypothetical-set function");
nargs /= 2;
- hypothetical_check_argtypes(fcinfo, nargs, osastate->tupdesc);
+ hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
/* insert the hypothetical row into the sort */
- slot = osastate->tupslot;
+ slot = osastate->qstate->tupslot;
ExecClearTuple(slot);
for (i = 0; i < nargs; i++)
{
int nargs = PG_NARGS() - 1;
int64 rank = 1;
int64 duplicate_count = 0;
- OrderedSetAggState *osastate;
- List *sortlist;
+ OSAPerGroupState *osastate;
int numDistinctCols;
AttrNumber *sortColIdx;
FmgrInfo *equalfns;
TupleTableSlot *extraslot;
TupleTableSlot *slot2;
MemoryContext tmpcontext;
- ListCell *lc;
int i;
/* safety check */
if (PG_ARGISNULL(0))
PG_RETURN_INT64(rank);
- osastate = (OrderedSetAggState *) PG_GETARG_POINTER(0);
+ osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
/* Adjust nargs to be the number of direct (or aggregated) args */
if (nargs % 2 != 0)
elog(ERROR, "wrong number of arguments in hypothetical-set function");
nargs /= 2;
- hypothetical_check_argtypes(fcinfo, nargs, osastate->tupdesc);
+ hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
/*
- * Construct list of columns to compare for uniqueness. We can omit the
- * flag column since we will only compare rows with flag == 0.
+ * When comparing tuples, we can omit the flag column since we will only
+ * compare rows with flag == 0.
*/
- sortlist = osastate->aggref->aggorder;
- numDistinctCols = list_length(sortlist);
- sortColIdx = (AttrNumber *) palloc(numDistinctCols * sizeof(AttrNumber));
- equalfns = (FmgrInfo *) palloc(numDistinctCols * sizeof(FmgrInfo));
+ numDistinctCols = osastate->qstate->numSortCols - 1;
- i = 0;
- foreach(lc, sortlist)
+ /* Look up the equality function(s), if we didn't already */
+ equalfns = osastate->qstate->equalfns;
+ if (equalfns == NULL)
{
- SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
- TargetEntry *tle = get_sortgroupclause_tle(sortcl,
- osastate->aggref->args);
+ MemoryContext qcontext = osastate->qstate->qcontext;
- sortColIdx[i] = tle->resno;
- fmgr_info(get_opcode(sortcl->eqop), &equalfns[i]);
- i++;
+ equalfns = (FmgrInfo *)
+ MemoryContextAlloc(qcontext, numDistinctCols * sizeof(FmgrInfo));
+ for (i = 0; i < numDistinctCols; i++)
+ {
+ fmgr_info_cxt(get_opcode(osastate->qstate->eqOperators[i]),
+ &equalfns[i],
+ qcontext);
+ }
+ osastate->qstate->equalfns = equalfns;
}
+ sortColIdx = osastate->qstate->sortColIdx;
/* Get short-term context we can use for execTuplesMatch */
tmpcontext = AggGetPerTupleEContext(fcinfo)->ecxt_per_tuple_memory;
/* insert the hypothetical row into the sort */
- slot = osastate->tupslot;
+ slot = osastate->qstate->tupslot;
ExecClearTuple(slot);
for (i = 0; i < nargs; i++)
{
tuplesort_performsort(osastate->sortstate);
/*
- * We alternate fetching into osastate->tupslot and extraslot so that we
- * have the previous row available for comparisons. This is accomplished
- * by swapping the slot pointer variables after each row.
+ * We alternate fetching into tupslot and extraslot so that we have the
+ * previous row available for comparisons. This is accomplished by
+ * swapping the slot pointer variables after each row.
*/
- extraslot = MakeSingleTupleTableSlot(osastate->tupdesc);
+ extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc);
slot2 = extraslot;
/* iterate till we find the hypothetical row */