mirror of
https://github.com/postgres/postgres.git
synced 2025-09-09 13:09:39 +03:00
generate their output tuple descriptors from their target lists (ie, using ExecAssignResultTypeFromTL()). We long ago fixed things so that all node types have minimally valid tlists, so there's no longer any good reason to have two different ways of doing it. This change is needed to fix bug reported by Hayden James: the fix of 2005-11-03 to emit the correct column names after optimizing away a SubqueryScan node didn't work if the new top-level plan node used ExecAssignResultTypeFromOuterPlan to generate its tupdesc, since the next plan node down won't have the correct column labels.
312 lines
7.2 KiB
C
312 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.51.2.1 2005/11/23 20:28:04 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.
|
|
*/
|
|
ExecAssignResultTypeFromTL(&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);
|
|
}
|