Commit f1f5ec1e authored by Robert Haas's avatar Robert Haas

Reuse abbreviated keys in ordered [set] aggregates.

When processing ordered aggregates following a sort that could make use
of the abbreviated key optimization, only call the equality operator to
compare successive pairs of tuples when their abbreviated keys were not
equal.

Peter Geoghegan, reviewd by Andreas Karlsson and by me.
parent 66f50386
...@@ -3063,7 +3063,7 @@ validate_index_heapscan(Relation heapRelation, ...@@ -3063,7 +3063,7 @@ validate_index_heapscan(Relation heapRelation,
} }
tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true, tuplesort_empty = !tuplesort_getdatum(state->tuplesort, true,
&ts_val, &ts_isnull); &ts_val, &ts_isnull, NULL);
Assert(tuplesort_empty || !ts_isnull); Assert(tuplesort_empty || !ts_isnull);
if (!tuplesort_empty) if (!tuplesort_empty)
{ {
......
...@@ -575,7 +575,8 @@ fetch_input_tuple(AggState *aggstate) ...@@ -575,7 +575,8 @@ fetch_input_tuple(AggState *aggstate)
if (aggstate->sort_in) if (aggstate->sort_in)
{ {
if (!tuplesort_gettupleslot(aggstate->sort_in, true, aggstate->sort_slot)) if (!tuplesort_gettupleslot(aggstate->sort_in, true, aggstate->sort_slot,
NULL))
return NULL; return NULL;
slot = aggstate->sort_slot; slot = aggstate->sort_slot;
} }
...@@ -1076,6 +1077,8 @@ process_ordered_aggregate_single(AggState *aggstate, ...@@ -1076,6 +1077,8 @@ process_ordered_aggregate_single(AggState *aggstate,
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory; MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
MemoryContext oldContext; MemoryContext oldContext;
bool isDistinct = (pertrans->numDistinctCols > 0); bool isDistinct = (pertrans->numDistinctCols > 0);
Datum newAbbrevVal = (Datum) 0;
Datum oldAbbrevVal = (Datum) 0;
FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo; FunctionCallInfo fcinfo = &pertrans->transfn_fcinfo;
Datum *newVal; Datum *newVal;
bool *isNull; bool *isNull;
...@@ -1095,7 +1098,7 @@ process_ordered_aggregate_single(AggState *aggstate, ...@@ -1095,7 +1098,7 @@ process_ordered_aggregate_single(AggState *aggstate,
*/ */
while (tuplesort_getdatum(pertrans->sortstates[aggstate->current_set], while (tuplesort_getdatum(pertrans->sortstates[aggstate->current_set],
true, newVal, isNull)) true, newVal, isNull, &newAbbrevVal))
{ {
/* /*
* Clear and select the working context for evaluation of the equality * Clear and select the working context for evaluation of the equality
...@@ -1113,6 +1116,7 @@ process_ordered_aggregate_single(AggState *aggstate, ...@@ -1113,6 +1116,7 @@ process_ordered_aggregate_single(AggState *aggstate,
haveOldVal && haveOldVal &&
((oldIsNull && *isNull) || ((oldIsNull && *isNull) ||
(!oldIsNull && !*isNull && (!oldIsNull && !*isNull &&
oldAbbrevVal == newAbbrevVal &&
DatumGetBool(FunctionCall2(&pertrans->equalfns[0], DatumGetBool(FunctionCall2(&pertrans->equalfns[0],
oldVal, *newVal))))) oldVal, *newVal)))))
{ {
...@@ -1128,6 +1132,7 @@ process_ordered_aggregate_single(AggState *aggstate, ...@@ -1128,6 +1132,7 @@ process_ordered_aggregate_single(AggState *aggstate,
pfree(DatumGetPointer(oldVal)); pfree(DatumGetPointer(oldVal));
/* and remember the new one for subsequent equality checks */ /* and remember the new one for subsequent equality checks */
oldVal = *newVal; oldVal = *newVal;
oldAbbrevVal = newAbbrevVal;
oldIsNull = *isNull; oldIsNull = *isNull;
haveOldVal = true; haveOldVal = true;
} }
...@@ -1165,6 +1170,8 @@ process_ordered_aggregate_multi(AggState *aggstate, ...@@ -1165,6 +1170,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
TupleTableSlot *slot2 = pertrans->uniqslot; TupleTableSlot *slot2 = pertrans->uniqslot;
int numTransInputs = pertrans->numTransInputs; int numTransInputs = pertrans->numTransInputs;
int numDistinctCols = pertrans->numDistinctCols; int numDistinctCols = pertrans->numDistinctCols;
Datum newAbbrevVal = (Datum) 0;
Datum oldAbbrevVal = (Datum) 0;
bool haveOldValue = false; bool haveOldValue = false;
int i; int i;
...@@ -1175,7 +1182,7 @@ process_ordered_aggregate_multi(AggState *aggstate, ...@@ -1175,7 +1182,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
ExecClearTuple(slot2); ExecClearTuple(slot2);
while (tuplesort_gettupleslot(pertrans->sortstates[aggstate->current_set], while (tuplesort_gettupleslot(pertrans->sortstates[aggstate->current_set],
true, slot1)) true, slot1, &newAbbrevVal))
{ {
/* /*
* Extract the first numTransInputs columns as datums to pass to the * Extract the first numTransInputs columns as datums to pass to the
...@@ -1186,6 +1193,7 @@ process_ordered_aggregate_multi(AggState *aggstate, ...@@ -1186,6 +1193,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
if (numDistinctCols == 0 || if (numDistinctCols == 0 ||
!haveOldValue || !haveOldValue ||
newAbbrevVal != oldAbbrevVal ||
!execTuplesMatch(slot1, slot2, !execTuplesMatch(slot1, slot2,
numDistinctCols, numDistinctCols,
pertrans->sortColIdx, pertrans->sortColIdx,
...@@ -1209,6 +1217,8 @@ process_ordered_aggregate_multi(AggState *aggstate, ...@@ -1209,6 +1217,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
slot2 = slot1; slot2 = slot1;
slot1 = tmpslot; slot1 = tmpslot;
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
oldAbbrevVal = newAbbrevVal;
haveOldValue = true; haveOldValue = true;
} }
} }
......
...@@ -137,7 +137,7 @@ ExecSort(SortState *node) ...@@ -137,7 +137,7 @@ ExecSort(SortState *node)
slot = node->ss.ps.ps_ResultTupleSlot; slot = node->ss.ps.ps_ResultTupleSlot;
(void) tuplesort_gettupleslot(tuplesortstate, (void) tuplesort_gettupleslot(tuplesortstate,
ScanDirectionIsForward(dir), ScanDirectionIsForward(dir),
slot); slot, NULL);
return slot; return slot;
} }
......
...@@ -453,7 +453,7 @@ percentile_disc_final(PG_FUNCTION_ARGS) ...@@ -453,7 +453,7 @@ percentile_disc_final(PG_FUNCTION_ARGS)
elog(ERROR, "missing row in percentile_disc"); elog(ERROR, "missing row in percentile_disc");
} }
if (!tuplesort_getdatum(osastate->sortstate, true, &val, &isnull)) if (!tuplesort_getdatum(osastate->sortstate, true, &val, &isnull, NULL))
elog(ERROR, "missing row in percentile_disc"); elog(ERROR, "missing row in percentile_disc");
/* /*
...@@ -553,7 +553,7 @@ percentile_cont_final_common(FunctionCallInfo fcinfo, ...@@ -553,7 +553,7 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
if (!tuplesort_skiptuples(osastate->sortstate, first_row, true)) if (!tuplesort_skiptuples(osastate->sortstate, first_row, true))
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
if (!tuplesort_getdatum(osastate->sortstate, true, &first_val, &isnull)) if (!tuplesort_getdatum(osastate->sortstate, true, &first_val, &isnull, NULL))
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
if (isnull) if (isnull)
PG_RETURN_NULL(); PG_RETURN_NULL();
...@@ -564,7 +564,7 @@ percentile_cont_final_common(FunctionCallInfo fcinfo, ...@@ -564,7 +564,7 @@ percentile_cont_final_common(FunctionCallInfo fcinfo,
} }
else else
{ {
if (!tuplesort_getdatum(osastate->sortstate, true, &second_val, &isnull)) if (!tuplesort_getdatum(osastate->sortstate, true, &second_val, &isnull, NULL))
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
if (isnull) if (isnull)
...@@ -792,7 +792,7 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS) ...@@ -792,7 +792,7 @@ percentile_disc_multi_final(PG_FUNCTION_ARGS)
if (!tuplesort_skiptuples(osastate->sortstate, target_row - rownum - 1, true)) if (!tuplesort_skiptuples(osastate->sortstate, target_row - rownum - 1, true))
elog(ERROR, "missing row in percentile_disc"); elog(ERROR, "missing row in percentile_disc");
if (!tuplesort_getdatum(osastate->sortstate, true, &val, &isnull)) if (!tuplesort_getdatum(osastate->sortstate, true, &val, &isnull, NULL))
elog(ERROR, "missing row in percentile_disc"); elog(ERROR, "missing row in percentile_disc");
rownum = target_row; rownum = target_row;
...@@ -921,7 +921,8 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo, ...@@ -921,7 +921,8 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
if (!tuplesort_skiptuples(osastate->sortstate, first_row - rownum - 1, true)) if (!tuplesort_skiptuples(osastate->sortstate, first_row - rownum - 1, true))
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
if (!tuplesort_getdatum(osastate->sortstate, true, &first_val, &isnull) || isnull) if (!tuplesort_getdatum(osastate->sortstate, true, &first_val,
&isnull, NULL) || isnull)
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
rownum = first_row; rownum = first_row;
...@@ -941,7 +942,8 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo, ...@@ -941,7 +942,8 @@ percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
/* Fetch second_row if needed */ /* Fetch second_row if needed */
if (second_row > rownum) if (second_row > rownum)
{ {
if (!tuplesort_getdatum(osastate->sortstate, true, &second_val, &isnull) || isnull) if (!tuplesort_getdatum(osastate->sortstate, true, &second_val,
&isnull, NULL) || isnull)
elog(ERROR, "missing row in percentile_cont"); elog(ERROR, "missing row in percentile_cont");
rownum++; rownum++;
} }
...@@ -1016,6 +1018,8 @@ mode_final(PG_FUNCTION_ARGS) ...@@ -1016,6 +1018,8 @@ mode_final(PG_FUNCTION_ARGS)
int64 last_val_freq = 0; int64 last_val_freq = 0;
bool last_val_is_mode = false; bool last_val_is_mode = false;
FmgrInfo *equalfn; FmgrInfo *equalfn;
Datum abbrev_val = (Datum) 0;
Datum last_abbrev_val = (Datum) 0;
bool shouldfree; bool shouldfree;
Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE); Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
...@@ -1042,7 +1046,7 @@ mode_final(PG_FUNCTION_ARGS) ...@@ -1042,7 +1046,7 @@ mode_final(PG_FUNCTION_ARGS)
tuplesort_performsort(osastate->sortstate); tuplesort_performsort(osastate->sortstate);
/* Scan tuples and count frequencies */ /* Scan tuples and count frequencies */
while (tuplesort_getdatum(osastate->sortstate, true, &val, &isnull)) while (tuplesort_getdatum(osastate->sortstate, true, &val, &isnull, &abbrev_val))
{ {
/* we don't expect any nulls, but ignore them if found */ /* we don't expect any nulls, but ignore them if found */
if (isnull) if (isnull)
...@@ -1054,8 +1058,10 @@ mode_final(PG_FUNCTION_ARGS) ...@@ -1054,8 +1058,10 @@ mode_final(PG_FUNCTION_ARGS)
mode_val = last_val = val; mode_val = last_val = val;
mode_freq = last_val_freq = 1; mode_freq = last_val_freq = 1;
last_val_is_mode = true; last_val_is_mode = true;
last_abbrev_val = abbrev_val;
} }
else if (DatumGetBool(FunctionCall2(equalfn, val, last_val))) else if (abbrev_val == last_abbrev_val &&
DatumGetBool(FunctionCall2(equalfn, val, last_val)))
{ {
/* value equal to previous value, count it */ /* value equal to previous value, count it */
if (last_val_is_mode) if (last_val_is_mode)
...@@ -1078,6 +1084,8 @@ mode_final(PG_FUNCTION_ARGS) ...@@ -1078,6 +1084,8 @@ mode_final(PG_FUNCTION_ARGS)
if (shouldfree && !last_val_is_mode) if (shouldfree && !last_val_is_mode)
pfree(DatumGetPointer(last_val)); pfree(DatumGetPointer(last_val));
last_val = val; last_val = val;
/* avoid equality function calls by reusing abbreviated keys */
last_abbrev_val = abbrev_val;
last_val_freq = 1; last_val_freq = 1;
last_val_is_mode = false; last_val_is_mode = false;
} }
...@@ -1181,7 +1189,7 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag, ...@@ -1181,7 +1189,7 @@ hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
tuplesort_performsort(osastate->sortstate); tuplesort_performsort(osastate->sortstate);
/* iterate till we find the hypothetical row */ /* iterate till we find the hypothetical row */
while (tuplesort_gettupleslot(osastate->sortstate, true, slot)) while (tuplesort_gettupleslot(osastate->sortstate, true, slot, NULL))
{ {
bool isnull; bool isnull;
Datum d = slot_getattr(slot, nargs + 1, &isnull); Datum d = slot_getattr(slot, nargs + 1, &isnull);
...@@ -1266,6 +1274,8 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) ...@@ -1266,6 +1274,8 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
int64 duplicate_count = 0; int64 duplicate_count = 0;
OSAPerGroupState *osastate; OSAPerGroupState *osastate;
int numDistinctCols; int numDistinctCols;
Datum abbrevVal = (Datum) 0;
Datum abbrevOld = (Datum) 0;
AttrNumber *sortColIdx; AttrNumber *sortColIdx;
FmgrInfo *equalfns; FmgrInfo *equalfns;
TupleTableSlot *slot; TupleTableSlot *slot;
...@@ -1342,7 +1352,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) ...@@ -1342,7 +1352,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
slot2 = extraslot; slot2 = extraslot;
/* iterate till we find the hypothetical row */ /* iterate till we find the hypothetical row */
while (tuplesort_gettupleslot(osastate->sortstate, true, slot)) while (tuplesort_gettupleslot(osastate->sortstate, true, slot, &abbrevVal))
{ {
bool isnull; bool isnull;
Datum d = slot_getattr(slot, nargs + 1, &isnull); Datum d = slot_getattr(slot, nargs + 1, &isnull);
...@@ -1353,6 +1363,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) ...@@ -1353,6 +1363,7 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
/* count non-distinct tuples */ /* count non-distinct tuples */
if (!TupIsNull(slot2) && if (!TupIsNull(slot2) &&
abbrevVal == abbrevOld &&
execTuplesMatch(slot, slot2, execTuplesMatch(slot, slot2,
numDistinctCols, numDistinctCols,
sortColIdx, sortColIdx,
...@@ -1363,6 +1374,8 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS) ...@@ -1363,6 +1374,8 @@ hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
tmpslot = slot2; tmpslot = slot2;
slot2 = slot; slot2 = slot;
slot = tmpslot; slot = tmpslot;
/* avoid execTuplesMatch() calls by reusing abbreviated keys */
abbrevOld = abbrevVal;
rank++; rank++;
......
...@@ -1280,7 +1280,8 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel, ...@@ -1280,7 +1280,8 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
* converter it won't expect NULL values, and cost model is not * converter it won't expect NULL values, and cost model is not
* required to account for NULL, so in that case we avoid calling * required to account for NULL, so in that case we avoid calling
* converter and just set datum1 to zeroed representation (to be * converter and just set datum1 to zeroed representation (to be
* consistent). * consistent, and to support cheap inequality tests for NULL
* abbreviated keys).
*/ */
stup.datum1 = original; stup.datum1 = original;
} }
...@@ -1349,7 +1350,11 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull) ...@@ -1349,7 +1350,11 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
if (isNull || state->datumTypeByVal) if (isNull || state->datumTypeByVal)
{ {
stup.datum1 = val; /*
* Set datum1 to zeroed representation for NULLs (to be consistent, and
* to support cheap inequality tests for NULL abbreviated keys).
*/
stup.datum1 = !isNull ? val : (Datum) 0;
stup.isnull1 = isNull; stup.isnull1 = isNull;
stup.tuple = NULL; /* no separate storage */ stup.tuple = NULL; /* no separate storage */
} }
...@@ -1866,10 +1871,17 @@ tuplesort_gettuple_common(Tuplesortstate *state, bool forward, ...@@ -1866,10 +1871,17 @@ tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
* Fetch the next tuple in either forward or back direction. * Fetch the next tuple in either forward or back direction.
* If successful, put tuple in slot and return TRUE; else, clear the slot * If successful, put tuple in slot and return TRUE; else, clear the slot
* and return FALSE. * and return FALSE.
*
* Caller may optionally be passed back abbreviated value (on TRUE return
* value) when abbreviation was used, which can be used to cheaply avoid
* equality checks that might otherwise be required. Caller can safely make a
* determination of "non-equal tuple" based on simple binary inequality. A
* NULL value in leading attribute will set abbreviated value to zeroed
* representation, which caller may rely on in abbreviated inequality check.
*/ */
bool bool
tuplesort_gettupleslot(Tuplesortstate *state, bool forward, tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
TupleTableSlot *slot) TupleTableSlot *slot, Datum *abbrev)
{ {
MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
SortTuple stup; SortTuple stup;
...@@ -1882,6 +1894,10 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward, ...@@ -1882,6 +1894,10 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
if (stup.tuple) if (stup.tuple)
{ {
/* Record abbreviated key for caller */
if (state->sortKeys->abbrev_converter && abbrev)
*abbrev = stup.datum1;
ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free); ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, should_free);
return true; return true;
} }
...@@ -1937,10 +1953,17 @@ tuplesort_getindextuple(Tuplesortstate *state, bool forward, ...@@ -1937,10 +1953,17 @@ tuplesort_getindextuple(Tuplesortstate *state, bool forward,
* *
* If the Datum is pass-by-ref type, the returned value is freshly palloc'd * If the Datum is pass-by-ref type, the returned value is freshly palloc'd
* and is now owned by the caller. * and is now owned by the caller.
*
* Caller may optionally be passed back abbreviated value (on TRUE return
* value) when abbreviation was used, which can be used to cheaply avoid
* equality checks that might otherwise be required. Caller can safely make a
* determination of "non-equal tuple" based on simple binary inequality. A
* NULL value will have a zeroed abbreviated value representation, which caller
* may rely on in abbreviated inequality check.
*/ */
bool bool
tuplesort_getdatum(Tuplesortstate *state, bool forward, tuplesort_getdatum(Tuplesortstate *state, bool forward,
Datum *val, bool *isNull) Datum *val, bool *isNull, Datum *abbrev)
{ {
MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext);
SortTuple stup; SortTuple stup;
...@@ -1952,6 +1975,10 @@ tuplesort_getdatum(Tuplesortstate *state, bool forward, ...@@ -1952,6 +1975,10 @@ tuplesort_getdatum(Tuplesortstate *state, bool forward,
return false; return false;
} }
/* Record abbreviated key for caller */
if (state->sortKeys->abbrev_converter && abbrev)
*abbrev = stup.datum1;
if (stup.isnull1 || state->datumTypeByVal) if (stup.isnull1 || state->datumTypeByVal)
{ {
*val = stup.datum1; *val = stup.datum1;
...@@ -2232,21 +2259,6 @@ mergeruns(Tuplesortstate *state) ...@@ -2232,21 +2259,6 @@ mergeruns(Tuplesortstate *state)
Assert(state->status == TSS_BUILDRUNS); Assert(state->status == TSS_BUILDRUNS);
Assert(state->memtupcount == 0); Assert(state->memtupcount == 0);
/*
* If we produced only one initial run (quite likely if the total data
* volume is between 1X and 2X workMem), we can just use that tape as the
* finished output, rather than doing a useless merge. (This obvious
* optimization is not in Knuth's algorithm.)
*/
if (state->currentRun == 1)
{
state->result_tape = state->tp_tapenum[state->destTape];
/* must freeze and rewind the finished output tape */
LogicalTapeFreeze(state->tapeset, state->result_tape);
state->status = TSS_SORTEDONTAPE;
return;
}
if (state->sortKeys != NULL && state->sortKeys->abbrev_converter != NULL) if (state->sortKeys != NULL && state->sortKeys->abbrev_converter != NULL)
{ {
/* /*
...@@ -2263,6 +2275,21 @@ mergeruns(Tuplesortstate *state) ...@@ -2263,6 +2275,21 @@ mergeruns(Tuplesortstate *state)
state->sortKeys->abbrev_full_comparator = NULL; state->sortKeys->abbrev_full_comparator = NULL;
} }
/*
* If we produced only one initial run (quite likely if the total data
* volume is between 1X and 2X workMem), we can just use that tape as the
* finished output, rather than doing a useless merge. (This obvious
* optimization is not in Knuth's algorithm.)
*/
if (state->currentRun == 1)
{
state->result_tape = state->tp_tapenum[state->destTape];
/* must freeze and rewind the finished output tape */
LogicalTapeFreeze(state->tapeset, state->result_tape);
state->status = TSS_SORTEDONTAPE;
return;
}
/* End of step D2: rewind all output tapes to prepare for merging */ /* End of step D2: rewind all output tapes to prepare for merging */
for (tapenum = 0; tapenum < state->tapeRange; tapenum++) for (tapenum = 0; tapenum < state->tapeRange; tapenum++)
LogicalTapeRewind(state->tapeset, tapenum, false); LogicalTapeRewind(state->tapeset, tapenum, false);
...@@ -3164,7 +3191,8 @@ copytup_heap(Tuplesortstate *state, SortTuple *stup, void *tup) ...@@ -3164,7 +3191,8 @@ copytup_heap(Tuplesortstate *state, SortTuple *stup, void *tup)
* converter it won't expect NULL values, and cost model is not * converter it won't expect NULL values, and cost model is not
* required to account for NULL, so in that case we avoid calling * required to account for NULL, so in that case we avoid calling
* converter and just set datum1 to zeroed representation (to be * converter and just set datum1 to zeroed representation (to be
* consistent). * consistent, and to support cheap inequality tests for NULL
* abbreviated keys).
*/ */
stup->datum1 = original; stup->datum1 = original;
} }
...@@ -3406,7 +3434,8 @@ copytup_cluster(Tuplesortstate *state, SortTuple *stup, void *tup) ...@@ -3406,7 +3434,8 @@ copytup_cluster(Tuplesortstate *state, SortTuple *stup, void *tup)
* converter it won't expect NULL values, and cost model is not * converter it won't expect NULL values, and cost model is not
* required to account for NULL, so in that case we avoid calling * required to account for NULL, so in that case we avoid calling
* converter and just set datum1 to zeroed representation (to be * converter and just set datum1 to zeroed representation (to be
* consistent). * consistent, and to support cheap inequality tests for NULL
* abbreviated keys).
*/ */
stup->datum1 = original; stup->datum1 = original;
} }
...@@ -3710,7 +3739,8 @@ copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup) ...@@ -3710,7 +3739,8 @@ copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup)
* converter it won't expect NULL values, and cost model is not * converter it won't expect NULL values, and cost model is not
* required to account for NULL, so in that case we avoid calling * required to account for NULL, so in that case we avoid calling
* converter and just set datum1 to zeroed representation (to be * converter and just set datum1 to zeroed representation (to be
* consistent). * consistent, and to support cheap inequality tests for NULL
* abbreviated keys).
*/ */
stup->datum1 = original; stup->datum1 = original;
} }
......
...@@ -93,13 +93,13 @@ extern void tuplesort_putdatum(Tuplesortstate *state, Datum val, ...@@ -93,13 +93,13 @@ extern void tuplesort_putdatum(Tuplesortstate *state, Datum val,
extern void tuplesort_performsort(Tuplesortstate *state); extern void tuplesort_performsort(Tuplesortstate *state);
extern bool tuplesort_gettupleslot(Tuplesortstate *state, bool forward, extern bool tuplesort_gettupleslot(Tuplesortstate *state, bool forward,
TupleTableSlot *slot); TupleTableSlot *slot, Datum *abbrev);
extern HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward, extern HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward,
bool *should_free); bool *should_free);
extern IndexTuple tuplesort_getindextuple(Tuplesortstate *state, bool forward, extern IndexTuple tuplesort_getindextuple(Tuplesortstate *state, bool forward,
bool *should_free); bool *should_free);
extern bool tuplesort_getdatum(Tuplesortstate *state, bool forward, extern bool tuplesort_getdatum(Tuplesortstate *state, bool forward,
Datum *val, bool *isNull); Datum *val, bool *isNull, Datum *abbrev);
extern bool tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples, extern bool tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples,
bool forward); bool forward);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment