mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-06 20:07:39 +08:00
of tuples when passing data up through multiple plan nodes. A slot can now hold either a normal "physical" HeapTuple, or a "virtual" tuple consisting of Datum/isnull arrays. Upper plan levels can usually just copy the Datum arrays, avoiding heap_formtuple() and possible subsequent nocachegetattr() calls to extract the data again. This work extends Atsushi Ogawa's earlier patch, which provided the key idea of adding Datum arrays to TupleTableSlots. (I believe however that something like this was foreseen way back in Berkeley days --- see the old comment on ExecProject.) A test case involving many levels of join of fairly wide tables (about 80 columns altogether) showed about 3x overall speedup, though simple queries will probably not be helped very much. I have also duplicated some code in heaptuple.c in order to provide versions of heap_formtuple and friends that use "bool" arrays to indicate null attributes, instead of the old convention of "char" arrays containing either 'n' or ' '. This provides a better match to the convention used by ExecEvalExpr. While I have not made a concerted effort to get rid of uses of the old routines, I think they should be deprecated and eventually removed.
313 lines
7.2 KiB
C
313 lines
7.2 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeSort.c
|
|
* Routines to handle sorting of relations.
|
|
*
|
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/executor/nodeSort.c,v 1.50 2005/03/16 21:38:08 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "executor/execdebug.h"
|
|
#include "executor/nodeSort.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/tuplesort.h"
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSort
|
|
*
|
|
* Sorts tuples from the outer subtree of the node using tuplesort,
|
|
* which saves the results in a temporary file or memory. After the
|
|
* initial call, returns a tuple from the file with each call.
|
|
*
|
|
* Conditions:
|
|
* -- none.
|
|
*
|
|
* Initial States:
|
|
* -- the outer child is prepared to return the first tuple.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleTableSlot *
|
|
ExecSort(SortState *node)
|
|
{
|
|
EState *estate;
|
|
ScanDirection dir;
|
|
Tuplesortstate *tuplesortstate;
|
|
HeapTuple heapTuple;
|
|
TupleTableSlot *slot;
|
|
bool should_free;
|
|
|
|
/*
|
|
* 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("ExecSort: %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("ExecSort: %s\n",
|
|
"calling tuplesort_begin");
|
|
|
|
outerNode = outerPlanState(node);
|
|
tupDesc = ExecGetResultType(outerNode);
|
|
|
|
tuplesortstate = tuplesort_begin_heap(tupDesc,
|
|
plannode->numCols,
|
|
plannode->sortOperators,
|
|
plannode->sortColIdx,
|
|
work_mem,
|
|
true /* randomAccess */ );
|
|
node->tuplesortstate = (void *) tuplesortstate;
|
|
|
|
/*
|
|
* Scan the subplan and feed all the tuples to tuplesort.
|
|
*/
|
|
|
|
for (;;)
|
|
{
|
|
slot = ExecProcNode(outerNode);
|
|
|
|
if (TupIsNull(slot))
|
|
break;
|
|
|
|
tuplesort_puttuple(tuplesortstate,
|
|
(void *) ExecFetchSlotTuple(slot));
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
SO1_printf("ExecSort: %s\n", "sorting done");
|
|
}
|
|
|
|
SO1_printf("ExecSort: %s\n",
|
|
"retrieving tuple from tuplesort");
|
|
|
|
/*
|
|
* Get the first or next tuple from tuplesort. Returns NULL if no more
|
|
* tuples.
|
|
*/
|
|
heapTuple = tuplesort_getheaptuple(tuplesortstate,
|
|
ScanDirectionIsForward(dir),
|
|
&should_free);
|
|
|
|
slot = node->ss.ps.ps_ResultTupleSlot;
|
|
if (heapTuple)
|
|
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
|
|
else
|
|
return ExecClearTuple(slot);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitSort
|
|
*
|
|
* Creates the run-time state information for the sort node
|
|
* produced by the planner and initailizes its outer subtree.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
SortState *
|
|
ExecInitSort(Sort *node, EState *estate)
|
|
{
|
|
SortState *sortstate;
|
|
|
|
SO1_printf("ExecInitSort: %s\n",
|
|
"initializing sort node");
|
|
|
|
/*
|
|
* create state structure
|
|
*/
|
|
sortstate = makeNode(SortState);
|
|
sortstate->ss.ps.plan = (Plan *) node;
|
|
sortstate->ss.ps.state = estate;
|
|
|
|
sortstate->sort_Done = false;
|
|
sortstate->tuplesortstate = NULL;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* Sort nodes don't initialize their ExprContexts because they never call
|
|
* ExecQual or ExecProject.
|
|
*/
|
|
|
|
#define SORT_NSLOTS 2
|
|
|
|
/*
|
|
* tuple table initialization
|
|
*
|
|
* sort nodes only return scan tuples from their sorted relation.
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &sortstate->ss.ps);
|
|
ExecInitScanTupleSlot(estate, &sortstate->ss);
|
|
|
|
/*
|
|
* initializes child nodes
|
|
*/
|
|
outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate);
|
|
|
|
/*
|
|
* initialize tuple type. no need to initialize projection info
|
|
* because this node doesn't do projections.
|
|
*/
|
|
ExecAssignResultTypeFromOuterPlan(&sortstate->ss.ps);
|
|
ExecAssignScanTypeFromOuterPlan(&sortstate->ss);
|
|
sortstate->ss.ps.ps_ProjInfo = NULL;
|
|
|
|
SO1_printf("ExecInitSort: %s\n",
|
|
"sort node initialized");
|
|
|
|
return sortstate;
|
|
}
|
|
|
|
int
|
|
ExecCountSlotsSort(Sort *node)
|
|
{
|
|
return ExecCountSlotsNode(outerPlan((Plan *) node)) +
|
|
ExecCountSlotsNode(innerPlan((Plan *) node)) +
|
|
SORT_NSLOTS;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndSort(node)
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndSort(SortState *node)
|
|
{
|
|
SO1_printf("ExecEndSort: %s\n",
|
|
"shutting down sort node");
|
|
|
|
/*
|
|
* clean out the tuple table
|
|
*/
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
|
|
|
/*
|
|
* Release tuplesort resources
|
|
*/
|
|
if (node->tuplesortstate != NULL)
|
|
tuplesort_end((Tuplesortstate *) node->tuplesortstate);
|
|
node->tuplesortstate = NULL;
|
|
|
|
/*
|
|
* shut down the subplan
|
|
*/
|
|
ExecEndNode(outerPlanState(node));
|
|
|
|
SO1_printf("ExecEndSort: %s\n",
|
|
"sort node shutdown");
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSortMarkPos
|
|
*
|
|
* Calls tuplesort to save the current position in the sorted file.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecSortMarkPos(SortState *node)
|
|
{
|
|
/*
|
|
* if we haven't sorted yet, just return
|
|
*/
|
|
if (!node->sort_Done)
|
|
return;
|
|
|
|
tuplesort_markpos((Tuplesortstate *) node->tuplesortstate);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSortRestrPos
|
|
*
|
|
* Calls tuplesort to restore the last saved sort file position.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecSortRestrPos(SortState *node)
|
|
{
|
|
/*
|
|
* if we haven't sorted yet, just return.
|
|
*/
|
|
if (!node->sort_Done)
|
|
return;
|
|
|
|
/*
|
|
* restore the scan to the previously marked position
|
|
*/
|
|
tuplesort_restorepos((Tuplesortstate *) node->tuplesortstate);
|
|
}
|
|
|
|
void
|
|
ExecReScanSort(SortState *node, ExprContext *exprCtxt)
|
|
{
|
|
/*
|
|
* If we haven't sorted yet, just return. If outerplan' chgParam is
|
|
* not NULL then it will be re-scanned by ExecProcNode, else - no
|
|
* reason to re-scan it at all.
|
|
*/
|
|
if (!node->sort_Done)
|
|
return;
|
|
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
|
|
/*
|
|
* If subnode is to be rescanned then we forget previous sort results;
|
|
* we have to re-read the subplan and re-sort.
|
|
*
|
|
* Otherwise we can just rewind and rescan the sorted output.
|
|
*/
|
|
if (((PlanState *) node)->lefttree->chgParam != NULL)
|
|
{
|
|
node->sort_Done = false;
|
|
tuplesort_end((Tuplesortstate *) node->tuplesortstate);
|
|
node->tuplesortstate = NULL;
|
|
}
|
|
else
|
|
tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
|
|
}
|