diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index b99027e0d7..f65755d019 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -29,6 +29,16 @@ * which saves the results in a temporary file or memory. After the * initial call, returns a tuple from the file with each call. * + * There are two distinct ways that we perform this sort: + * + * 1) When the result is a single column we perform a Datum sort. + * + * 2) When the result contains multiple columns we perform a tuple sort. + * + * We could do this by always performing a tuple sort, however sorting + * Datums only can be significantly faster than sorting tuples, + * especially when the Datums are of a byval type. + * * Conditions: * -- none. * @@ -37,7 +47,7 @@ * ---------------------------------------------------------------- */ static TupleTableSlot * -ExecSort(PlanState *pstate) +ExecSortTuple(PlanState *pstate) { SortState *node = castNode(SortState, pstate); EState *estate; @@ -50,7 +60,7 @@ ExecSort(PlanState *pstate) /* * get state info from node */ - SO1_printf("ExecSort: %s\n", + SO1_printf("ExecSortTuple: %s\n", "entering routine"); estate = node->ss.ps.state; @@ -68,7 +78,7 @@ ExecSort(PlanState *pstate) PlanState *outerNode; TupleDesc tupDesc; - SO1_printf("ExecSort: %s\n", + SO1_printf("ExecSortTuple: %s\n", "sorting subplan"); /* @@ -80,7 +90,7 @@ ExecSort(PlanState *pstate) /* * Initialize tuplesort module. */ - SO1_printf("ExecSort: %s\n", + SO1_printf("ExecSortTuple: %s\n", "calling tuplesort_begin"); outerNode = outerPlanState(node); @@ -138,10 +148,10 @@ ExecSort(PlanState *pstate) si = &node->shared_info->sinstrument[ParallelWorkerNumber]; tuplesort_get_stats(tuplesortstate, si); } - SO1_printf("ExecSort: %s\n", "sorting done"); + SO1_printf("ExecSortTuple: %s\n", "sorting done"); } - SO1_printf("ExecSort: %s\n", + SO1_printf("ExecSortTuple: %s\n", "retrieving tuple from tuplesort"); /* @@ -156,6 +166,127 @@ ExecSort(PlanState *pstate) return slot; } +static TupleTableSlot * +ExecSortDatum(PlanState *pstate) +{ + SortState *node = castNode(SortState, pstate); + EState *estate; + ScanDirection dir; + Tuplesortstate *tuplesortstate; + TupleTableSlot *slot; + + CHECK_FOR_INTERRUPTS(); + + /* + * get state info from node + */ + SO1_printf("ExecSort: %s\n", + "entering routine"); + + estate = node->ss.ps.state; + dir = estate->es_direction; + tuplesortstate = (Tuplesortstate *) node->tuplesortstate; + + /* + * If first time through, read all tuples from outer plan and pass them to + * tuplesort.c. Subsequent calls just fetch tuples from tuplesort. + */ + + if (!node->sort_Done) + { + Sort *plannode = (Sort *) node->ss.ps.plan; + PlanState *outerNode; + TupleDesc tupDesc; + + SO1_printf("ExecSortDatum: %s\n", + "sorting subplan"); + + /* + * Want to scan subplan in the forward direction while creating the + * sorted data. + */ + estate->es_direction = ForwardScanDirection; + + /* + * Initialize tuplesort module. + */ + SO1_printf("ExecSortDatum: %s\n", + "calling tuplesort_begin"); + + outerNode = outerPlanState(node); + tupDesc = ExecGetResultType(outerNode); + + tuplesortstate = tuplesort_begin_datum(TupleDescAttr(tupDesc, 0)->atttypid, + plannode->sortOperators[0], + plannode->collations[0], + plannode->nullsFirst[0], + work_mem, + NULL, + node->randomAccess); + if (node->bounded) + tuplesort_set_bound(tuplesortstate, node->bound); + node->tuplesortstate = (void *) tuplesortstate; + + /* + * Scan the subplan and feed all the tuples to tuplesort. + */ + + for (;;) + { + slot = ExecProcNode(outerNode); + + if (TupIsNull(slot)) + break; + slot_getsomeattrs(slot, 1); + tuplesort_putdatum(tuplesortstate, + slot->tts_values[0], + slot->tts_isnull[0]); + } + + /* + * Complete the sort. + */ + tuplesort_performsort(tuplesortstate); + + /* + * restore to user specified direction + */ + estate->es_direction = dir; + + /* + * finally set the sorted flag to true + */ + node->sort_Done = true; + node->bounded_Done = node->bounded; + node->bound_Done = node->bound; + if (node->shared_info && node->am_worker) + { + TuplesortInstrumentation *si; + + Assert(IsParallelWorker()); + Assert(ParallelWorkerNumber <= node->shared_info->num_workers); + si = &node->shared_info->sinstrument[ParallelWorkerNumber]; + tuplesort_get_stats(tuplesortstate, si); + } + SO1_printf("ExecSortDatum: %s\n", "sorting done"); + } + + SO1_printf("ExecSortDatum: %s\n", + "retrieving datum from tuplesort"); + + /* + * Get the first or next datum from tuplesort. We clear the slot before + * doing this. This means when we run out of datums to get that the + * function returns false and the slot is left empty. + */ + slot = node->ss.ps.ps_ResultTupleSlot; + ExecClearTuple(slot); + if (tuplesort_getdatum(tuplesortstate, ScanDirectionIsForward(dir), + &(slot->tts_values[0]), &(slot->tts_isnull[0]), NULL)) + ExecStoreVirtualTuple(slot); + return slot; +} + /* ---------------------------------------------------------------- * ExecInitSort * @@ -177,7 +308,6 @@ ExecInitSort(Sort *node, EState *estate, int eflags) sortstate = makeNode(SortState); sortstate->ss.ps.plan = (Plan *) node; sortstate->ss.ps.state = estate; - sortstate->ss.ps.ExecProcNode = ExecSort; /* * We must have random access to the sort output to do backward scan or @@ -221,6 +351,15 @@ ExecInitSort(Sort *node, EState *estate, int eflags) ExecInitResultTupleSlotTL(&sortstate->ss.ps, &TTSOpsMinimalTuple); sortstate->ss.ps.ps_ProjInfo = NULL; + /* + * We perform a Datum sort when we're sorting just a single column, + * otherwise we perform a tuple sort. + */ + if (ExecGetResultType(outerPlanState(sortstate))->natts == 1) + sortstate->ss.ps.ExecProcNode = ExecSortDatum; + else + sortstate->ss.ps.ExecProcNode = ExecSortTuple; + SO1_printf("ExecInitSort: %s\n", "sort node initialized");