mirror of
https://github.com/postgres/postgres.git
synced 2025-05-15 00:02:24 -04:00
Allow internal sorts to be stored in memory rather than in files.
This commit is contained in:
parent
3bea7b138b
commit
f5f366e188
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.5 1996/11/10 02:59:54 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.6 1997/08/06 03:41:29 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -374,6 +374,18 @@ ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
|
|||||||
printf("******** \n");
|
printf("******** \n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
CleanUpSort(Plan *plan) {
|
||||||
|
|
||||||
|
if (plan == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (plan->type == T_Sort) {
|
||||||
|
Sort *sort = (Sort *)plan;
|
||||||
|
psort_end(sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecMergeJoin
|
* ExecMergeJoin
|
||||||
*
|
*
|
||||||
@ -676,6 +688,8 @@ ExecMergeJoin(MergeJoin *node)
|
|||||||
*/
|
*/
|
||||||
if (TupIsNull(outerTupleSlot)) {
|
if (TupIsNull(outerTupleSlot)) {
|
||||||
MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
|
MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
|
||||||
|
CleanUpSort(node->join.lefttree->lefttree);
|
||||||
|
CleanUpSort(node->join.righttree->lefttree);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
* nodeSort.c--
|
* nodeSort.c--
|
||||||
* Routines to handle sorting of relations into temporaries.
|
* Routines to handle sorting of relations.
|
||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.4 1996/11/08 05:56:17 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.5 1997/08/06 03:41:31 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -86,10 +86,9 @@ FormSortKeys(Sort *sortnode)
|
|||||||
* ExecSort
|
* ExecSort
|
||||||
*
|
*
|
||||||
* old comments
|
* old comments
|
||||||
* Retrieves tuples fron the outer subtree and insert them into a
|
* Sorts tuples from the outer subtree of the node in psort,
|
||||||
* temporary relation. The temporary relation is then sorted and
|
* which saves the results in a temporary file or memory. After the
|
||||||
* the sorted relation is stored in the relation whose ID is indicated
|
* initial call, returns a tuple from the file with each call.
|
||||||
* in the 'tempid' field of this node.
|
|
||||||
* Assumes that heap access method is used.
|
* Assumes that heap access method is used.
|
||||||
*
|
*
|
||||||
* Conditions:
|
* Conditions:
|
||||||
@ -108,13 +107,8 @@ ExecSort(Sort *node)
|
|||||||
ScanDirection dir;
|
ScanDirection dir;
|
||||||
int keycount;
|
int keycount;
|
||||||
ScanKey sortkeys;
|
ScanKey sortkeys;
|
||||||
Relation tempRelation;
|
|
||||||
Relation currentRelation;
|
|
||||||
HeapScanDesc currentScanDesc;
|
|
||||||
HeapTuple heapTuple;
|
HeapTuple heapTuple;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
Buffer buffer;
|
|
||||||
int tupCount = 0;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* get state info from node
|
* get state info from node
|
||||||
@ -128,10 +122,8 @@ ExecSort(Sort *node)
|
|||||||
dir = estate->es_direction;
|
dir = estate->es_direction;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* the first time we call this, we retrieve all tuples
|
* the first time we call this, psort sorts this into a file.
|
||||||
* from the subplan into a temporary relation and then
|
* Subsequent calls return tuples from psort.
|
||||||
* we sort the relation. Subsequent calls return tuples
|
|
||||||
* from the temporary relation.
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -144,78 +136,23 @@ ExecSort(Sort *node)
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
estate->es_direction = ForwardScanDirection;
|
estate->es_direction = ForwardScanDirection;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if we couldn't create the temp or current relations then
|
* prepare information for psort_begin()
|
||||||
* we print a warning and return NULL.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
tempRelation = sortstate->sort_TempRelation;
|
|
||||||
if (tempRelation == NULL) {
|
|
||||||
elog(DEBUG, "ExecSort: temp relation is NULL! aborting...");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRelation = sortstate->csstate.css_currentRelation;
|
|
||||||
if (currentRelation == NULL) {
|
|
||||||
elog(DEBUG, "ExecSort: current relation is NULL! aborting...");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* retrieve tuples from the subplan and
|
|
||||||
* insert them in the temporary relation
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
outerNode = outerPlan((Plan *) node);
|
outerNode = outerPlan((Plan *) node);
|
||||||
SO1_printf("ExecSort: %s\n",
|
|
||||||
"inserting tuples into tempRelation");
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
slot = ExecProcNode(outerNode, (Plan*)node);
|
|
||||||
|
|
||||||
if (TupIsNull(slot))
|
|
||||||
break;
|
|
||||||
|
|
||||||
tupCount++;
|
|
||||||
|
|
||||||
heapTuple = slot->val;
|
|
||||||
|
|
||||||
heap_insert(tempRelation, /* relation desc */
|
|
||||||
heapTuple); /* heap tuple to insert */
|
|
||||||
|
|
||||||
ExecClearTuple(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* now sort the tuples in our temporary relation
|
|
||||||
* into a new sorted relation using psort()
|
|
||||||
*
|
|
||||||
* psort() seems to require that the relations
|
|
||||||
* are created and opened in advance.
|
|
||||||
* -cim 1/25/90
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
keycount = node->keycount;
|
keycount = node->keycount;
|
||||||
sortkeys = (ScanKey)sortstate->sort_Keys;
|
sortkeys = (ScanKey)sortstate->sort_Keys;
|
||||||
SO1_printf("ExecSort: %s\n",
|
SO1_printf("ExecSort: %s\n",
|
||||||
"calling psort");
|
"calling psort_begin");
|
||||||
|
|
||||||
/*
|
if (!psort_begin(node, /* this node */
|
||||||
* If no tuples were fetched from the proc node return NULL now
|
keycount, /* number keys */
|
||||||
* psort dumps it if 0 tuples are in the relation and I don't want
|
sortkeys)) /* keys */
|
||||||
* to try to debug *that* routine!!
|
{
|
||||||
*/
|
/* Psort says, there are no tuples to be sorted */
|
||||||
if (tupCount == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
psort(tempRelation, /* old relation */
|
|
||||||
currentRelation, /* new relation */
|
|
||||||
keycount, /* number keys */
|
|
||||||
sortkeys); /* keys */
|
|
||||||
|
|
||||||
if (currentRelation == NULL) {
|
|
||||||
elog(DEBUG, "ExecSort: sorted relation is NULL! aborting...");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,67 +162,56 @@ ExecSort(Sort *node)
|
|||||||
*/
|
*/
|
||||||
estate->es_direction = dir;
|
estate->es_direction = dir;
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* now initialize the scan descriptor to scan the
|
|
||||||
* sorted relation and update the sortstate information
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
currentScanDesc = heap_beginscan(currentRelation, /* relation */
|
|
||||||
ScanDirectionIsBackward(dir),
|
|
||||||
/* bkwd flag */
|
|
||||||
NowTimeQual, /* time qual */
|
|
||||||
0, /* num scan keys */
|
|
||||||
NULL); /* scan keys */
|
|
||||||
|
|
||||||
sortstate->csstate.css_currentRelation = currentRelation;
|
|
||||||
sortstate->csstate.css_currentScanDesc = currentScanDesc;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* make sure the tuple descriptor is up to date
|
* make sure the tuple descriptor is up to date
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
slot = sortstate->csstate.css_ScanTupleSlot;
|
slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot;
|
||||||
|
/* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
|
||||||
slot->ttc_tupleDescriptor =
|
|
||||||
RelationGetTupleDescriptor(currentRelation);
|
slot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
|
||||||
|
#ifdef 0
|
||||||
|
slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
|
||||||
|
#endif
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* finally set the sorted flag to true
|
* finally set the sorted flag to true
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
sortstate->sort_Flag = true;
|
sortstate->sort_Flag = true;
|
||||||
|
SO1_printf(stderr, "ExecSort: sorting done.\n");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
slot = sortstate->csstate.css_ScanTupleSlot;
|
slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot;
|
||||||
|
/* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
|
||||||
|
/* slot = sortstate->csstate.css_ScanTupleSlot; orig */
|
||||||
}
|
}
|
||||||
|
|
||||||
SO1_printf("ExecSort: %s\n",
|
SO1_printf("ExecSort: %s\n",
|
||||||
"retrieveing tuple from sorted relation");
|
"retrieving tuple from sorted relation");
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* at this point we know we have a sorted relation so
|
* at this point we grab a tuple from psort
|
||||||
* we preform a simple scan on it with amgetnext()..
|
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
currentScanDesc = sortstate->csstate.css_currentScanDesc;
|
heapTuple = psort_grabtuple(node);
|
||||||
|
|
||||||
heapTuple = heap_getnext(currentScanDesc, /* scan desc */
|
if (heapTuple == NULL) {
|
||||||
ScanDirectionIsBackward(dir),
|
/* psort_end(node); */
|
||||||
/* bkwd flag */
|
return (ExecClearTuple(slot));
|
||||||
&buffer); /* return: buffer */
|
}
|
||||||
|
|
||||||
/* Increase the pin count on the buffer page, because the tuple stored in
|
ExecStoreTuple(heapTuple, /* tuple to store */
|
||||||
the slot also points to it (as well as the scan descriptor). If we
|
slot, /* slot to store in */
|
||||||
don't, ExecStoreTuple will decrease the pin count on the next iteration.
|
InvalidBuffer, /* no buffer */
|
||||||
- 01/09/93 */
|
true); /* free the palloc'd tuple */
|
||||||
|
/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/
|
||||||
if (buffer != InvalidBuffer)
|
return slot;
|
||||||
IncrBufferRefCount(buffer);
|
#if 0
|
||||||
|
return ExecStoreTuple(heapTuple, /* tuple to store */
|
||||||
return ExecStoreTuple(heapTuple, /* tuple to store */
|
slot, /* slot to store in */
|
||||||
slot, /* slot to store in */
|
InvalidBuffer, /* no buffer */
|
||||||
buffer, /* this tuple's buffer */
|
true); /* free the palloc'd tuple */
|
||||||
false); /* don't free stuff from amgetnext */
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
@ -302,11 +228,6 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
|
|||||||
SortState *sortstate;
|
SortState *sortstate;
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
ScanKey sortkeys;
|
ScanKey sortkeys;
|
||||||
TupleDesc tupType;
|
|
||||||
Oid tempOid;
|
|
||||||
Oid sortOid;
|
|
||||||
Relation tempDesc;
|
|
||||||
Relation sortedDesc;
|
|
||||||
|
|
||||||
SO1_printf("ExecInitSort: %s\n",
|
SO1_printf("ExecInitSort: %s\n",
|
||||||
"initializing sort node");
|
"initializing sort node");
|
||||||
@ -324,7 +245,7 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
|
|||||||
sortstate = makeNode(SortState);
|
sortstate = makeNode(SortState);
|
||||||
sortstate->sort_Flag = 0;
|
sortstate->sort_Flag = 0;
|
||||||
sortstate->sort_Keys = NULL;
|
sortstate->sort_Keys = NULL;
|
||||||
sortstate->sort_TempRelation = NULL;
|
node->cleaned = FALSE;
|
||||||
|
|
||||||
node->sortstate = sortstate;
|
node->sortstate = sortstate;
|
||||||
|
|
||||||
@ -348,8 +269,8 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
|
|||||||
* relation.
|
* relation.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecInitScanTupleSlot(estate, &sortstate->csstate);
|
ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
|
||||||
ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
|
ExecInitScanTupleSlot(estate, &sortstate->csstate);
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* initializes child nodes
|
* initializes child nodes
|
||||||
@ -371,41 +292,10 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
|
|||||||
* info because this node doesn't do projections.
|
* info because this node doesn't do projections.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
|
ExecAssignResultTypeFromOuterPlan((Plan *)node, &sortstate->csstate.cstate);
|
||||||
|
ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
|
||||||
sortstate->csstate.cstate.cs_ProjInfo = NULL;
|
sortstate->csstate.cstate.cs_ProjInfo = NULL;
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* get type information needed for ExecCreatR
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
tupType = ExecGetScanType(&sortstate->csstate);
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* ExecCreatR wants its second argument to be an object id of
|
|
||||||
* a relation in the range table or _TEMP_RELATION_ID_
|
|
||||||
* indicating that the relation is not in the range table.
|
|
||||||
*
|
|
||||||
* In the second case ExecCreatR creates a temp relation.
|
|
||||||
* (currently this is the only case we support -cim 10/16/89)
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
tempOid = node->tempid;
|
|
||||||
sortOid = _TEMP_RELATION_ID_;
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* create the temporary relations
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
/* len = ExecTargetListLength(node->plan.targetlist); */
|
|
||||||
tempDesc = ExecCreatR(tupType, tempOid);
|
|
||||||
sortedDesc = ExecCreatR(tupType, sortOid);
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* save the relation descriptor in the sortstate
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
sortstate->sort_TempRelation = tempDesc;
|
|
||||||
sortstate->csstate.css_currentRelation = sortedDesc;
|
|
||||||
SO1_printf("ExecInitSort: %s\n",
|
SO1_printf("ExecInitSort: %s\n",
|
||||||
"sort node initialized");
|
"sort node initialized");
|
||||||
|
|
||||||
@ -429,15 +319,12 @@ ExecCountSlotsSort(Sort *node)
|
|||||||
* ExecEndSort(node)
|
* ExecEndSort(node)
|
||||||
*
|
*
|
||||||
* old comments
|
* old comments
|
||||||
* destroys the temporary relation.
|
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecEndSort(Sort *node)
|
ExecEndSort(Sort *node)
|
||||||
{
|
{
|
||||||
SortState *sortstate;
|
SortState *sortstate;
|
||||||
Relation tempRelation;
|
|
||||||
Relation sortedRelation;
|
|
||||||
Plan *outerPlan;
|
Plan *outerPlan;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -448,18 +335,6 @@ ExecEndSort(Sort *node)
|
|||||||
"shutting down sort node");
|
"shutting down sort node");
|
||||||
|
|
||||||
sortstate = node->sortstate;
|
sortstate = node->sortstate;
|
||||||
tempRelation = sortstate->sort_TempRelation;
|
|
||||||
sortedRelation = sortstate->csstate.css_currentRelation;
|
|
||||||
|
|
||||||
heap_destroyr(tempRelation);
|
|
||||||
heap_destroyr(sortedRelation);
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
|
||||||
* close the sorted relation and shut down the scan.
|
|
||||||
* ----------------
|
|
||||||
*/
|
|
||||||
ExecCloseR((Plan *) node);
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* shut down the subplan
|
* shut down the subplan
|
||||||
@ -473,6 +348,9 @@ ExecEndSort(Sort *node)
|
|||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
|
ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
|
||||||
|
|
||||||
|
/* Clean up after psort */
|
||||||
|
psort_end(node);
|
||||||
|
|
||||||
SO1_printf("ExecEndSort: %s\n",
|
SO1_printf("ExecEndSort: %s\n",
|
||||||
"sort node shutdown");
|
"sort node shutdown");
|
||||||
@ -480,37 +358,39 @@ ExecEndSort(Sort *node)
|
|||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecSortMarkPos
|
* ExecSortMarkPos
|
||||||
|
*
|
||||||
|
* Calls psort to save the current position in the sorted file.
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecSortMarkPos(Sort *node)
|
ExecSortMarkPos(Sort *node)
|
||||||
{
|
{
|
||||||
SortState *sortstate;
|
SortState *sortstate;
|
||||||
HeapScanDesc sdesc;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if we haven't sorted yet, just return
|
* if we haven't sorted yet, just return
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
sortstate = node->sortstate;
|
sortstate = node->sortstate;
|
||||||
if (sortstate->sort_Flag == false)
|
if (sortstate->sort_Flag == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sdesc = sortstate->csstate.css_currentScanDesc;
|
psort_markpos(node);
|
||||||
heap_markpos(sdesc);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecSortRestrPos
|
* ExecSortRestrPos
|
||||||
|
*
|
||||||
|
* Calls psort to restore the last saved sort file position.
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecSortRestrPos(Sort *node)
|
ExecSortRestrPos(Sort *node)
|
||||||
{
|
{
|
||||||
SortState *sortstate;
|
SortState *sortstate;
|
||||||
HeapScanDesc sdesc;
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* if we haven't sorted yet, just return.
|
* if we haven't sorted yet, just return.
|
||||||
* ----------------
|
* ----------------
|
||||||
@ -520,9 +400,8 @@ ExecSortRestrPos(Sort *node)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* restore the scan to the previously marked position
|
* restore the scan to the previously marked position
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
sdesc = sortstate->csstate.css_currentScanDesc;
|
psort_restorepos(node);
|
||||||
heap_restrpos(sdesc);
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.36 1997/07/29 16:14:40 thomas Exp $
|
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.37 1997/08/06 03:41:41 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* this is the "main" module of the postgres backend and
|
||||||
@ -108,6 +108,7 @@ extern int lockingOff;
|
|||||||
extern int NBuffers;
|
extern int NBuffers;
|
||||||
|
|
||||||
int fsyncOff = 0;
|
int fsyncOff = 0;
|
||||||
|
int SortMem = 512;
|
||||||
|
|
||||||
int dontExecute = 0;
|
int dontExecute = 0;
|
||||||
static int ShowStats;
|
static int ShowStats;
|
||||||
@ -1038,7 +1039,16 @@ PostgresMain(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
flagQ = 1;
|
flagQ = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
/* ----------------
|
||||||
|
* S - amount of sort memory to use in 1k bytes
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
SortMem = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef NOT_USED
|
||||||
case 'S':
|
case 'S':
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* S - assume stable main memory
|
* S - assume stable main memory
|
||||||
@ -1048,6 +1058,7 @@ PostgresMain(int argc, char *argv[])
|
|||||||
flagS = 1;
|
flagS = 1;
|
||||||
SetTransactionFlushEnabled(false);
|
SetTransactionFlushEnabled(false);
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
/* ----------------
|
/* ----------------
|
||||||
@ -1173,6 +1184,7 @@ PostgresMain(int argc, char *argv[])
|
|||||||
printf("\ttimings = %c\n", ShowStats ? 't' : 'f');
|
printf("\ttimings = %c\n", ShowStats ? 't' : 'f');
|
||||||
printf("\tdates = %s\n", EuroDates ? "European" : "Normal");
|
printf("\tdates = %s\n", EuroDates ? "European" : "Normal");
|
||||||
printf("\tbufsize = %d\n", NBuffers);
|
printf("\tbufsize = %d\n", NBuffers);
|
||||||
|
printf("\tsortmem = %d\n", SortMem);
|
||||||
|
|
||||||
printf("\tquery echo = %c\n", EchoQuery ? 't' : 'f');
|
printf("\tquery echo = %c\n", EchoQuery ? 't' : 'f');
|
||||||
printf("\tmultiplexed backend? = %c\n", multiplexedBackend ? 't' : 'f');
|
printf("\tmultiplexed backend? = %c\n", multiplexedBackend ? 't' : 'f');
|
||||||
@ -1280,7 +1292,7 @@ PostgresMain(int argc, char *argv[])
|
|||||||
*/
|
*/
|
||||||
if (IsUnderPostmaster == false) {
|
if (IsUnderPostmaster == false) {
|
||||||
puts("\nPOSTGRES backend interactive interface");
|
puts("\nPOSTGRES backend interactive interface");
|
||||||
puts("$Revision: 1.36 $ $Date: 1997/07/29 16:14:40 $");
|
puts("$Revision: 1.37 $ $Date: 1997/08/06 03:41:41 $");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.3 1997/05/20 11:35:48 vadim Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.4 1997/08/06 03:41:47 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,37 +26,14 @@
|
|||||||
#include "utils/psort.h"
|
#include "utils/psort.h"
|
||||||
#include "utils/lselect.h"
|
#include "utils/lselect.h"
|
||||||
|
|
||||||
extern Relation SortRdesc; /* later static */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PUTTUP - writes the next tuple
|
|
||||||
* ENDRUN - mark end of run
|
|
||||||
* GETLEN - reads the length of the next tuple
|
|
||||||
* ALLOCTUP - returns space for the new tuple
|
|
||||||
* SETTUPLEN - stores the length into the tuple
|
|
||||||
* GETTUP - reads the tuple
|
|
||||||
*
|
|
||||||
* Note:
|
|
||||||
* LEN field must be a short; FP is a stream
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PUTTUP(TUP, FP) fwrite((char *)TUP, (TUP)->t_len, 1, FP)
|
#define PUTTUP(TUP, FP) fwrite((char *)TUP, (TUP)->t_len, 1, FP)
|
||||||
#define ENDRUN(FP) fwrite((char *)&shortzero, sizeof (shortzero), 1, FP)
|
|
||||||
#define GETLEN(LEN, FP) fread(&(LEN), sizeof (shortzero), 1, FP)
|
|
||||||
#define ALLOCTUP(LEN) ((HeapTuple)palloc((unsigned)LEN))
|
|
||||||
#define GETTUP(TUP, LEN, FP)\
|
|
||||||
fread((char *)(TUP) + sizeof (shortzero), 1, (LEN) - sizeof (shortzero), FP)
|
|
||||||
#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* USEMEM - record use of memory
|
* USEMEM - record use of memory
|
||||||
* FREEMEM - record freeing of memory
|
* FREEMEM - record freeing of memory
|
||||||
* FULLMEM - 1 iff a tuple will fit
|
|
||||||
*/
|
*/
|
||||||
|
#define USEMEM(context,AMT) context->sortMem -= (AMT)
|
||||||
#define USEMEM(AMT) SortMemory -= (AMT)
|
#define FREEMEM(context,AMT) context->sortMem += (AMT)
|
||||||
#define FREEMEM(AMT) SortMemory += (AMT)
|
|
||||||
#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lmerge - merges two leftist trees into one
|
* lmerge - merges two leftist trees into one
|
||||||
@ -67,12 +44,12 @@ extern Relation SortRdesc; /* later static */
|
|||||||
* speed up code significantly.
|
* speed up code significantly.
|
||||||
*/
|
*/
|
||||||
struct leftist *
|
struct leftist *
|
||||||
lmerge(struct leftist *pt, struct leftist *qt)
|
lmerge(struct leftist *pt, struct leftist *qt, LeftistContext context)
|
||||||
{
|
{
|
||||||
register struct leftist *root, *majorLeftist, *minorLeftist;
|
register struct leftist *root, *majorLeftist, *minorLeftist;
|
||||||
int dist;
|
int dist;
|
||||||
|
|
||||||
if (tuplecmp(pt->lt_tuple, qt->lt_tuple)) {
|
if (tuplecmp(pt->lt_tuple, qt->lt_tuple, context)) {
|
||||||
root = pt;
|
root = pt;
|
||||||
majorLeftist = qt;
|
majorLeftist = qt;
|
||||||
} else {
|
} else {
|
||||||
@ -83,7 +60,7 @@ lmerge(struct leftist *pt, struct leftist *qt)
|
|||||||
root->lt_left = majorLeftist;
|
root->lt_left = majorLeftist;
|
||||||
else {
|
else {
|
||||||
if ((minorLeftist = root->lt_right) != NULL)
|
if ((minorLeftist = root->lt_right) != NULL)
|
||||||
majorLeftist = lmerge(majorLeftist, minorLeftist);
|
majorLeftist = lmerge(majorLeftist, minorLeftist, context);
|
||||||
if ((dist = root->lt_left->lt_dist) < majorLeftist->lt_dist) {
|
if ((dist = root->lt_left->lt_dist) < majorLeftist->lt_dist) {
|
||||||
root->lt_dist = 1 + dist;
|
root->lt_dist = 1 + dist;
|
||||||
root->lt_right = root->lt_left;
|
root->lt_right = root->lt_left;
|
||||||
@ -97,11 +74,11 @@ lmerge(struct leftist *pt, struct leftist *qt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct leftist *
|
static struct leftist *
|
||||||
linsert(struct leftist *root, struct leftist *new1)
|
linsert(struct leftist *root, struct leftist *new1, LeftistContext context)
|
||||||
{
|
{
|
||||||
register struct leftist *left, *right;
|
register struct leftist *left, *right;
|
||||||
|
|
||||||
if (! tuplecmp(root->lt_tuple, new1->lt_tuple)) {
|
if (! tuplecmp(root->lt_tuple, new1->lt_tuple, context)) {
|
||||||
new1->lt_left = root;
|
new1->lt_left = root;
|
||||||
return(new1);
|
return(new1);
|
||||||
}
|
}
|
||||||
@ -116,7 +93,7 @@ linsert(struct leftist *root, struct leftist *new1)
|
|||||||
}
|
}
|
||||||
return(root);
|
return(root);
|
||||||
}
|
}
|
||||||
right = linsert(right, new1);
|
right = linsert(right, new1, context);
|
||||||
if (right->lt_dist < left->lt_dist) {
|
if (right->lt_dist < left->lt_dist) {
|
||||||
root->lt_dist = 1 + left->lt_dist;
|
root->lt_dist = 1 + left->lt_dist;
|
||||||
root->lt_left = right;
|
root->lt_left = right;
|
||||||
@ -142,7 +119,8 @@ linsert(struct leftist *root, struct leftist *new1)
|
|||||||
*/
|
*/
|
||||||
HeapTuple
|
HeapTuple
|
||||||
gettuple(struct leftist **treep,
|
gettuple(struct leftist **treep,
|
||||||
short *devnum) /* device from which tuple came */
|
short *devnum, /* device from which tuple came */
|
||||||
|
LeftistContext context)
|
||||||
{
|
{
|
||||||
register struct leftist *tp;
|
register struct leftist *tp;
|
||||||
HeapTuple tup;
|
HeapTuple tup;
|
||||||
@ -153,9 +131,9 @@ gettuple(struct leftist **treep,
|
|||||||
if (tp->lt_dist == 1) /* lt_left == NULL */
|
if (tp->lt_dist == 1) /* lt_left == NULL */
|
||||||
*treep = tp->lt_left;
|
*treep = tp->lt_left;
|
||||||
else
|
else
|
||||||
*treep = lmerge(tp->lt_left, tp->lt_right);
|
*treep = lmerge(tp->lt_left, tp->lt_right, context);
|
||||||
|
|
||||||
FREEMEM(sizeof (struct leftist));
|
FREEMEM(context,sizeof (struct leftist));
|
||||||
FREE(tp);
|
FREE(tp);
|
||||||
return(tup);
|
return(tup);
|
||||||
}
|
}
|
||||||
@ -169,14 +147,17 @@ gettuple(struct leftist **treep,
|
|||||||
* Note:
|
* Note:
|
||||||
* Currently never returns NULL BUG
|
* Currently never returns NULL BUG
|
||||||
*/
|
*/
|
||||||
int
|
void
|
||||||
puttuple(struct leftist **treep, HeapTuple newtuple, int devnum)
|
puttuple(struct leftist **treep,
|
||||||
|
HeapTuple newtuple,
|
||||||
|
short devnum,
|
||||||
|
LeftistContext context)
|
||||||
{
|
{
|
||||||
register struct leftist *new1;
|
register struct leftist *new1;
|
||||||
register struct leftist *tp;
|
register struct leftist *tp;
|
||||||
|
|
||||||
new1 = (struct leftist *) palloc((unsigned) sizeof (struct leftist));
|
new1 = (struct leftist *) palloc((unsigned) sizeof (struct leftist));
|
||||||
USEMEM(sizeof (struct leftist));
|
USEMEM(context,sizeof (struct leftist));
|
||||||
new1->lt_dist = 1;
|
new1->lt_dist = 1;
|
||||||
new1->lt_devnum = devnum;
|
new1->lt_devnum = devnum;
|
||||||
new1->lt_tuple = newtuple;
|
new1->lt_tuple = newtuple;
|
||||||
@ -185,38 +166,11 @@ puttuple(struct leftist **treep, HeapTuple newtuple, int devnum)
|
|||||||
if ((tp = *treep) == NULL)
|
if ((tp = *treep) == NULL)
|
||||||
*treep = new1;
|
*treep = new1;
|
||||||
else
|
else
|
||||||
*treep = linsert(tp, new1);
|
*treep = linsert(tp, new1, context);
|
||||||
return(1);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* dumptuples - stores all the tuples in tree into file
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
dumptuples(FILE *file)
|
|
||||||
{
|
|
||||||
register struct leftist *tp;
|
|
||||||
register struct leftist *newp;
|
|
||||||
HeapTuple tup;
|
|
||||||
|
|
||||||
tp = Tuples;
|
|
||||||
while (tp != NULL) {
|
|
||||||
tup = tp->lt_tuple;
|
|
||||||
if (tp->lt_dist == 1) /* lt_right == NULL */
|
|
||||||
newp = tp->lt_left;
|
|
||||||
else
|
|
||||||
newp = lmerge(tp->lt_left, tp->lt_right);
|
|
||||||
FREEMEM(sizeof (struct leftist));
|
|
||||||
FREE(tp);
|
|
||||||
PUTTUP(tup, file);
|
|
||||||
FREEMEM(tup->t_len);
|
|
||||||
FREE(tup);
|
|
||||||
tp = newp;
|
|
||||||
}
|
|
||||||
Tuples = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* tuplecmp - Compares two tuples with respect CmpList
|
* tuplecmp - Compares two tuples with respect CmpList
|
||||||
*
|
*
|
||||||
@ -225,7 +179,7 @@ dumptuples(FILE *file)
|
|||||||
* Assumtions:
|
* Assumtions:
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
tuplecmp(HeapTuple ltup, HeapTuple rtup)
|
tuplecmp(HeapTuple ltup, HeapTuple rtup, LeftistContext context)
|
||||||
{
|
{
|
||||||
register char *lattr, *rattr;
|
register char *lattr, *rattr;
|
||||||
int nkey = 0;
|
int nkey = 0;
|
||||||
@ -238,24 +192,27 @@ tuplecmp(HeapTuple ltup, HeapTuple rtup)
|
|||||||
return(0);
|
return(0);
|
||||||
if (rtup == (HeapTuple)NULL)
|
if (rtup == (HeapTuple)NULL)
|
||||||
return(1);
|
return(1);
|
||||||
while (nkey < Nkeys && !result) {
|
while (nkey < context->nKeys && !result) {
|
||||||
lattr = heap_getattr(ltup, InvalidBuffer,
|
lattr = heap_getattr(ltup, InvalidBuffer,
|
||||||
Key[nkey].sk_attno,
|
context->scanKeys[nkey].sk_attno,
|
||||||
RelationGetTupleDescriptor(SortRdesc),
|
context->tupDesc, &isnull);
|
||||||
&isnull);
|
|
||||||
if (isnull)
|
if (isnull)
|
||||||
return(0);
|
return(0);
|
||||||
rattr = heap_getattr(rtup, InvalidBuffer,
|
rattr = heap_getattr(rtup, InvalidBuffer,
|
||||||
Key[nkey].sk_attno,
|
context->scanKeys[nkey].sk_attno,
|
||||||
RelationGetTupleDescriptor(SortRdesc),
|
context->tupDesc,
|
||||||
&isnull);
|
&isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
return(1);
|
return(1);
|
||||||
if (Key[nkey].sk_flags & SK_COMMUTE) {
|
if (context->scanKeys[nkey].sk_flags & SK_COMMUTE) {
|
||||||
if (!(result = (long) (*Key[nkey].sk_func) (rattr, lattr)))
|
if (!(result =
|
||||||
result = -(long) (*Key[nkey].sk_func) (lattr, rattr);
|
(long) (*context->scanKeys[nkey].sk_func) (rattr, lattr)))
|
||||||
} else if (!(result = (long) (*Key[nkey].sk_func) (lattr, rattr)))
|
result =
|
||||||
result = -(long) (*Key[nkey].sk_func) (rattr, lattr);
|
-(long) (*context->scanKeys[nkey].sk_func) (lattr, rattr);
|
||||||
|
} else if (!(result =
|
||||||
|
(long) (*context->scanKeys[nkey].sk_func) (lattr, rattr)))
|
||||||
|
result =
|
||||||
|
-(long) (*context->scanKeys[nkey].sk_func) (rattr, lattr);
|
||||||
nkey++;
|
nkey++;
|
||||||
}
|
}
|
||||||
return (result == 1);
|
return (result == 1);
|
||||||
@ -263,7 +220,7 @@ tuplecmp(HeapTuple ltup, HeapTuple rtup)
|
|||||||
|
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
void
|
void
|
||||||
checktree(struct leftist *tree)
|
checktree(struct leftist *tree, LeftistContext context)
|
||||||
{
|
{
|
||||||
int lnodes;
|
int lnodes;
|
||||||
int rnodes;
|
int rnodes;
|
||||||
@ -272,8 +229,8 @@ checktree(struct leftist *tree)
|
|||||||
puts("Null tree.");
|
puts("Null tree.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lnodes = checktreer(tree->lt_left, 1);
|
lnodes = checktreer(tree->lt_left, 1, context);
|
||||||
rnodes = checktreer(tree->lt_right, 1);
|
rnodes = checktreer(tree->lt_right, 1, context);
|
||||||
if (lnodes < 0) {
|
if (lnodes < 0) {
|
||||||
lnodes = -lnodes;
|
lnodes = -lnodes;
|
||||||
puts("0:\tBad left side.");
|
puts("0:\tBad left side.");
|
||||||
@ -297,24 +254,24 @@ checktree(struct leftist *tree)
|
|||||||
} else if (tree->lt_dist != 1+ tree->lt_right->lt_dist)
|
} else if (tree->lt_dist != 1+ tree->lt_right->lt_dist)
|
||||||
puts("0:\tDistance incorrect.");
|
puts("0:\tDistance incorrect.");
|
||||||
if (lnodes > 0)
|
if (lnodes > 0)
|
||||||
if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple))
|
if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple, context))
|
||||||
printf("%d:\tLeft child < parent.\n");
|
printf("%d:\tLeft child < parent.\n");
|
||||||
if (rnodes > 0)
|
if (rnodes > 0)
|
||||||
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple))
|
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple, context))
|
||||||
printf("%d:\tRight child < parent.\n");
|
printf("%d:\tRight child < parent.\n");
|
||||||
printf("Tree has %d nodes\n", 1 + lnodes + rnodes);
|
printf("Tree has %d nodes\n", 1 + lnodes + rnodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
checktreer(struct leftist *tree, int level)
|
checktreer(struct leftist *tree, int level, LeftistContext context)
|
||||||
{
|
{
|
||||||
int lnodes, rnodes;
|
int lnodes, rnodes;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
if (tree == NULL)
|
if (tree == NULL)
|
||||||
return(0);
|
return(0);
|
||||||
lnodes = checktreer(tree->lt_left, level + 1);
|
lnodes = checktreer(tree->lt_left, level + 1, context);
|
||||||
rnodes = checktreer(tree->lt_right, level + 1);
|
rnodes = checktreer(tree->lt_right, level + 1, context);
|
||||||
if (lnodes < 0) {
|
if (lnodes < 0) {
|
||||||
error = 1;
|
error = 1;
|
||||||
lnodes = -lnodes;
|
lnodes = -lnodes;
|
||||||
@ -349,12 +306,12 @@ checktreer(struct leftist *tree, int level)
|
|||||||
printf("%d:\tDistance incorrect.\n", level);
|
printf("%d:\tDistance incorrect.\n", level);
|
||||||
}
|
}
|
||||||
if (lnodes > 0)
|
if (lnodes > 0)
|
||||||
if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple)) {
|
if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple, context)) {
|
||||||
error = 1;
|
error = 1;
|
||||||
printf("%d:\tLeft child < parent.\n");
|
printf("%d:\tLeft child < parent.\n");
|
||||||
}
|
}
|
||||||
if (rnodes > 0)
|
if (rnodes > 0)
|
||||||
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple)) {
|
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple, context)) {
|
||||||
error = 1;
|
error = 1;
|
||||||
printf("%d:\tRight child < parent.\n");
|
printf("%d:\tRight child < parent.\n");
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,25 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.5 1997/07/24 20:18:07 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.6 1997/08/06 03:41:55 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Sorts the first relation into the second relation. The sort may
|
* Sorts the first relation into the second relation.
|
||||||
* not be called twice simultaneously.
|
*
|
||||||
|
* The old psort.c's routines formed a temporary relation from the merged
|
||||||
|
* sort files. This version keeps the files around instead of generating the
|
||||||
|
* relation from them, and provides interface functions to the file so that
|
||||||
|
* you can grab tuples, mark a position in the file, restore a position in the
|
||||||
|
* file. You must now explicitly call an interface function to end the sort,
|
||||||
|
* psort_end, when you are done.
|
||||||
|
* Now most of the global variables are stuck in the Sort nodes, and
|
||||||
|
* accessed from there (they are passed to all the psort routines) so that
|
||||||
|
* each sort running has its own separate state. This is facilitated by having
|
||||||
|
* the Sort nodes passed in to all the interface functions.
|
||||||
|
* The one global variable that all the sorts still share is SortMemory.
|
||||||
|
* You should now be allowed to run two or more psorts concurrently,
|
||||||
|
* so long as the memory they eat up is not greater than SORTMEM, the initial
|
||||||
|
* value of SortMemory. -Rex 2.15.1995
|
||||||
*
|
*
|
||||||
* Use the tape-splitting method (Knuth, Vol. III, pp281-86) in the future.
|
* Use the tape-splitting method (Knuth, Vol. III, pp281-86) in the future.
|
||||||
*
|
*
|
||||||
@ -21,7 +35,6 @@
|
|||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
@ -35,120 +48,150 @@
|
|||||||
#include "storage/buf.h"
|
#include "storage/buf.h"
|
||||||
#include "storage/bufmgr.h" /* for BLCKSZ */
|
#include "storage/bufmgr.h" /* for BLCKSZ */
|
||||||
#include "utils/portal.h" /* for {Start,End}PortalAllocMode */
|
#include "utils/portal.h" /* for {Start,End}PortalAllocMode */
|
||||||
|
#include "utils/elog.h"
|
||||||
#include "utils/rel.h"
|
#include "utils/rel.h"
|
||||||
|
|
||||||
#include "utils/psort.h"
|
#include "nodes/execnodes.h"
|
||||||
|
#include "nodes/plannodes.h"
|
||||||
|
#include "executor/executor.h"
|
||||||
|
|
||||||
#include "utils/lselect.h"
|
#include "utils/lselect.h"
|
||||||
|
#include "utils/psort.h"
|
||||||
|
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
|
|
||||||
#ifndef HAVE_MEMMOVE
|
|
||||||
# include <regex/utils.h>
|
|
||||||
#else
|
|
||||||
# include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TEMPDIR "./"
|
#define TEMPDIR "./"
|
||||||
|
|
||||||
int Nkeys;
|
extern int SortMem; /* defined as postgres option */
|
||||||
ScanKey Key;
|
static long shortzero = 0; /* used to delimit runs */
|
||||||
int SortMemory;
|
|
||||||
|
|
||||||
static int TapeRange; /* number of tapes - 1 (T) */
|
|
||||||
static int Level; /* (l) */
|
|
||||||
static int TotalDummy; /* summation of tp_dummy */
|
|
||||||
static struct tape Tape[MAXTAPES];
|
|
||||||
static long shortzero = 0; /* used to delimit runs */
|
|
||||||
static struct tuple *LastTuple = NULL; /* last output */
|
|
||||||
|
|
||||||
static int BytesRead; /* to keep track of # of IO */
|
|
||||||
static int BytesWritten;
|
|
||||||
|
|
||||||
Relation SortRdesc; /* current tuples in memory */
|
|
||||||
struct leftist *Tuples; /* current tuples in memory */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* psort - polyphase merge sort entry point
|
* old psort global variables
|
||||||
|
*
|
||||||
|
* (These are the global variables from the old psort. They are still used,
|
||||||
|
* but are now accessed from Sort nodes using the PS macro. Note that while
|
||||||
|
* these variables will be accessed by PS(node)->whatever, they will still
|
||||||
|
* be called by their original names within the comments! -Rex 2.10.1995)
|
||||||
|
*
|
||||||
|
* LeftistContextData treeContext;
|
||||||
|
*
|
||||||
|
* static int TapeRange; // number of tapes - 1 (T) //
|
||||||
|
* static int Level; // (l) //
|
||||||
|
* static int TotalDummy; // summation of tp_dummy //
|
||||||
|
* static struct tape *Tape;
|
||||||
|
*
|
||||||
|
* static int BytesRead; // to keep track of # of IO //
|
||||||
|
* static int BytesWritten;
|
||||||
|
*
|
||||||
|
* struct leftist *Tuples; // current tuples in memory //
|
||||||
|
*
|
||||||
|
* FILE *psort_grab_file; // this holds tuples grabbed
|
||||||
|
* from merged sort runs //
|
||||||
|
* long psort_current; // current file position //
|
||||||
|
* long psort_saved; // file position saved for
|
||||||
|
* mark and restore //
|
||||||
*/
|
*/
|
||||||
void
|
|
||||||
psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key)
|
/*
|
||||||
|
* PS - Macro to access and cast psortstate from a Sort node
|
||||||
|
*/
|
||||||
|
#define PS(N) ((Psortstate *)N->psortstate)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* psort_begin - polyphase merge sort entry point. Sorts the subplan
|
||||||
|
* into a temporary file psort_grab_file. After
|
||||||
|
* this is called, calling the interface function
|
||||||
|
* psort_grabtuple iteratively will get you the sorted
|
||||||
|
* tuples. psort_end then finishes the sort off, after
|
||||||
|
* all the tuples have been grabbed.
|
||||||
|
*
|
||||||
|
* Allocates and initializes sort node's psort state.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
psort_begin(Sort *node, int nkeys, ScanKey key)
|
||||||
{
|
{
|
||||||
|
bool empty; /* to answer: is child node empty? */
|
||||||
|
|
||||||
|
node->psortstate = (struct Psortstate *)palloc(sizeof(struct Psortstate));
|
||||||
|
if (node->psortstate == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
AssertArg(nkeys >= 1);
|
AssertArg(nkeys >= 1);
|
||||||
AssertArg(key[0].sk_attno != 0);
|
AssertArg(key[0].sk_attno != 0);
|
||||||
AssertArg(key[0].sk_procedure != 0);
|
AssertArg(key[0].sk_procedure != 0);
|
||||||
|
|
||||||
Nkeys = nkeys;
|
PS(node)->BytesRead = 0;
|
||||||
Key = key;
|
PS(node)->BytesWritten = 0;
|
||||||
SortMemory = 0;
|
PS(node)->treeContext.tupDesc =
|
||||||
SortRdesc = oldrel;
|
ExecGetTupType(outerPlan((Plan *)node));
|
||||||
BytesRead = 0;
|
PS(node)->treeContext.nKeys = nkeys;
|
||||||
BytesWritten = 0;
|
PS(node)->treeContext.scanKeys = key;
|
||||||
/*
|
PS(node)->treeContext.sortMem = SortMem;
|
||||||
* may not be the best place.
|
|
||||||
*
|
PS(node)->Tuples = NULL;
|
||||||
* Pass 0 for the "limit" as the argument is currently ignored.
|
PS(node)->tupcount = 0;
|
||||||
* Previously, only one arg was passed. -mer 12 Nov. 1991
|
|
||||||
*/
|
PS(node)->using_tape_files = false;
|
||||||
StartPortalAllocMode(StaticAllocMode, (Size)0);
|
PS(node)->memtuples = NULL;
|
||||||
initpsort();
|
|
||||||
initialrun(oldrel);
|
initialrun(node, &empty);
|
||||||
/* call finalrun(newrel, mergerun()) instead */
|
|
||||||
endpsort(newrel, mergeruns());
|
if (empty)
|
||||||
EndPortalAllocMode();
|
return false;
|
||||||
NDirectFileRead += (int)ceil((double)BytesRead / BLCKSZ);
|
|
||||||
NDirectFileWrite += (int)ceil((double)BytesWritten / BLCKSZ);
|
if (PS(node)->using_tape_files)
|
||||||
|
PS(node)->psort_grab_file = mergeruns(node);
|
||||||
|
|
||||||
|
PS(node)->psort_current = 0;
|
||||||
|
PS(node)->psort_saved = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TAPENO - number of tape in Tape
|
* inittapes - initializes the tapes
|
||||||
*/
|
|
||||||
|
|
||||||
#define TAPENO(NODE) (NODE - Tape)
|
|
||||||
#define TUPLENO(TUP) ((TUP == NULL) ? -1 : (int) TUP->t_iid)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* initpsort - initializes the tapes
|
|
||||||
* - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
|
* - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
|
||||||
* Returns:
|
* Returns:
|
||||||
* number of allocated tapes
|
* number of allocated tapes
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
initpsort()
|
inittapes(Sort *node)
|
||||||
{
|
{
|
||||||
register int i;
|
register int i;
|
||||||
register struct tape *tp;
|
register struct tape *tp;
|
||||||
|
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ASSERT(ntapes >= 3 && ntapes <= MAXTAPES,
|
ASSERT(ntapes >= 3 && ntapes <= MAXTAPES,
|
||||||
"initpsort: Invalid number of tapes to initialize.\n");
|
"inittapes: Invalid number of tapes to initialize.\n");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
tp = Tape;
|
tp = PS(node)->Tape;
|
||||||
for (i = 0; i < MAXTAPES && (tp->tp_file = gettape()) != NULL; i++) {
|
for (i = 0; i < MAXTAPES && (tp->tp_file = gettape()) != NULL; i++) {
|
||||||
tp->tp_dummy = 1;
|
tp->tp_dummy = 1;
|
||||||
tp->tp_fib = 1;
|
tp->tp_fib = 1;
|
||||||
tp->tp_prev = tp - 1;
|
tp->tp_prev = tp - 1;
|
||||||
tp++;
|
tp++;
|
||||||
}
|
}
|
||||||
TapeRange = --tp - Tape;
|
PS(node)->TapeRange = --tp - PS(node)->Tape;
|
||||||
tp->tp_dummy = 0;
|
tp->tp_dummy = 0;
|
||||||
tp->tp_fib = 0;
|
tp->tp_fib = 0;
|
||||||
Tape[0].tp_prev = tp;
|
PS(node)->Tape[0].tp_prev = tp;
|
||||||
|
|
||||||
if (TapeRange <= 1)
|
if (PS(node)->TapeRange <= 1)
|
||||||
elog(WARN, "initpsort: Could only allocate %d < 3 tapes\n",
|
elog(WARN, "inittapes: Could only allocate %d < 3 tapes\n",
|
||||||
TapeRange + 1);
|
PS(node)->TapeRange + 1);
|
||||||
|
|
||||||
Level = 1;
|
PS(node)->Level = 1;
|
||||||
TotalDummy = TapeRange;
|
PS(node)->TotalDummy = PS(node)->TapeRange;
|
||||||
|
|
||||||
SortMemory = SORTMEM;
|
PS(node)->using_tape_files = true;
|
||||||
LastTuple = NULL;
|
|
||||||
Tuples = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* resetpsort - resets (frees) malloc'd memory for an aborted Xaction
|
* resetpsort - resets (pfrees) palloc'd memory for an aborted Xaction
|
||||||
*
|
*
|
||||||
* Not implemented yet.
|
* Not implemented yet.
|
||||||
*/
|
*/
|
||||||
@ -170,16 +213,18 @@ resetpsort()
|
|||||||
* LEN field must be a short; FP is a stream
|
* LEN field must be a short; FP is a stream
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define PUTTUP(TUP, FP)\
|
|
||||||
BytesWritten += (TUP)->t_len; \
|
#define PUTTUP(NODE, TUP, FP) if (1) {\
|
||||||
fwrite((char *)TUP, (TUP)->t_len, 1, FP)
|
((Psortstate *)NODE->psortstate)->BytesWritten += (TUP)->t_len; \
|
||||||
|
fwrite((char *)TUP, (TUP)->t_len, 1, FP);} else
|
||||||
#define ENDRUN(FP) fwrite((char *)&shortzero, sizeof (shortzero), 1, FP)
|
#define ENDRUN(FP) fwrite((char *)&shortzero, sizeof (shortzero), 1, FP)
|
||||||
#define GETLEN(LEN, FP) fread((char *)&(LEN), sizeof (shortzero), 1, FP)
|
#define GETLEN(LEN, FP) fread((char *)&(LEN), sizeof (shortzero), 1, FP)
|
||||||
#define ALLOCTUP(LEN) ((HeapTuple)palloc((unsigned)LEN))
|
#define ALLOCTUP(LEN) ((HeapTuple)palloc((unsigned)LEN))
|
||||||
#define GETTUP(TUP, LEN, FP)\
|
#define GETTUP(NODE, TUP, LEN, FP) if (1) {\
|
||||||
IncrProcessed(); \
|
IncrProcessed(); \
|
||||||
BytesRead += (LEN) - sizeof (shortzero); \
|
((Psortstate *)NODE->psortstate)->BytesRead += (LEN) - sizeof (shortzero); \
|
||||||
fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP)
|
fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP);} \
|
||||||
|
else
|
||||||
#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN
|
#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -188,9 +233,9 @@ resetpsort()
|
|||||||
* FULLMEM - 1 iff a tuple will fit
|
* FULLMEM - 1 iff a tuple will fit
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define USEMEM(AMT) SortMemory -= (AMT)
|
#define USEMEM(NODE,AMT) PS(node)->treeContext.sortMem -= (AMT)
|
||||||
#define FREEMEM(AMT) SortMemory += (AMT)
|
#define FREEMEM(NODE,AMT) PS(node)->treeContext.sortMem += (AMT)
|
||||||
#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */
|
#define LACKMEM(NODE) (PS(node)->treeContext.sortMem <= MAXBLCKSZ) /* not accurate */
|
||||||
#define TRACEMEM(FUNC)
|
#define TRACEMEM(FUNC)
|
||||||
#define TRACEOUT(FUNC, TUP)
|
#define TRACEOUT(FUNC, TUP)
|
||||||
|
|
||||||
@ -219,61 +264,66 @@ resetpsort()
|
|||||||
* Also, perhaps allocate tapes when needed. Split into 2 funcs.
|
* Also, perhaps allocate tapes when needed. Split into 2 funcs.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
initialrun(Relation rdesc)
|
initialrun(Sort *node, bool *empty)
|
||||||
{
|
{
|
||||||
/* register struct tuple *tup; */
|
/* register struct tuple *tup; */
|
||||||
register struct tape *tp;
|
register struct tape *tp;
|
||||||
HeapScanDesc sdesc;
|
|
||||||
int baseruns; /* D:(a) */
|
int baseruns; /* D:(a) */
|
||||||
int morepasses; /* EOF */
|
int extrapasses; /* EOF */
|
||||||
|
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
|
tp = PS(node)->Tape;
|
||||||
|
|
||||||
sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0,
|
if ((bool)createrun(node, tp->tp_file, empty) != false) {
|
||||||
(ScanKey)NULL);
|
if (! PS(node)->using_tape_files)
|
||||||
tp = Tape;
|
inittapes(node);
|
||||||
|
extrapasses = 0;
|
||||||
if ((bool)createrun(sdesc, tp->tp_file) != false)
|
}
|
||||||
morepasses = 0;
|
|
||||||
else
|
else
|
||||||
morepasses = 1 + (Tuples != NULL); /* (T != N) ? 2 : 1 */
|
return; /* if rows fit in memory, we never access tape stuff */
|
||||||
|
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
tp->tp_dummy--;
|
tp->tp_dummy--;
|
||||||
TotalDummy--;
|
PS(node)->TotalDummy--;
|
||||||
if (tp->tp_dummy < (tp + 1)->tp_dummy)
|
if (tp->tp_dummy < (tp + 1)->tp_dummy)
|
||||||
tp++;
|
tp++;
|
||||||
else if (tp->tp_dummy != 0)
|
else if (tp->tp_dummy != 0)
|
||||||
tp = Tape;
|
tp = PS(node)->Tape;
|
||||||
else {
|
else {
|
||||||
Level++;
|
PS(node)->Level++;
|
||||||
baseruns = Tape[0].tp_fib;
|
baseruns = PS(node)->Tape[0].tp_fib;
|
||||||
for (tp = Tape; tp - Tape < TapeRange; tp++) {
|
for (tp = PS(node)->Tape;
|
||||||
TotalDummy +=
|
tp - PS(node)->Tape < PS(node)->TapeRange; tp++) {
|
||||||
|
PS(node)->TotalDummy +=
|
||||||
(tp->tp_dummy = baseruns
|
(tp->tp_dummy = baseruns
|
||||||
+ (tp + 1)->tp_fib
|
+ (tp + 1)->tp_fib
|
||||||
- tp->tp_fib);
|
- tp->tp_fib);
|
||||||
tp->tp_fib = baseruns
|
tp->tp_fib = baseruns
|
||||||
+ (tp + 1)->tp_fib;
|
+ (tp + 1)->tp_fib;
|
||||||
}
|
}
|
||||||
tp = Tape; /* D4 */
|
tp = PS(node)->Tape; /* D4 */
|
||||||
} /* D3 */
|
} /* D3 */
|
||||||
if (morepasses)
|
if (extrapasses)
|
||||||
if (--morepasses) {
|
if (--extrapasses) {
|
||||||
dumptuples(tp->tp_file);
|
dumptuples(node);
|
||||||
ENDRUN(tp->tp_file);
|
ENDRUN(tp->tp_file);
|
||||||
continue;
|
continue;
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
if ((bool)createrun(sdesc, tp->tp_file) == false)
|
|
||||||
morepasses = 1 + (Tuples != NULL);
|
if ((bool)createrun(node, tp->tp_file, empty) == false)
|
||||||
|
extrapasses = 1 + (PS(node)->Tuples != NULL);
|
||||||
/* D2 */
|
/* D2 */
|
||||||
}
|
}
|
||||||
for (tp = Tape + TapeRange; tp >= Tape; tp--)
|
for (tp = PS(node)->Tape + PS(node)->TapeRange; tp >= PS(node)->Tape; tp--)
|
||||||
rewind(tp->tp_file); /* D. */
|
rewind(tp->tp_file); /* D. */
|
||||||
heap_endscan(sdesc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* createrun - places the next run on file
|
* createrun - places the next run on file, grabbing the tuples by
|
||||||
|
* executing the subplan passed in
|
||||||
*
|
*
|
||||||
* Uses:
|
* Uses:
|
||||||
* Tuples, which should contain any tuples for this run
|
* Tuples, which should contain any tuples for this run
|
||||||
@ -283,7 +333,7 @@ initialrun(Relation rdesc)
|
|||||||
* Tuples contains the tuples for the following run upon exit
|
* Tuples contains the tuples for the following run upon exit
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
createrun(HeapScanDesc sdesc, FILE *file)
|
createrun(Sort *node, FILE *file, bool *empty)
|
||||||
{
|
{
|
||||||
register HeapTuple lasttuple;
|
register HeapTuple lasttuple;
|
||||||
register HeapTuple btup, tup;
|
register HeapTuple btup, tup;
|
||||||
@ -291,47 +341,74 @@ createrun(HeapScanDesc sdesc, FILE *file)
|
|||||||
Buffer b;
|
Buffer b;
|
||||||
bool foundeor;
|
bool foundeor;
|
||||||
short junk;
|
short junk;
|
||||||
|
|
||||||
|
int cr_tuples = 0; /* Count tuples grabbed from plannode */
|
||||||
|
TupleTableSlot *cr_slot;
|
||||||
|
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
lasttuple = NULL;
|
lasttuple = NULL;
|
||||||
nextrun = NULL;
|
nextrun = NULL;
|
||||||
foundeor = false;
|
foundeor = false;
|
||||||
for ( ; ; ) {
|
for ( ; ; ) {
|
||||||
while (LACKMEM() && Tuples != NULL) {
|
while (LACKMEM(node) && PS(node)->Tuples != NULL) {
|
||||||
if (lasttuple != NULL) {
|
if (lasttuple != NULL) {
|
||||||
FREEMEM(lasttuple->t_len);
|
FREEMEM(node,lasttuple->t_len);
|
||||||
FREE(lasttuple);
|
FREE(lasttuple);
|
||||||
TRACEMEM(createrun);
|
TRACEMEM(createrun);
|
||||||
}
|
}
|
||||||
lasttuple = tup = gettuple(&Tuples, &junk);
|
lasttuple = tup = gettuple(&PS(node)->Tuples, &junk,
|
||||||
PUTTUP(tup, file);
|
&PS(node)->treeContext);
|
||||||
|
if (! PS(node)->using_tape_files)
|
||||||
|
inittapes(node);
|
||||||
|
PUTTUP(node, tup, PS(node)->Tape->tp_file);
|
||||||
TRACEOUT(createrun, tup);
|
TRACEOUT(createrun, tup);
|
||||||
}
|
}
|
||||||
if (LACKMEM())
|
if (LACKMEM(node))
|
||||||
break;
|
break;
|
||||||
btup = heap_getnext(sdesc, 0, &b);
|
|
||||||
if (!HeapTupleIsValid(btup)) {
|
/* About to call ExecProcNode, it can mess up the state if it
|
||||||
|
* eventually calls another Sort node. So must stow it away here for
|
||||||
|
* the meantime. -Rex 2.2.1995
|
||||||
|
*/
|
||||||
|
|
||||||
|
cr_slot = ExecProcNode(outerPlan((Plan *)node), (Plan *)node);
|
||||||
|
|
||||||
|
if (TupIsNull(cr_slot)) {
|
||||||
foundeor = true;
|
foundeor = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
tup = tuplecopy(cr_slot->val);
|
||||||
|
ExecClearTuple(cr_slot);
|
||||||
|
PS(node)->tupcount++;
|
||||||
|
cr_tuples++;
|
||||||
|
}
|
||||||
|
|
||||||
IncrProcessed();
|
IncrProcessed();
|
||||||
tup = tuplecopy(btup, sdesc->rs_rd, b);
|
USEMEM(node,tup->t_len);
|
||||||
USEMEM(tup->t_len);
|
|
||||||
TRACEMEM(createrun);
|
TRACEMEM(createrun);
|
||||||
if (lasttuple != NULL && tuplecmp(tup, lasttuple))
|
if (lasttuple != NULL && tuplecmp(tup, lasttuple,
|
||||||
puttuple(&nextrun, tup, 0);
|
&PS(node)->treeContext))
|
||||||
|
puttuple(&nextrun, tup, 0, &PS(node)->treeContext);
|
||||||
else
|
else
|
||||||
puttuple(&Tuples, tup, 0);
|
puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext);
|
||||||
ReleaseBuffer(b);
|
|
||||||
}
|
}
|
||||||
if (lasttuple != NULL) {
|
if (lasttuple != NULL) {
|
||||||
FREEMEM(lasttuple->t_len);
|
FREEMEM(node,lasttuple->t_len);
|
||||||
FREE(lasttuple);
|
FREE(lasttuple);
|
||||||
TRACEMEM(createrun);
|
TRACEMEM(createrun);
|
||||||
}
|
}
|
||||||
dumptuples(file);
|
dumptuples(node);
|
||||||
ENDRUN(file);
|
if (PS(node)->using_tape_files)
|
||||||
|
ENDRUN(file);
|
||||||
/* delimit the end of the run */
|
/* delimit the end of the run */
|
||||||
Tuples = nextrun;
|
PS(node)->Tuples = nextrun;
|
||||||
|
|
||||||
|
/* if we did not see any tuples, mark empty */
|
||||||
|
*empty = (cr_tuples > 0) ? false : true;
|
||||||
|
|
||||||
return((bool)! foundeor); /* XXX - works iff bool is {0,1} */
|
return((bool)! foundeor); /* XXX - works iff bool is {0,1} */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,10 +416,10 @@ createrun(HeapScanDesc sdesc, FILE *file)
|
|||||||
* tuplecopy - see also tuple.c:palloctup()
|
* tuplecopy - see also tuple.c:palloctup()
|
||||||
*
|
*
|
||||||
* This should eventually go there under that name? And this will
|
* This should eventually go there under that name? And this will
|
||||||
* then use malloc directly (see version -r1.2).
|
* then use palloc directly (see version -r1.2).
|
||||||
*/
|
*/
|
||||||
HeapTuple
|
HeapTuple
|
||||||
tuplecopy(HeapTuple tup, Relation rdesc, Buffer b)
|
tuplecopy(HeapTuple tup)
|
||||||
{
|
{
|
||||||
HeapTuple rettup;
|
HeapTuple rettup;
|
||||||
|
|
||||||
@ -362,18 +439,22 @@ tuplecopy(HeapTuple tup, Relation rdesc, Buffer b)
|
|||||||
* file of tuples in order
|
* file of tuples in order
|
||||||
*/
|
*/
|
||||||
FILE *
|
FILE *
|
||||||
mergeruns()
|
mergeruns(Sort *node)
|
||||||
{
|
{
|
||||||
register struct tape *tp;
|
register struct tape *tp;
|
||||||
|
|
||||||
tp = Tape + TapeRange;
|
Assert(node != (Sort *) NULL);
|
||||||
merge(tp);
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
Assert(PS(node)->using_tape_files == true);
|
||||||
|
|
||||||
|
tp = PS(node)->Tape + PS(node)->TapeRange;
|
||||||
|
merge(node, tp);
|
||||||
rewind(tp->tp_file);
|
rewind(tp->tp_file);
|
||||||
while (--Level != 0) {
|
while (--PS(node)->Level != 0) {
|
||||||
tp = tp->tp_prev;
|
tp = tp->tp_prev;
|
||||||
rewind(tp->tp_file);
|
rewind(tp->tp_file);
|
||||||
/* resettape(tp->tp_file); -not sufficient */
|
/* resettape(tp->tp_file); -not sufficient */
|
||||||
merge(tp);
|
merge(node, tp);
|
||||||
rewind(tp->tp_file);
|
rewind(tp->tp_file);
|
||||||
}
|
}
|
||||||
return(tp->tp_file);
|
return(tp->tp_file);
|
||||||
@ -384,7 +465,7 @@ mergeruns()
|
|||||||
* (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271)
|
* (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
merge(struct tape *dest)
|
merge(Sort *node, struct tape *dest)
|
||||||
{
|
{
|
||||||
register HeapTuple tup;
|
register HeapTuple tup;
|
||||||
register struct tape *lasttp; /* (TAPE[P]) */
|
register struct tape *lasttp; /* (TAPE[P]) */
|
||||||
@ -396,6 +477,10 @@ merge(struct tape *dest)
|
|||||||
short fromtape;
|
short fromtape;
|
||||||
long tuplen;
|
long tuplen;
|
||||||
|
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
Assert(PS(node)->using_tape_files == true);
|
||||||
|
|
||||||
lasttp = dest->tp_prev;
|
lasttp = dest->tp_prev;
|
||||||
times = lasttp->tp_fib;
|
times = lasttp->tp_fib;
|
||||||
for (tp = lasttp ; tp != dest; tp = tp->tp_prev)
|
for (tp = lasttp ; tp != dest; tp = tp->tp_prev)
|
||||||
@ -403,95 +488,217 @@ merge(struct tape *dest)
|
|||||||
tp->tp_fib += times;
|
tp->tp_fib += times;
|
||||||
/* Tape[].tp_fib (A[]) is set to proper exit values */
|
/* Tape[].tp_fib (A[]) is set to proper exit values */
|
||||||
|
|
||||||
if (TotalDummy < TapeRange) /* no complete dummy runs */
|
if (PS(node)->TotalDummy < PS(node)->TapeRange)/* no complete dummy runs */
|
||||||
outdummy = 0;
|
outdummy = 0;
|
||||||
else {
|
else {
|
||||||
outdummy = TotalDummy; /* a large positive number */
|
outdummy = PS(node)->TotalDummy; /* a large positive number */
|
||||||
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
|
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
|
||||||
if (outdummy > tp->tp_dummy)
|
if (outdummy > tp->tp_dummy)
|
||||||
outdummy = tp->tp_dummy;
|
outdummy = tp->tp_dummy;
|
||||||
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
|
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
|
||||||
tp->tp_dummy -= outdummy;
|
tp->tp_dummy -= outdummy;
|
||||||
tp->tp_dummy += outdummy;
|
tp->tp_dummy += outdummy;
|
||||||
TotalDummy -= outdummy * TapeRange;
|
PS(node)->TotalDummy -= outdummy * PS(node)->TapeRange;
|
||||||
/* do not add the outdummy runs yet */
|
/* do not add the outdummy runs yet */
|
||||||
times -= outdummy;
|
times -= outdummy;
|
||||||
}
|
}
|
||||||
destfile = dest->tp_file;
|
destfile = dest->tp_file;
|
||||||
while (times-- != 0) { /* merge one run */
|
while (times-- != 0) { /* merge one run */
|
||||||
tuples = NULL;
|
tuples = NULL;
|
||||||
if (TotalDummy == 0)
|
if (PS(node)->TotalDummy == 0)
|
||||||
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
||||||
GETLEN(tuplen, tp->tp_file);
|
GETLEN(tuplen, tp->tp_file);
|
||||||
tup = ALLOCTUP(tuplen);
|
tup = ALLOCTUP(tuplen);
|
||||||
USEMEM(tuplen);
|
USEMEM(node,tuplen);
|
||||||
TRACEMEM(merge);
|
TRACEMEM(merge);
|
||||||
SETTUPLEN(tup, tuplen);
|
SETTUPLEN(tup, tuplen);
|
||||||
GETTUP(tup, tuplen, tp->tp_file);
|
GETTUP(node, tup, tuplen, tp->tp_file);
|
||||||
puttuple(&tuples, tup, TAPENO(tp));
|
puttuple(&tuples, tup, tp - PS(node)->Tape,
|
||||||
|
&PS(node)->treeContext);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
||||||
if (tp->tp_dummy != 0) {
|
if (tp->tp_dummy != 0) {
|
||||||
tp->tp_dummy--;
|
tp->tp_dummy--;
|
||||||
TotalDummy--;
|
PS(node)->TotalDummy--;
|
||||||
} else {
|
} else {
|
||||||
GETLEN(tuplen, tp->tp_file);
|
GETLEN(tuplen, tp->tp_file);
|
||||||
tup = ALLOCTUP(tuplen);
|
tup = ALLOCTUP(tuplen);
|
||||||
USEMEM(tuplen);
|
USEMEM(node,tuplen);
|
||||||
TRACEMEM(merge);
|
TRACEMEM(merge);
|
||||||
SETTUPLEN(tup, tuplen);
|
SETTUPLEN(tup, tuplen);
|
||||||
GETTUP(tup, tuplen, tp->tp_file);
|
GETTUP(node, tup, tuplen, tp->tp_file);
|
||||||
puttuple(&tuples, tup, TAPENO(tp));
|
puttuple(&tuples, tup, tp - PS(node)->Tape,
|
||||||
|
&PS(node)->treeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (tuples != NULL) {
|
while (tuples != NULL) {
|
||||||
/* possible optimization by using count in tuples */
|
/* possible optimization by using count in tuples */
|
||||||
tup = gettuple(&tuples, &fromtape);
|
tup = gettuple(&tuples, &fromtape, &PS(node)->treeContext);
|
||||||
PUTTUP(tup, destfile);
|
PUTTUP(node, tup, destfile);
|
||||||
FREEMEM(tup->t_len);
|
FREEMEM(node,tup->t_len);
|
||||||
FREE(tup);
|
FREE(tup);
|
||||||
TRACEMEM(merge);
|
TRACEMEM(merge);
|
||||||
GETLEN(tuplen, Tape[fromtape].tp_file);
|
GETLEN(tuplen, PS(node)->Tape[fromtape].tp_file);
|
||||||
if (tuplen == 0)
|
if (tuplen == 0)
|
||||||
;
|
;
|
||||||
else {
|
else {
|
||||||
tup = ALLOCTUP(tuplen);
|
tup = ALLOCTUP(tuplen);
|
||||||
USEMEM(tuplen);
|
USEMEM(node,tuplen);
|
||||||
TRACEMEM(merge);
|
TRACEMEM(merge);
|
||||||
SETTUPLEN(tup, tuplen);
|
SETTUPLEN(tup, tuplen);
|
||||||
GETTUP(tup, tuplen, Tape[fromtape].tp_file);
|
GETTUP(node, tup, tuplen, PS(node)->Tape[fromtape].tp_file);
|
||||||
puttuple(&tuples, tup, fromtape);
|
puttuple(&tuples, tup, fromtape, &PS(node)->treeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ENDRUN(destfile);
|
ENDRUN(destfile);
|
||||||
}
|
}
|
||||||
TotalDummy += outdummy;
|
PS(node)->TotalDummy += outdummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* endpsort - creates the new relation and unlinks the tape files
|
* dumptuples - stores all the tuples in tree into file
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
endpsort(Relation rdesc, FILE *file)
|
dumptuples(Sort *node)
|
||||||
{
|
{
|
||||||
register struct tape *tp;
|
register struct leftist *tp;
|
||||||
register HeapTuple tup;
|
register struct leftist *newp;
|
||||||
long tuplen;
|
struct leftist **treep = &PS(node)->Tuples;
|
||||||
|
LeftistContext context = &PS(node)->treeContext;
|
||||||
|
HeapTuple tup;
|
||||||
|
int memtupindex = 0;
|
||||||
|
|
||||||
|
if (! PS(node)->using_tape_files) {
|
||||||
|
Assert(PS(node)->memtuples == NULL);
|
||||||
|
PS(node)->memtuples = palloc(PS(node)->tupcount * sizeof(HeapTuple));
|
||||||
|
}
|
||||||
|
|
||||||
if (! feof(file))
|
tp = *treep;
|
||||||
while (GETLEN(tuplen, file) && tuplen != 0) {
|
while (tp != NULL) {
|
||||||
tup = ALLOCTUP(tuplen);
|
tup = tp->lt_tuple;
|
||||||
SortMemory += tuplen;
|
if (tp->lt_dist == 1) /* lt_right == NULL */
|
||||||
SETTUPLEN(tup, tuplen);
|
newp = tp->lt_left;
|
||||||
GETTUP(tup, tuplen, file);
|
else
|
||||||
heap_insert(rdesc, tup);
|
newp = lmerge(tp->lt_left, tp->lt_right, context);
|
||||||
|
FREEMEM(node,sizeof (struct leftist));
|
||||||
|
FREE(tp);
|
||||||
|
if (PS(node)->using_tape_files) {
|
||||||
|
PUTTUP(node, tup, PS(node)->Tape->tp_file);
|
||||||
|
FREEMEM(node,tup->t_len);
|
||||||
FREE(tup);
|
FREE(tup);
|
||||||
SortMemory -= tuplen;
|
|
||||||
}
|
}
|
||||||
for (tp = Tape + TapeRange; tp >= Tape; tp--)
|
else
|
||||||
destroytape(tp->tp_file);
|
PS(node)->memtuples[memtupindex++] = tup;
|
||||||
|
|
||||||
|
tp = newp;
|
||||||
|
}
|
||||||
|
*treep = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* psort_grabtuple - gets a tuple from the sorted file and returns it.
|
||||||
|
* If there are no tuples left, returns NULL.
|
||||||
|
* Should not call psort_end unless this has returned
|
||||||
|
* a NULL indicating the last tuple has been processed.
|
||||||
|
*/
|
||||||
|
HeapTuple
|
||||||
|
psort_grabtuple(Sort *node)
|
||||||
|
{
|
||||||
|
register HeapTuple tup;
|
||||||
|
long tuplen;
|
||||||
|
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
|
if (PS(node)->using_tape_files == true) {
|
||||||
|
if (!feof(PS(node)->psort_grab_file)) {
|
||||||
|
if (GETLEN(tuplen, PS(node)->psort_grab_file) && tuplen != 0) {
|
||||||
|
tup = (HeapTuple)palloc((unsigned)tuplen);
|
||||||
|
SETTUPLEN(tup, tuplen);
|
||||||
|
GETTUP(node, tup, tuplen, PS(node)->psort_grab_file);
|
||||||
|
|
||||||
|
/* Update current merged sort file position */
|
||||||
|
PS(node)->psort_current += tuplen;
|
||||||
|
|
||||||
|
return tup;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (PS(node)->psort_current < PS(node)->tupcount)
|
||||||
|
return PS(node)->memtuples[PS(node)->psort_current++];
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* psort_markpos - saves current position in the merged sort file
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
psort_markpos(Sort *node)
|
||||||
|
{
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
|
PS(node)->psort_saved = PS(node)->psort_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* psort_restorepos- restores current position in merged sort file to
|
||||||
|
* last saved position
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
psort_restorepos(Sort *node)
|
||||||
|
{
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
Assert(PS(node) != (Psortstate *) NULL);
|
||||||
|
|
||||||
|
if (PS(node)->using_tape_files == true)
|
||||||
|
fseek(PS(node)->psort_grab_file, PS(node)->psort_saved, SEEK_SET);
|
||||||
|
PS(node)->psort_current = PS(node)->psort_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* psort_end - unlinks the tape files, and cleans up. Should not be
|
||||||
|
* called unless psort_grabtuple has returned a NULL.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
psort_end(Sort *node)
|
||||||
|
{
|
||||||
|
register struct tape *tp;
|
||||||
|
|
||||||
|
if (!node->cleaned) {
|
||||||
|
Assert(node != (Sort *) NULL);
|
||||||
|
/* Assert(PS(node) != (Psortstate *) NULL); */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I'm changing this because if we are sorting a relation
|
||||||
|
* with no tuples, psortstate is NULL.
|
||||||
|
*/
|
||||||
|
if (PS(node) != (Psortstate *) NULL) {
|
||||||
|
if (PS(node)->using_tape_files == true)
|
||||||
|
for (tp = PS(node)->Tape + PS(node)->TapeRange; tp >= PS(node)->Tape; tp--)
|
||||||
|
destroytape(tp->tp_file);
|
||||||
|
else if (PS(node)->memtuples)
|
||||||
|
pfree(PS(node)->memtuples);
|
||||||
|
|
||||||
|
NDirectFileRead +=
|
||||||
|
(int)ceil((double)PS(node)->BytesRead / BLCKSZ);
|
||||||
|
NDirectFileWrite +=
|
||||||
|
(int)ceil((double)PS(node)->BytesWritten / BLCKSZ);
|
||||||
|
|
||||||
|
pfree((void *)node->psortstate);
|
||||||
|
|
||||||
|
node->cleaned = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -522,26 +729,34 @@ static char Tempfile[MAXPGPATH] = TEMPDIR;
|
|||||||
FILE *
|
FILE *
|
||||||
gettape()
|
gettape()
|
||||||
{
|
{
|
||||||
register struct tapelst *tp;
|
register struct tapelst *tp;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
static int tapeinit = 0;
|
static int tapeinit = 0;
|
||||||
|
char *mktemp();
|
||||||
|
static unsigned int uniqueFileId = 0;
|
||||||
|
extern int errno;
|
||||||
|
char uniqueName[MAXPGPATH];
|
||||||
|
|
||||||
tp = (struct tapelst *)palloc((unsigned)sizeof (struct tapelst));
|
tp = (struct tapelst *)palloc((unsigned)sizeof (struct tapelst));
|
||||||
if (!tapeinit) {
|
|
||||||
Tempfile[sizeof (TEMPDIR) - 1] = '/';
|
sprintf(uniqueName, "%spg_psort.%d.%d", TEMPDIR, getpid(), uniqueFileId);
|
||||||
memmove(Tempfile + sizeof(TEMPDIR), TAPEEXT, sizeof (TAPEEXT));
|
uniqueFileId++;
|
||||||
tapeinit = 1;
|
|
||||||
}
|
tapeinit = 1;
|
||||||
tp->tl_name = palloc((unsigned)sizeof(Tempfile));
|
|
||||||
|
tp->tl_name = palloc((unsigned)sizeof(uniqueName));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* now, copy template with final null into malloc'd space
|
* now, copy template with final null into palloc'd space
|
||||||
*/
|
*/
|
||||||
memmove(tp->tl_name, Tempfile, sizeof (TEMPDIR) + sizeof (TAPEEXT));
|
|
||||||
mktemp(tp->tl_name);
|
memmove(tp->tl_name, uniqueName, strlen(uniqueName));
|
||||||
|
|
||||||
|
|
||||||
AllocateFile();
|
AllocateFile();
|
||||||
file = fopen(tp->tl_name, "w+");
|
file = fopen(tp->tl_name, "w+");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
|
elog(NOTICE, "psort: gettape: fopen returned error code %i", errno);
|
||||||
/* XXX this should not happen */
|
/* XXX this should not happen */
|
||||||
FreeFile();
|
FreeFile();
|
||||||
FREE(tp->tl_name);
|
FREE(tp->tl_name);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: execnodes.h,v 1.6 1996/11/04 08:52:54 scrappy Exp $
|
* $Id: execnodes.h,v 1.7 1997/08/06 03:42:02 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -605,7 +605,7 @@ typedef struct SortState {
|
|||||||
CommonScanState csstate; /* its first field is NodeTag */
|
CommonScanState csstate; /* its first field is NodeTag */
|
||||||
bool sort_Flag;
|
bool sort_Flag;
|
||||||
ScanKey sort_Keys;
|
ScanKey sort_Keys;
|
||||||
Relation sort_TempRelation;
|
bool cleaned;
|
||||||
} SortState;
|
} SortState;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: plannodes.h,v 1.5 1996/11/05 08:18:44 scrappy Exp $
|
* $Id: plannodes.h,v 1.6 1997/08/06 03:42:04 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -265,6 +265,8 @@ typedef struct Sort {
|
|||||||
Oid tempid;
|
Oid tempid;
|
||||||
int keycount;
|
int keycount;
|
||||||
SortState *sortstate;
|
SortState *sortstate;
|
||||||
|
void *psortstate;
|
||||||
|
bool cleaned;
|
||||||
} Sort;
|
} Sort;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -6,15 +6,15 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: lselect.h,v 1.3 1996/11/04 11:51:19 scrappy Exp $
|
* $Id: lselect.h,v 1.4 1997/08/06 03:42:07 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef LSELECT_H
|
#ifndef LSELECT_H
|
||||||
#define LSELECT_H
|
#define LSELECT_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <access/htup.h>
|
#include "access/htup.h"
|
||||||
|
|
||||||
struct leftist {
|
struct leftist {
|
||||||
short lt_dist; /* distance to leaf/empty node */
|
short lt_dist; /* distance to leaf/empty node */
|
||||||
@ -24,17 +24,26 @@ struct leftist {
|
|||||||
struct leftist *lt_right;
|
struct leftist *lt_right;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct leftist *Tuples;
|
/* replaces global variables in lselect.c to make it reentrant */
|
||||||
|
typedef struct {
|
||||||
|
TupleDesc tupDesc;
|
||||||
|
int nKeys;
|
||||||
|
ScanKey scanKeys;
|
||||||
|
int sortMem; /* needed for psort */
|
||||||
|
} LeftistContextData;
|
||||||
|
typedef LeftistContextData *LeftistContext;
|
||||||
|
|
||||||
extern struct leftist *lmerge(struct leftist *pt, struct leftist *qt);
|
extern struct leftist *lmerge(struct leftist *pt, struct leftist *qt,
|
||||||
extern HeapTuple gettuple(struct leftist **treep, short *devnum);
|
LeftistContext context);
|
||||||
extern int puttuple(struct leftist **treep, HeapTuple newtuple, int devnum);
|
extern HeapTuple gettuple(struct leftist **treep, short *devnum,
|
||||||
extern void dumptuples(FILE *file);
|
LeftistContext context);
|
||||||
extern int tuplecmp(HeapTuple ltup, HeapTuple rtup);
|
extern void puttuple(struct leftist **treep, HeapTuple newtuple, short devnum,
|
||||||
|
LeftistContext context);
|
||||||
|
extern int tuplecmp(HeapTuple ltup, HeapTuple rtup, LeftistContext context);
|
||||||
|
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
extern void checktree(struct leftist *tree);
|
extern void checktree(struct leftist *tree, LeftistContext context);
|
||||||
extern int checktreer(struct leftist *tree, int level);
|
extern int checktreer(struct leftist *tree, int level, LeftistContext context);
|
||||||
#endif /* EBUG */
|
#endif /* EBUG */
|
||||||
|
|
||||||
#endif /* LSELECT_H */
|
#endif /* LSELECT_H */
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: psort.h,v 1.3 1997/05/20 11:37:33 vadim Exp $
|
* $Id: psort.h,v 1.4 1997/08/06 03:42:13 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -14,11 +14,13 @@
|
|||||||
#define PSORT_H
|
#define PSORT_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <access/relscan.h>
|
#include "access/relscan.h"
|
||||||
|
#include "utils/lselect.h"
|
||||||
|
#include "nodes/plannodes.h"
|
||||||
|
|
||||||
#define SORTMEM (1 << 18) /* 1/4 M - any static memory */
|
#define SORTMEM (1 << 18) /* 1/4 M - any static memory */
|
||||||
#define MAXTAPES 7 /* 7--See Fig. 70, p273 */
|
#define MAXTAPES 7 /* 7--See Fig. 70, p273 */
|
||||||
#define TAPEEXT "pg_psort.XXXXXX" /* TEMPDIR/TAPEEXT */
|
#define TAPEEXTLEN strlen("pg_psort.xxxxx.xxx") /* TEMPDIR/TAPEEXT */
|
||||||
#define FREE(x) pfree((char *) x)
|
#define FREE(x) pfree((char *) x)
|
||||||
|
|
||||||
struct tape {
|
struct tape {
|
||||||
@ -35,13 +37,38 @@ struct cmplist {
|
|||||||
struct cmplist *cp_next; /* next in chain */
|
struct cmplist *cp_next; /* next in chain */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int Nkeys;
|
/* This structure preserves the state of psort between calls from different
|
||||||
extern ScanKey key;
|
* nodes to its interface functions. Basically, it includes all of the global
|
||||||
extern int SortMemory; /* free memory */
|
* variables in psort. In case you were wondering, pointers to these structures
|
||||||
extern Relation SortRdesc;
|
* are included in Sort node structures. -Rex 2.6.1995
|
||||||
extern struct leftist *Tuples;
|
*/
|
||||||
|
typedef struct Psortstate {
|
||||||
|
LeftistContextData treeContext;
|
||||||
|
|
||||||
|
int TapeRange;
|
||||||
|
int Level;
|
||||||
|
int TotalDummy;
|
||||||
|
struct tape Tape[MAXTAPES];
|
||||||
|
|
||||||
|
int BytesRead;
|
||||||
|
int BytesWritten;
|
||||||
|
int tupcount;
|
||||||
|
|
||||||
|
struct leftist *Tuples;
|
||||||
|
|
||||||
|
FILE *psort_grab_file;
|
||||||
|
long psort_current; /* could be file offset, or array index */
|
||||||
|
long psort_saved; /* could be file offset, or array index */
|
||||||
|
bool using_tape_files;
|
||||||
|
|
||||||
|
HeapTuple *memtuples;
|
||||||
|
} Psortstate;
|
||||||
|
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "utils/elog.h"
|
||||||
|
#include "storage/buf.h"
|
||||||
|
#include "storage/bufmgr.h"
|
||||||
|
|
||||||
#define PDEBUG(PROC, S1)\
|
#define PDEBUG(PROC, S1)\
|
||||||
elog(DEBUG, "%s:%d>> PROC: %s.", __FILE__, __LINE__, S1)
|
elog(DEBUG, "%s:%d>> PROC: %s.", __FILE__, __LINE__, S1)
|
||||||
@ -69,15 +96,21 @@ if (1) CODE; else
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* psort.c */
|
/* psort.c */
|
||||||
extern void psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key);
|
extern bool psort_begin(Sort *node, int nkeys, ScanKey key);
|
||||||
extern void initpsort(void);
|
extern void inittapes(Sort *node);
|
||||||
extern void resetpsort(void);
|
extern void resetpsort(void);
|
||||||
extern void initialrun(Relation rdesc);
|
extern void initialrun(Sort *node, bool *empty);
|
||||||
extern bool createrun(HeapScanDesc sdesc, FILE *file);
|
extern bool createrun(Sort *node, FILE *file, bool *empty);
|
||||||
extern HeapTuple tuplecopy(HeapTuple tup, Relation rdesc, Buffer b);
|
extern HeapTuple tuplecopy(HeapTuple tup);
|
||||||
extern FILE *mergeruns(void);
|
extern FILE *mergeruns(Sort *node);
|
||||||
extern void merge(struct tape *dest);
|
extern void merge(Sort *node, struct tape *dest);
|
||||||
extern void endpsort(Relation rdesc, FILE *file);
|
|
||||||
|
extern void dumptuples(Sort *node);
|
||||||
|
extern HeapTuple psort_grabtuple(Sort *node);
|
||||||
|
extern void psort_markpos(Sort *node);
|
||||||
|
extern void psort_restorepos(Sort *node);
|
||||||
|
extern void psort_end(Sort *node);
|
||||||
|
|
||||||
extern FILE *gettape(void);
|
extern FILE *gettape(void);
|
||||||
extern void resettape(FILE *file);
|
extern void resettape(FILE *file);
|
||||||
extern void destroytape(FILE *file);
|
extern void destroytape(FILE *file);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" This is -*-nroff-*-
|
.\" This is -*-nroff-*-
|
||||||
.\" XXX standard disclaimer belongs here....
|
.\" XXX standard disclaimer belongs here....
|
||||||
.\" $Header: /cvsroot/pgsql/src/man/Attic/postgres.1,v 1.5 1997/01/26 15:32:20 scrappy Exp $
|
.\" $Header: /cvsroot/pgsql/src/man/Attic/postgres.1,v 1.6 1997/08/06 03:42:18 momjian Exp $
|
||||||
.TH POSTGRES95 UNIX 12/08/96 Postgres95 Postgres95
|
.TH POSTGRES95 UNIX 12/08/96 Postgres95 Postgres95
|
||||||
.SH NAME
|
.SH NAME
|
||||||
postgres \(em the Postgres backend server
|
postgres \(em the Postgres backend server
|
||||||
@ -79,7 +79,10 @@ is the number of shared-memory buffers that the
|
|||||||
.IR "postmaster"
|
.IR "postmaster"
|
||||||
has allocated for the backend server processes that it starts. If the
|
has allocated for the backend server processes that it starts. If the
|
||||||
backend is running standalone, this specifies the number of buffers to
|
backend is running standalone, this specifies the number of buffers to
|
||||||
allocate. This value defaults to 64.
|
allocate. This value defaults to 64, and each buffer is 8k bytes.
|
||||||
|
.TP
|
||||||
|
.BR "-E"
|
||||||
|
Echo all queries.
|
||||||
.TP
|
.TP
|
||||||
.BR "-F"
|
.BR "-F"
|
||||||
Disable automatic fsync() call after each transaction.
|
Disable automatic fsync() call after each transaction.
|
||||||
@ -89,15 +92,17 @@ while a transaction is in progress will probably cause data loss.
|
|||||||
.BR "-P" " filedes"
|
.BR "-P" " filedes"
|
||||||
.IR "filedes"
|
.IR "filedes"
|
||||||
specifies the file descriptor that corresponds to the socket (port) on
|
specifies the file descriptor that corresponds to the socket (port) on
|
||||||
which to communicate to the frontend process. This option is
|
which to communicate to the frontend process. This option is
|
||||||
.BR not
|
.BR not
|
||||||
useful for interactive use.
|
useful for interactive use.
|
||||||
.TP
|
.TP
|
||||||
.BR "-Q"
|
.BR "-Q"
|
||||||
Specifies \*(lqquiet\*(rq mode.
|
Specifies \*(lqquiet\*(rq mode.
|
||||||
.TP
|
.TP
|
||||||
.BR "-E"
|
.BR "-S"
|
||||||
Echo all queries.
|
Specifies the amount of memory to be used by internal sorts before using
|
||||||
|
disk files for sorting. This value is specified in 1k bytes, and
|
||||||
|
defaults to 512.
|
||||||
.TP
|
.TP
|
||||||
.BR "-e"
|
.BR "-e"
|
||||||
The
|
The
|
||||||
@ -154,15 +159,6 @@ Turns off the locking system.
|
|||||||
.BR "-N"
|
.BR "-N"
|
||||||
Disables use of newline as a query delimiter.
|
Disables use of newline as a query delimiter.
|
||||||
.TP
|
.TP
|
||||||
.BR "-S"
|
|
||||||
Indicates that the transaction system can run with the assumption of
|
|
||||||
stable main memory, thereby avoiding the necessary flushing of data
|
|
||||||
and log pages to disk at the end of each transaction system. This is
|
|
||||||
only used for performance comparisons for stable vs. non-stable
|
|
||||||
storage. Do not use this in other cases, as recovery after a system
|
|
||||||
crash may be impossible when this option is specified in the absence
|
|
||||||
of stable main memory.
|
|
||||||
.TP
|
|
||||||
.BR "-b"
|
.BR "-b"
|
||||||
Enables generation of bushy query plan trees (as opposed to left-deep
|
Enables generation of bushy query plan trees (as opposed to left-deep
|
||||||
query plans trees). These query plans are not intended for actual
|
query plans trees). These query plans are not intended for actual
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.\" This is -*-nroff-*-
|
.\" This is -*-nroff-*-
|
||||||
.\" XXX standard disclaimer belongs here....
|
.\" XXX standard disclaimer belongs here....
|
||||||
.\" $Header: /cvsroot/pgsql/src/man/Attic/postmaster.1,v 1.5 1997/02/19 01:31:30 momjian Exp $
|
.\" $Header: /cvsroot/pgsql/src/man/Attic/postmaster.1,v 1.6 1997/08/06 03:42:21 momjian Exp $
|
||||||
.TH POSTMASTER UNIX 11/05/95 PostgreSQL PostgreSQL
|
.TH POSTMASTER UNIX 11/05/95 PostgreSQL PostgreSQL
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
postmaster \(em run the Postgres postmaster
|
postmaster \(em run the Postgres postmaster
|
||||||
@ -60,7 +60,7 @@ understands the following command-line options:
|
|||||||
is the number of shared-memory buffers for the
|
is the number of shared-memory buffers for the
|
||||||
.IR "postmaster"
|
.IR "postmaster"
|
||||||
to allocate and manage for the backend server processes that it
|
to allocate and manage for the backend server processes that it
|
||||||
starts. This value defaults to 64.
|
starts. This value defaults to 64, and each buffer is 8k bytes.
|
||||||
.TP
|
.TP
|
||||||
.BR "-D" " data_dir"
|
.BR "-D" " data_dir"
|
||||||
Specifies the directory to use as the root of the tree of database
|
Specifies the directory to use as the root of the tree of database
|
||||||
|
Loading…
x
Reference in New Issue
Block a user