*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
printf("******** \n");
}
+static void
+CleanUpSort(Plan *plan) {
+
+ if (plan == NULL)
+ return;
+
+ if (plan->type == T_Sort) {
+ Sort *sort = (Sort *)plan;
+ psort_end(sort);
+ }
+}
+
/* ----------------------------------------------------------------
* ExecMergeJoin
*
*/
if (TupIsNull(outerTupleSlot)) {
MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
+ CleanUpSort(node->join.lefttree->lefttree);
+ CleanUpSort(node->join.righttree->lefttree);
return NULL;
}
/*-------------------------------------------------------------------------
*
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* ExecSort
*
* old comments
- * Retrieves tuples fron the outer subtree and insert them into a
- * temporary relation. The temporary relation is then sorted and
- * the sorted relation is stored in the relation whose ID is indicated
- * in the 'tempid' field of this node.
+ * Sorts tuples from the outer subtree of the node in psort,
+ * which saves the results in a temporary file or memory. After the
+ * initial call, returns a tuple from the file with each call.
* Assumes that heap access method is used.
*
* Conditions:
ScanDirection dir;
int keycount;
ScanKey sortkeys;
- Relation tempRelation;
- Relation currentRelation;
- HeapScanDesc currentScanDesc;
HeapTuple heapTuple;
TupleTableSlot *slot;
- Buffer buffer;
- int tupCount = 0;
/* ----------------
* get state info from node
dir = estate->es_direction;
/* ----------------
- * the first time we call this, we retrieve all tuples
- * from the subplan into a temporary relation and then
- * we sort the relation. Subsequent calls return tuples
- * from the temporary relation.
+ * the first time we call this, psort sorts this into a file.
+ * Subsequent calls return tuples from psort.
* ----------------
*/
* ----------------
*/
estate->es_direction = ForwardScanDirection;
-
- /* ----------------
- * if we couldn't create the temp or current relations then
- * 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
+ * prepare information for psort_begin()
* ----------------
*/
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;
sortkeys = (ScanKey)sortstate->sort_Keys;
SO1_printf("ExecSort: %s\n",
- "calling psort");
-
- /*
- * If no tuples were fetched from the proc node return NULL now
- * psort dumps it if 0 tuples are in the relation and I don't want
- * to try to debug *that* routine!!
- */
- 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...");
+ "calling psort_begin");
+
+ if (!psort_begin(node, /* this node */
+ keycount, /* number keys */
+ sortkeys)) /* keys */
+ {
+ /* Psort says, there are no tuples to be sorted */
return NULL;
}
*/
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
* ----------------
*/
- slot = sortstate->csstate.css_ScanTupleSlot;
-
- slot->ttc_tupleDescriptor =
- RelationGetTupleDescriptor(currentRelation);
-
+ slot = (TupleTableSlot*)sortstate->csstate.cstate.cs_ResultTupleSlot;
+ /* *** get_cs_ResultTupleSlot((CommonState) sortstate); */
+
+ slot->ttc_tupleDescriptor = ExecGetTupType(outerNode);
+#ifdef 0
+ slot->ttc_execTupDescriptor = ExecGetExecTupDesc(outerNode);
+#endif
/* ----------------
* finally set the sorted flag to true
* ----------------
*/
sortstate->sort_Flag = true;
+ SO1_printf(stderr, "ExecSort: sorting done.\n");
}
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",
- "retrieveing tuple from sorted relation");
+ "retrieving tuple from sorted relation");
/* ----------------
- * at this point we know we have a sorted relation so
- * we preform a simple scan on it with amgetnext()..
+ * at this point we grab a tuple from psort
* ----------------
*/
- currentScanDesc = sortstate->csstate.css_currentScanDesc;
-
- heapTuple = heap_getnext(currentScanDesc, /* scan desc */
- ScanDirectionIsBackward(dir),
- /* bkwd flag */
- &buffer); /* return: buffer */
-
- /* Increase the pin count on the buffer page, because the tuple stored in
- the slot also points to it (as well as the scan descriptor). If we
- don't, ExecStoreTuple will decrease the pin count on the next iteration.
- - 01/09/93 */
-
- if (buffer != InvalidBuffer)
- IncrBufferRefCount(buffer);
-
- return ExecStoreTuple(heapTuple, /* tuple to store */
- slot, /* slot to store in */
- buffer, /* this tuple's buffer */
- false); /* don't free stuff from amgetnext */
+ heapTuple = psort_grabtuple(node);
+
+ if (heapTuple == NULL) {
+/* psort_end(node); */
+ return (ExecClearTuple(slot));
+ }
+
+ ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* no buffer */
+ true); /* free the palloc'd tuple */
+/* printf("ExecSort: (%x)",node);print_slot(slot);printf("\n");*/
+ return slot;
+#if 0
+ return ExecStoreTuple(heapTuple, /* tuple to store */
+ slot, /* slot to store in */
+ InvalidBuffer, /* no buffer */
+ true); /* free the palloc'd tuple */
+#endif
}
/* ----------------------------------------------------------------
SortState *sortstate;
Plan *outerPlan;
ScanKey sortkeys;
- TupleDesc tupType;
- Oid tempOid;
- Oid sortOid;
- Relation tempDesc;
- Relation sortedDesc;
SO1_printf("ExecInitSort: %s\n",
"initializing sort node");
sortstate = makeNode(SortState);
sortstate->sort_Flag = 0;
sortstate->sort_Keys = NULL;
- sortstate->sort_TempRelation = NULL;
+ node->cleaned = FALSE;
node->sortstate = sortstate;
* relation.
* ----------------
*/
- ExecInitScanTupleSlot(estate, &sortstate->csstate);
- ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
+ ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
+ ExecInitScanTupleSlot(estate, &sortstate->csstate);
/* ----------------
* initializes child nodes
* 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;
- /* ----------------
- * 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",
"sort node initialized");
* ExecEndSort(node)
*
* old comments
- * destroys the temporary relation.
* ----------------------------------------------------------------
*/
void
ExecEndSort(Sort *node)
{
SortState *sortstate;
- Relation tempRelation;
- Relation sortedRelation;
Plan *outerPlan;
/* ----------------
"shutting down sort node");
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
* ----------------
*/
ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
+
+ /* Clean up after psort */
+ psort_end(node);
SO1_printf("ExecEndSort: %s\n",
"sort node shutdown");
/* ----------------------------------------------------------------
* ExecSortMarkPos
+ *
+ * Calls psort to save the current position in the sorted file.
* ----------------------------------------------------------------
*/
void
ExecSortMarkPos(Sort *node)
{
- SortState *sortstate;
- HeapScanDesc sdesc;
-
+ SortState *sortstate;
+
/* ----------------
- * if we haven't sorted yet, just return
+ * if we haven't sorted yet, just return
* ----------------
*/
sortstate = node->sortstate;
if (sortstate->sort_Flag == false)
return;
- sdesc = sortstate->csstate.css_currentScanDesc;
- heap_markpos(sdesc);
+ psort_markpos(node);
+
return;
}
/* ----------------------------------------------------------------
* ExecSortRestrPos
+ *
+ * Calls psort to restore the last saved sort file position.
* ----------------------------------------------------------------
*/
void
ExecSortRestrPos(Sort *node)
{
- SortState *sortstate;
- HeapScanDesc sdesc;
-
+ SortState *sortstate;
+
/* ----------------
* if we haven't sorted yet, just return.
* ----------------
return;
/* ----------------
- * restore the scan to the previously marked position
+ * restore the scan to the previously marked position
* ----------------
*/
- sdesc = sortstate->csstate.css_currentScanDesc;
- heap_restrpos(sdesc);
+ psort_restorepos(node);
}
*
*
* 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
* this is the "main" module of the postgres backend and
extern int NBuffers;
int fsyncOff = 0;
+int SortMem = 512;
int dontExecute = 0;
static int ShowStats;
*/
flagQ = 1;
break;
-
+
+ case 'S':
+ /* ----------------
+ * S - amount of sort memory to use in 1k bytes
+ * ----------------
+ */
+ SortMem = atoi(optarg);
+ break;
+
+#ifdef NOT_USED
case 'S':
/* ----------------
* S - assume stable main memory
flagS = 1;
SetTransactionFlushEnabled(false);
break;
+#endif
case 's':
/* ----------------
printf("\ttimings = %c\n", ShowStats ? 't' : 'f');
printf("\tdates = %s\n", EuroDates ? "European" : "Normal");
printf("\tbufsize = %d\n", NBuffers);
+ printf("\tsortmem = %d\n", SortMem);
printf("\tquery echo = %c\n", EchoQuery ? 't' : 'f');
printf("\tmultiplexed backend? = %c\n", multiplexedBackend ? 't' : 'f');
*/
if (IsUnderPostmaster == false) {
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 $");
}
/* ----------------
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "utils/psort.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 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
* FREEMEM - record freeing of memory
- * FULLMEM - 1 iff a tuple will fit
*/
-
-#define USEMEM(AMT) SortMemory -= (AMT)
-#define FREEMEM(AMT) SortMemory += (AMT)
-#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */
+#define USEMEM(context,AMT) context->sortMem -= (AMT)
+#define FREEMEM(context,AMT) context->sortMem += (AMT)
/*
* lmerge - merges two leftist trees into one
* speed up code significantly.
*/
struct leftist *
-lmerge(struct leftist *pt, struct leftist *qt)
+lmerge(struct leftist *pt, struct leftist *qt, LeftistContext context)
{
register struct leftist *root, *majorLeftist, *minorLeftist;
int dist;
- if (tuplecmp(pt->lt_tuple, qt->lt_tuple)) {
+ if (tuplecmp(pt->lt_tuple, qt->lt_tuple, context)) {
root = pt;
majorLeftist = qt;
} else {
root->lt_left = majorLeftist;
else {
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) {
root->lt_dist = 1 + dist;
root->lt_right = root->lt_left;
}
static struct leftist *
-linsert(struct leftist *root, struct leftist *new1)
+linsert(struct leftist *root, struct leftist *new1, LeftistContext context)
{
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;
return(new1);
}
}
return(root);
}
- right = linsert(right, new1);
+ right = linsert(right, new1, context);
if (right->lt_dist < left->lt_dist) {
root->lt_dist = 1 + left->lt_dist;
root->lt_left = right;
*/
HeapTuple
gettuple(struct leftist **treep,
- short *devnum) /* device from which tuple came */
+ short *devnum, /* device from which tuple came */
+ LeftistContext context)
{
register struct leftist *tp;
HeapTuple tup;
if (tp->lt_dist == 1) /* lt_left == NULL */
*treep = tp->lt_left;
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);
return(tup);
}
* Note:
* Currently never returns NULL BUG
*/
-int
-puttuple(struct leftist **treep, HeapTuple newtuple, int devnum)
+void
+puttuple(struct leftist **treep,
+ HeapTuple newtuple,
+ short devnum,
+ LeftistContext context)
{
register struct leftist *new1;
register struct leftist *tp;
new1 = (struct leftist *) palloc((unsigned) sizeof (struct leftist));
- USEMEM(sizeof (struct leftist));
+ USEMEM(context,sizeof (struct leftist));
new1->lt_dist = 1;
new1->lt_devnum = devnum;
new1->lt_tuple = newtuple;
if ((tp = *treep) == NULL)
*treep = new1;
else
- *treep = linsert(tp, new1);
- return(1);
+ *treep = linsert(tp, new1, context);
+ 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
*
* Assumtions:
*/
int
-tuplecmp(HeapTuple ltup, HeapTuple rtup)
+tuplecmp(HeapTuple ltup, HeapTuple rtup, LeftistContext context)
{
register char *lattr, *rattr;
int nkey = 0;
return(0);
if (rtup == (HeapTuple)NULL)
return(1);
- while (nkey < Nkeys && !result) {
+ while (nkey < context->nKeys && !result) {
lattr = heap_getattr(ltup, InvalidBuffer,
- Key[nkey].sk_attno,
- RelationGetTupleDescriptor(SortRdesc),
- &isnull);
+ context->scanKeys[nkey].sk_attno,
+ context->tupDesc, &isnull);
if (isnull)
return(0);
rattr = heap_getattr(rtup, InvalidBuffer,
- Key[nkey].sk_attno,
- RelationGetTupleDescriptor(SortRdesc),
+ context->scanKeys[nkey].sk_attno,
+ context->tupDesc,
&isnull);
if (isnull)
return(1);
- if (Key[nkey].sk_flags & SK_COMMUTE) {
- if (!(result = (long) (*Key[nkey].sk_func) (rattr, lattr)))
- result = -(long) (*Key[nkey].sk_func) (lattr, rattr);
- } else if (!(result = (long) (*Key[nkey].sk_func) (lattr, rattr)))
- result = -(long) (*Key[nkey].sk_func) (rattr, lattr);
+ if (context->scanKeys[nkey].sk_flags & SK_COMMUTE) {
+ if (!(result =
+ (long) (*context->scanKeys[nkey].sk_func) (rattr, lattr)))
+ result =
+ -(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++;
}
return (result == 1);
#ifdef EBUG
void
-checktree(struct leftist *tree)
+checktree(struct leftist *tree, LeftistContext context)
{
int lnodes;
int rnodes;
puts("Null tree.");
return;
}
- lnodes = checktreer(tree->lt_left, 1);
- rnodes = checktreer(tree->lt_right, 1);
+ lnodes = checktreer(tree->lt_left, 1, context);
+ rnodes = checktreer(tree->lt_right, 1, context);
if (lnodes < 0) {
lnodes = -lnodes;
puts("0:\tBad left side.");
} else if (tree->lt_dist != 1+ tree->lt_right->lt_dist)
puts("0:\tDistance incorrect.");
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");
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("Tree has %d nodes\n", 1 + lnodes + rnodes);
}
int
-checktreer(struct leftist *tree, int level)
+checktreer(struct leftist *tree, int level, LeftistContext context)
{
int lnodes, rnodes;
int error = 0;
if (tree == NULL)
return(0);
- lnodes = checktreer(tree->lt_left, level + 1);
- rnodes = checktreer(tree->lt_right, level + 1);
+ lnodes = checktreer(tree->lt_left, level + 1, context);
+ rnodes = checktreer(tree->lt_right, level + 1, context);
if (lnodes < 0) {
error = 1;
lnodes = -lnodes;
printf("%d:\tDistance incorrect.\n", level);
}
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;
printf("%d:\tLeft child < parent.\n");
}
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;
printf("%d:\tRight child < parent.\n");
}
*
*
* 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
- * Sorts the first relation into the second relation. The sort may
- * not be called twice simultaneously.
+ * Sorts the first relation into the second relation.
+ *
+ * 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.
*
*/
#include
#include
-#include
#include "postgres.h"
#include "storage/buf.h"
#include "storage/bufmgr.h" /* for BLCKSZ */
#include "utils/portal.h" /* for {Start,End}PortalAllocMode */
+#include "utils/elog.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/psort.h"
#include "storage/fd.h"
-#ifndef HAVE_MEMMOVE
-# include
-#else
-# include
-#endif
-
#define TEMPDIR "./"
-int Nkeys;
-ScanKey Key;
-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 */
+extern int SortMem; /* defined as postgres option */
+static long shortzero = 0; /* used to delimit runs */
-static int BytesRead; /* to keep track of # of IO */
-static int BytesWritten;
+/*
+ * 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 //
+ */
-Relation SortRdesc; /* current tuples in memory */
-struct leftist *Tuples; /* current tuples in memory */
+/*
+ * PS - Macro to access and cast psortstate from a Sort node
+ */
+#define PS(N) ((Psortstate *)N->psortstate)
/*
- * psort - polyphase merge sort entry point
+ * 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.
*/
-void
-psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key)
+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(key[0].sk_attno != 0);
AssertArg(key[0].sk_procedure != 0);
- Nkeys = nkeys;
- Key = key;
- SortMemory = 0;
- SortRdesc = oldrel;
- BytesRead = 0;
- BytesWritten = 0;
- /*
- * may not be the best place.
- *
- * Pass 0 for the "limit" as the argument is currently ignored.
- * Previously, only one arg was passed. -mer 12 Nov. 1991
- */
- StartPortalAllocMode(StaticAllocMode, (Size)0);
- initpsort();
- initialrun(oldrel);
- /* call finalrun(newrel, mergerun()) instead */
- endpsort(newrel, mergeruns());
- EndPortalAllocMode();
- NDirectFileRead += (int)ceil((double)BytesRead / BLCKSZ);
- NDirectFileWrite += (int)ceil((double)BytesWritten / BLCKSZ);
-}
+ PS(node)->BytesRead = 0;
+ PS(node)->BytesWritten = 0;
+ PS(node)->treeContext.tupDesc =
+ ExecGetTupType(outerPlan((Plan *)node));
+ PS(node)->treeContext.nKeys = nkeys;
+ PS(node)->treeContext.scanKeys = key;
+ PS(node)->treeContext.sortMem = SortMem;
-/*
- * TAPENO - number of tape in Tape
- */
+ PS(node)->Tuples = NULL;
+ PS(node)->tupcount = 0;
+
+ PS(node)->using_tape_files = false;
+ PS(node)->memtuples = NULL;
+
+ initialrun(node, &empty);
+
+ if (empty)
+ return false;
+
+ if (PS(node)->using_tape_files)
+ PS(node)->psort_grab_file = mergeruns(node);
-#define TAPENO(NODE) (NODE - Tape)
-#define TUPLENO(TUP) ((TUP == NULL) ? -1 : (int) TUP->t_iid)
+ PS(node)->psort_current = 0;
+ PS(node)->psort_saved = 0;
+
+ return true;
+}
/*
- * initpsort - initializes the tapes
+ * inittapes - initializes the tapes
* - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
* Returns:
* number of allocated tapes
*/
void
-initpsort()
+inittapes(Sort *node)
{
register int i;
register struct tape *tp;
+ Assert(node != (Sort *) NULL);
+ Assert(PS(node) != (Psortstate *) NULL);
+
/*
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++) {
tp->tp_dummy = 1;
tp->tp_fib = 1;
tp->tp_prev = tp - 1;
tp++;
}
- TapeRange = --tp - Tape;
+ PS(node)->TapeRange = --tp - PS(node)->Tape;
tp->tp_dummy = 0;
tp->tp_fib = 0;
- Tape[0].tp_prev = tp;
-
- if (TapeRange <= 1)
- elog(WARN, "initpsort: Could only allocate %d < 3 tapes\n",
- TapeRange + 1);
+ PS(node)->Tape[0].tp_prev = tp;
- Level = 1;
- TotalDummy = TapeRange;
+ if (PS(node)->TapeRange <= 1)
+ elog(WARN, "inittapes: Could only allocate %d < 3 tapes\n",
+ PS(node)->TapeRange + 1);
- SortMemory = SORTMEM;
- LastTuple = NULL;
- Tuples = NULL;
+ PS(node)->Level = 1;
+ PS(node)->TotalDummy = PS(node)->TapeRange;
+
+ PS(node)->using_tape_files = true;
}
/*
- * resetpsort - resets (frees) malloc'd memory for an aborted Xaction
+ * resetpsort - resets (pfrees) palloc'd memory for an aborted Xaction
*
* Not implemented yet.
*/
* LEN field must be a short; FP is a stream
*/
-#define PUTTUP(TUP, FP)\
- BytesWritten += (TUP)->t_len; \
- fwrite((char *)TUP, (TUP)->t_len, 1, FP)
+
+#define PUTTUP(NODE, TUP, FP) if (1) {\
+ ((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 GETLEN(LEN, FP) fread((char *)&(LEN), sizeof (shortzero), 1, FP)
#define ALLOCTUP(LEN) ((HeapTuple)palloc((unsigned)LEN))
-#define GETTUP(TUP, LEN, FP)\
+#define GETTUP(NODE, TUP, LEN, FP) if (1) {\
IncrProcessed(); \
- BytesRead += (LEN) - sizeof (shortzero); \
- fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP)
+ ((Psortstate *)NODE->psortstate)->BytesRead += (LEN) - sizeof (shortzero); \
+ fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP);} \
+ else
#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN
/*
* FULLMEM - 1 iff a tuple will fit
*/
-#define USEMEM(AMT) SortMemory -= (AMT)
-#define FREEMEM(AMT) SortMemory += (AMT)
-#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */
+#define USEMEM(NODE,AMT) PS(node)->treeContext.sortMem -= (AMT)
+#define FREEMEM(NODE,AMT) PS(node)->treeContext.sortMem += (AMT)
+#define LACKMEM(NODE) (PS(node)->treeContext.sortMem <= MAXBLCKSZ) /* not accurate */
#define TRACEMEM(FUNC)
#define TRACEOUT(FUNC, TUP)
* Also, perhaps allocate tapes when needed. Split into 2 funcs.
*/
void
-initialrun(Relation rdesc)
+initialrun(Sort *node, bool *empty)
{
/* register struct tuple *tup; */
register struct tape *tp;
- HeapScanDesc sdesc;
int baseruns; /* D:(a) */
- int morepasses; /* EOF */
-
- sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0,
- (ScanKey)NULL);
- tp = Tape;
+ int extrapasses; /* EOF */
+
+ Assert(node != (Sort *) NULL);
+ Assert(PS(node) != (Psortstate *) NULL);
+
+ tp = PS(node)->Tape;
- if ((bool)createrun(sdesc, tp->tp_file) != false)
- morepasses = 0;
+ if ((bool)createrun(node, tp->tp_file, empty) != false) {
+ if (! PS(node)->using_tape_files)
+ inittapes(node);
+ extrapasses = 0;
+ }
else
- morepasses = 1 + (Tuples != NULL); /* (T != N) ? 2 : 1 */
+ return; /* if rows fit in memory, we never access tape stuff */
for ( ; ; ) {
tp->tp_dummy--;
- TotalDummy--;
+ PS(node)->TotalDummy--;
if (tp->tp_dummy < (tp + 1)->tp_dummy)
tp++;
else if (tp->tp_dummy != 0)
- tp = Tape;
+ tp = PS(node)->Tape;
else {
- Level++;
- baseruns = Tape[0].tp_fib;
- for (tp = Tape; tp - Tape < TapeRange; tp++) {
- TotalDummy +=
+ PS(node)->Level++;
+ baseruns = PS(node)->Tape[0].tp_fib;
+ for (tp = PS(node)->Tape;
+ tp - PS(node)->Tape < PS(node)->TapeRange; tp++) {
+ PS(node)->TotalDummy +=
(tp->tp_dummy = baseruns
+ (tp + 1)->tp_fib
- tp->tp_fib);
tp->tp_fib = baseruns
+ (tp + 1)->tp_fib;
}
- tp = Tape; /* D4 */
+ tp = PS(node)->Tape; /* D4 */
} /* D3 */
- if (morepasses)
- if (--morepasses) {
- dumptuples(tp->tp_file);
+ if (extrapasses)
+ if (--extrapasses) {
+ dumptuples(node);
ENDRUN(tp->tp_file);
continue;
} else
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 */
}
- for (tp = Tape + TapeRange; tp >= Tape; tp--)
- rewind(tp->tp_file); /* D. */
- heap_endscan(sdesc);
+ for (tp = PS(node)->Tape + PS(node)->TapeRange; tp >= PS(node)->Tape; tp--)
+ rewind(tp->tp_file); /* D. */
}
/*
- * createrun - places the next run on file
+ * createrun - places the next run on file, grabbing the tuples by
+ * executing the subplan passed in
*
* Uses:
* Tuples, which should contain any tuples for this run
* Tuples contains the tuples for the following run upon exit
*/
bool
-createrun(HeapScanDesc sdesc, FILE *file)
+createrun(Sort *node, FILE *file, bool *empty)
{
register HeapTuple lasttuple;
register HeapTuple btup, tup;
Buffer b;
bool foundeor;
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;
nextrun = NULL;
foundeor = false;
for ( ; ; ) {
- while (LACKMEM() && Tuples != NULL) {
+ while (LACKMEM(node) && PS(node)->Tuples != NULL) {
if (lasttuple != NULL) {
- FREEMEM(lasttuple->t_len);
+ FREEMEM(node,lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createrun);
}
- lasttuple = tup = gettuple(&Tuples, &junk);
- PUTTUP(tup, file);
+ lasttuple = tup = gettuple(&PS(node)->Tuples, &junk,
+ &PS(node)->treeContext);
+ if (! PS(node)->using_tape_files)
+ inittapes(node);
+ PUTTUP(node, tup, PS(node)->Tape->tp_file);
TRACEOUT(createrun, tup);
}
- if (LACKMEM())
+ if (LACKMEM(node))
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;
break;
}
+ else {
+ tup = tuplecopy(cr_slot->val);
+ ExecClearTuple(cr_slot);
+ PS(node)->tupcount++;
+ cr_tuples++;
+ }
+
IncrProcessed();
- tup = tuplecopy(btup, sdesc->rs_rd, b);
- USEMEM(tup->t_len);
+ USEMEM(node,tup->t_len);
TRACEMEM(createrun);
- if (lasttuple != NULL && tuplecmp(tup, lasttuple))
- puttuple(&nextrun, tup, 0);
+ if (lasttuple != NULL && tuplecmp(tup, lasttuple,
+ &PS(node)->treeContext))
+ puttuple(&nextrun, tup, 0, &PS(node)->treeContext);
else
- puttuple(&Tuples, tup, 0);
- ReleaseBuffer(b);
+ puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext);
}
if (lasttuple != NULL) {
- FREEMEM(lasttuple->t_len);
+ FREEMEM(node,lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createrun);
}
- dumptuples(file);
- ENDRUN(file);
+ dumptuples(node);
+ if (PS(node)->using_tape_files)
+ ENDRUN(file);
/* 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} */
}
* tuplecopy - see also tuple.c:palloctup()
*
* 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
-tuplecopy(HeapTuple tup, Relation rdesc, Buffer b)
+tuplecopy(HeapTuple tup)
{
HeapTuple rettup;
* file of tuples in order
*/
FILE *
-mergeruns()
+mergeruns(Sort *node)
{
- register struct tape *tp;
-
- tp = Tape + TapeRange;
- merge(tp);
+ register struct tape *tp;
+
+ Assert(node != (Sort *) NULL);
+ 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);
- while (--Level != 0) {
+ while (--PS(node)->Level != 0) {
tp = tp->tp_prev;
rewind(tp->tp_file);
- /* resettape(tp->tp_file); -not sufficient */
- merge(tp);
+ /* resettape(tp->tp_file); -not sufficient */
+ merge(node, tp);
rewind(tp->tp_file);
}
return(tp->tp_file);
* (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271)
*/
void
-merge(struct tape *dest)
+merge(Sort *node, struct tape *dest)
{
register HeapTuple tup;
register struct tape *lasttp; /* (TAPE[P]) */
short fromtape;
long tuplen;
+ Assert(node != (Sort *) NULL);
+ Assert(PS(node) != (Psortstate *) NULL);
+ Assert(PS(node)->using_tape_files == true);
+
lasttp = dest->tp_prev;
times = lasttp->tp_fib;
for (tp = lasttp ; tp != dest; tp = tp->tp_prev)
tp->tp_fib += times;
/* 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;
else {
- outdummy = TotalDummy; /* a large positive number */
+ outdummy = PS(node)->TotalDummy; /* a large positive number */
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
if (outdummy > tp->tp_dummy)
outdummy = tp->tp_dummy;
for (tp = lasttp; tp != dest; tp = tp->tp_prev)
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 */
times -= outdummy;
}
destfile = dest->tp_file;
- while (times-- != 0) { /* merge one run */
+ while (times-- != 0) { /* merge one run */
tuples = NULL;
- if (TotalDummy == 0)
+ if (PS(node)->TotalDummy == 0)
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
GETLEN(tuplen, tp->tp_file);
tup = ALLOCTUP(tuplen);
- USEMEM(tuplen);
+ USEMEM(node,tuplen);
TRACEMEM(merge);
SETTUPLEN(tup, tuplen);
- GETTUP(tup, tuplen, tp->tp_file);
- puttuple(&tuples, tup, TAPENO(tp));
+ GETTUP(node, tup, tuplen, tp->tp_file);
+ puttuple(&tuples, tup, tp - PS(node)->Tape,
+ &PS(node)->treeContext);
}
else {
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
if (tp->tp_dummy != 0) {
tp->tp_dummy--;
- TotalDummy--;
+ PS(node)->TotalDummy--;
} else {
GETLEN(tuplen, tp->tp_file);
tup = ALLOCTUP(tuplen);
- USEMEM(tuplen);
+ USEMEM(node,tuplen);
TRACEMEM(merge);
SETTUPLEN(tup, tuplen);
- GETTUP(tup, tuplen, tp->tp_file);
- puttuple(&tuples, tup, TAPENO(tp));
+ GETTUP(node, tup, tuplen, tp->tp_file);
+ puttuple(&tuples, tup, tp - PS(node)->Tape,
+ &PS(node)->treeContext);
}
}
}
while (tuples != NULL) {
/* possible optimization by using count in tuples */
- tup = gettuple(&tuples, &fromtape);
- PUTTUP(tup, destfile);
- FREEMEM(tup->t_len);
+ tup = gettuple(&tuples, &fromtape, &PS(node)->treeContext);
+ PUTTUP(node, tup, destfile);
+ FREEMEM(node,tup->t_len);
FREE(tup);
TRACEMEM(merge);
- GETLEN(tuplen, Tape[fromtape].tp_file);
+ GETLEN(tuplen, PS(node)->Tape[fromtape].tp_file);
if (tuplen == 0)
;
else {
tup = ALLOCTUP(tuplen);
- USEMEM(tuplen);
+ USEMEM(node,tuplen);
TRACEMEM(merge);
SETTUPLEN(tup, tuplen);
- GETTUP(tup, tuplen, Tape[fromtape].tp_file);
- puttuple(&tuples, tup, fromtape);
+ GETTUP(node, tup, tuplen, PS(node)->Tape[fromtape].tp_file);
+ puttuple(&tuples, tup, fromtape, &PS(node)->treeContext);
}
- }
+ }
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
-endpsort(Relation rdesc, FILE *file)
+dumptuples(Sort *node)
{
- register struct tape *tp;
- register HeapTuple tup;
- long tuplen;
+ register struct leftist *tp;
+ register struct leftist *newp;
+ 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))
- while (GETLEN(tuplen, file) && tuplen != 0) {
- tup = ALLOCTUP(tuplen);
- SortMemory += tuplen;
- SETTUPLEN(tup, tuplen);
- GETTUP(tup, tuplen, file);
- heap_insert(rdesc, tup);
+ tp = *treep;
+ 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, 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);
- SortMemory -= tuplen;
}
- for (tp = Tape + TapeRange; tp >= Tape; tp--)
- destroytape(tp->tp_file);
+ else
+ 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;
+ }
+ }
}
/*
FILE *
gettape()
{
- register struct tapelst *tp;
- FILE *file;
- static int tapeinit = 0;
+ register struct tapelst *tp;
+ FILE *file;
+ 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));
- if (!tapeinit) {
- Tempfile[sizeof (TEMPDIR) - 1] = '/';
- memmove(Tempfile + sizeof(TEMPDIR), TAPEEXT, sizeof (TAPEEXT));
- tapeinit = 1;
- }
- tp->tl_name = palloc((unsigned)sizeof(Tempfile));
+
+ sprintf(uniqueName, "%spg_psort.%d.%d", TEMPDIR, getpid(), uniqueFileId);
+ uniqueFileId++;
+
+ tapeinit = 1;
+
+ 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();
file = fopen(tp->tl_name, "w+");
if (file == NULL) {
+ elog(NOTICE, "psort: gettape: fopen returned error code %i", errno);
/* XXX this should not happen */
FreeFile();
FREE(tp->tl_name);
*
* 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 $
*
*-------------------------------------------------------------------------
*/
CommonScanState csstate; /* its first field is NodeTag */
bool sort_Flag;
ScanKey sort_Keys;
- Relation sort_TempRelation;
+ bool cleaned;
} SortState;
/* ----------------
*
* 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 $
*
*-------------------------------------------------------------------------
*/
Oid tempid;
int keycount;
SortState *sortstate;
+ void *psortstate;
+ bool cleaned;
} Sort;
/* ----------------
*
* 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
#define LSELECT_H
#include
+#include "access/htup.h"
struct leftist {
short lt_dist; /* distance to leaf/empty node */
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 HeapTuple gettuple(struct leftist **treep, short *devnum);
-extern int puttuple(struct leftist **treep, HeapTuple newtuple, int devnum);
-extern void dumptuples(FILE *file);
-extern int tuplecmp(HeapTuple ltup, HeapTuple rtup);
+extern struct leftist *lmerge(struct leftist *pt, struct leftist *qt,
+ LeftistContext context);
+extern HeapTuple gettuple(struct leftist **treep, short *devnum,
+ LeftistContext context);
+extern void puttuple(struct leftist **treep, HeapTuple newtuple, short devnum,
+ LeftistContext context);
+extern int tuplecmp(HeapTuple ltup, HeapTuple rtup, LeftistContext context);
#ifdef EBUG
-extern void checktree(struct leftist *tree);
-extern int checktreer(struct leftist *tree, int level);
+extern void checktree(struct leftist *tree, LeftistContext context);
+extern int checktreer(struct leftist *tree, int level, LeftistContext context);
#endif /* EBUG */
#endif /* LSELECT_H */
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#define PSORT_H
#include
+#include "access/relscan.h"
+#include "utils/lselect.h"
+#include "nodes/plannodes.h"
#define SORTMEM (1 << 18) /* 1/4 M - any static memory */
#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)
struct tape {
struct cmplist *cp_next; /* next in chain */
};
-extern int Nkeys;
-extern ScanKey key;
-extern int SortMemory; /* free memory */
-extern Relation SortRdesc;
-extern struct leftist *Tuples;
+/* This structure preserves the state of psort between calls from different
+ * nodes to its interface functions. Basically, it includes all of the global
+ * variables in psort. In case you were wondering, pointers to these structures
+ * are included in Sort node structures. -Rex 2.6.1995
+ */
+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
+#include
+#include "utils/elog.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
#define PDEBUG(PROC, S1)\
elog(DEBUG, "%s:%d>> PROC: %s.", __FILE__, __LINE__, S1)
#endif
/* psort.c */
-extern void psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key);
-extern void initpsort(void);
+extern bool psort_begin(Sort *node, int nkeys, ScanKey key);
+extern void inittapes(Sort *node);
extern void resetpsort(void);
-extern void initialrun(Relation rdesc);
-extern bool createrun(HeapScanDesc sdesc, FILE *file);
-extern HeapTuple tuplecopy(HeapTuple tup, Relation rdesc, Buffer b);
-extern FILE *mergeruns(void);
-extern void merge(struct tape *dest);
-extern void endpsort(Relation rdesc, FILE *file);
+extern void initialrun(Sort *node, bool *empty);
+extern bool createrun(Sort *node, FILE *file, bool *empty);
+extern HeapTuple tuplecopy(HeapTuple tup);
+extern FILE *mergeruns(Sort *node);
+extern void merge(Sort *node, struct tape *dest);
+
+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 void resettape(FILE *file);
extern void destroytape(FILE *file);
.\" This is -*-nroff-*-
.\" 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
.SH NAME
postgres \(em the Postgres backend server
.IR "postmaster"
has allocated for the backend server processes that it starts. If the
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
.BR "-F"
Disable automatic fsync() call after each transaction.
.BR "-P" " filedes"
.IR "filedes"
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
useful for interactive use.
.TP
.BR "-Q"
Specifies \*(lqquiet\*(rq mode.
.TP
-.BR "-E"
-Echo all queries.
+.BR "-S"
+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
.BR "-e"
The
.BR "-N"
Disables use of newline as a query delimiter.
.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"
Enables generation of bushy query plan trees (as opposed to left-deep
query plans trees). These query plans are not intended for actual
.\" This is -*-nroff-*-
.\" 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
.SH "NAME"
postmaster \(em run the Postgres postmaster
is the number of shared-memory buffers for the
.IR "postmaster"
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
.BR "-D" " data_dir"
Specifies the directory to use as the root of the tree of database