diff options
Diffstat (limited to 'src/backend/utils/sort/psort.c')
-rw-r--r-- | src/backend/utils/sort/psort.c | 1311 |
1 files changed, 679 insertions, 632 deletions
diff --git a/src/backend/utils/sort/psort.c b/src/backend/utils/sort/psort.c index 12486166268..00cbfbacba5 100644 --- a/src/backend/utils/sort/psort.c +++ b/src/backend/utils/sort/psort.c @@ -1,36 +1,36 @@ /*------------------------------------------------------------------------- * * psort.c-- - * Polyphase merge sort. + * Polyphase merge sort. * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.18 1997/09/05 00:09:23 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.19 1997/09/07 04:54:17 momjian Exp $ * * NOTES - * Sorts the first relation into the second relation. + * Sorts the first relation into the second relation. * - * The old psort.c's routines formed a temporary relation from the merged + * 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 + * 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, + * 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 + * 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. * - * Arguments? Variables? - * MAXMERGE, MAXTAPES + * Arguments? Variables? + * MAXMERGE, MAXTAPES *------------------------------------------------------------------------- */ #include <stdio.h> @@ -46,11 +46,11 @@ #include "access/htup.h" #include "access/relscan.h" #include "access/skey.h" -#include "utils/tqual.h" /* for NowTimeQual */ +#include "utils/tqual.h" /* for NowTimeQual */ #include "storage/buf.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ -#include "utils/portal.h" /* for {Start,End}PortalAllocMode */ +#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "utils/portal.h" /* for {Start,End}PortalAllocMode */ #include "utils/elog.h" #include "utils/rel.h" @@ -64,47 +64,47 @@ #include "miscadmin.h" #include "storage/fd.h" -static bool createrun(Sort *node, FILE *file, bool *empty); -static void destroytape(FILE *file); -static void dumptuples(FILE *file, Sort *node); -static FILE *gettape(void); -static void initialrun(Sort *node, bool *empty); -static void inittapes(Sort *node); -static void merge(Sort *node, struct tape *dest); -static FILE *mergeruns(Sort *node); +static bool createrun(Sort * node, FILE * file, bool * empty); +static void destroytape(FILE * file); +static void dumptuples(FILE * file, Sort * node); +static FILE *gettape(void); +static void initialrun(Sort * node, bool * empty); +static void inittapes(Sort * node); +static void merge(Sort * node, struct tape * dest); +static FILE *mergeruns(Sort * node); static HeapTuple tuplecopy(HeapTuple tup); -#define TEMPDIR "./" +#define TEMPDIR "./" -static long shortzero = 0; /* used to delimit runs */ +static long shortzero = 0; /* used to delimit runs */ /* * 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) + * 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; + * 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 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; + * static int BytesRead; to keep track of # of IO + * static int BytesWritten; * - * struct leftist *Tuples; current tuples in memory + * 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 + * 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 */ /* @@ -113,730 +113,777 @@ static long shortzero = 0; /* used to delimit runs */ #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. + * 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. + * Allocates and initializes sort node's psort state. */ bool -psort_begin(Sort *node, int nkeys, ScanKey key) +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); - - 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 * 1024; - - 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); - - PS(node)->psort_current = 0; - PS(node)->psort_saved = 0; - - return true; + 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); + + 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 * 1024; + + 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); + + PS(node)->psort_current = 0; + PS(node)->psort_saved = 0; + + return true; } /* - * inittapes - initializes the tapes - * - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270) - * Returns: - * number of allocated tapes + * inittapes - initializes the tapes + * - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270) + * Returns: + * number of allocated tapes */ static void -inittapes(Sort *node) +inittapes(Sort * node) { - register int i; - register struct tape *tp; - - Assert(node != (Sort *) NULL); - Assert(PS(node) != (Psortstate *) NULL); - - /* - ASSERT(ntapes >= 3 && ntapes <= MAXTAPES, - "inittapes: Invalid number of tapes to initialize.\n"); - */ - - 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++; - } - PS(node)->TapeRange = --tp - PS(node)->Tape; - tp->tp_dummy = 0; - tp->tp_fib = 0; - PS(node)->Tape[0].tp_prev = tp; - - if (PS(node)->TapeRange <= 1) - elog(WARN, "inittapes: Could only allocate %d < 3 tapes\n", - PS(node)->TapeRange + 1); - - PS(node)->Level = 1; - PS(node)->TotalDummy = PS(node)->TapeRange; - - PS(node)->using_tape_files = true; + register int i; + register struct tape *tp; + + Assert(node != (Sort *) NULL); + Assert(PS(node) != (Psortstate *) NULL); + + /* + * ASSERT(ntapes >= 3 && ntapes <= MAXTAPES, "inittapes: Invalid + * number of tapes to initialize.\n"); + */ + + 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++; + } + PS(node)->TapeRange = --tp - PS(node)->Tape; + tp->tp_dummy = 0; + tp->tp_fib = 0; + PS(node)->Tape[0].tp_prev = tp; + + if (PS(node)->TapeRange <= 1) + elog(WARN, "inittapes: Could only allocate %d < 3 tapes\n", + PS(node)->TapeRange + 1); + + PS(node)->Level = 1; + PS(node)->TotalDummy = PS(node)->TapeRange; + + PS(node)->using_tape_files = true; } /* - * 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 + * 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 + * Note: + * LEN field must be a short; FP is a stream */ -#define PUTTUP(NODE, TUP, FP) do {\ - ((Psortstate *)NODE->psortstate)->BytesWritten += (TUP)->t_len; \ - fwrite((char *)TUP, (TUP)->t_len, 1, FP);} while (0) -#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(NODE, TUP, LEN, FP) do {\ - IncrProcessed(); \ - ((Psortstate *)NODE->psortstate)->BytesRead += (LEN) - sizeof (shortzero); \ - fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP);} \ - while (0) -#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(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) +#define PUTTUP(NODE, TUP, FP) do {\ + ((Psortstate *)NODE->psortstate)->BytesWritten += (TUP)->t_len; \ + fwrite((char *)TUP, (TUP)->t_len, 1, FP);} while (0) +#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(NODE, TUP, LEN, FP) do {\ + IncrProcessed(); \ + ((Psortstate *)NODE->psortstate)->BytesRead += (LEN) - sizeof (shortzero); \ + fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP);} \ + while (0) +#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(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) /* - * initialrun - distributes tuples from the relation - * - (replacement selection(R2-R3)--Knuth, Vol.3, p.257) - * - (polyphase merge Alg.D(D2-D4)--Knuth, Vol.3, p.271) + * initialrun - distributes tuples from the relation + * - (replacement selection(R2-R3)--Knuth, Vol.3, p.257) + * - (polyphase merge Alg.D(D2-D4)--Knuth, Vol.3, p.271) * - * Explaination: - * Tuples are distributed to the tapes as in Algorithm D. - * A "tuple" with t_size == 0 is used to mark the end of a run. + * Explaination: + * Tuples are distributed to the tapes as in Algorithm D. + * A "tuple" with t_size == 0 is used to mark the end of a run. * - * Note: - * The replacement selection algorithm has been modified - * to go from R1 directly to R3 skipping R2 the first time. + * Note: + * The replacement selection algorithm has been modified + * to go from R1 directly to R3 skipping R2 the first time. * - * Maybe should use closer(rdesc) before return - * Perhaps should adjust the number of tapes if less than n. - * used--v. likely to have problems in mergeruns(). - * Must know if should open/close files before each - * call to psort()? If should--messy?? + * Maybe should use closer(rdesc) before return + * Perhaps should adjust the number of tapes if less than n. + * used--v. likely to have problems in mergeruns(). + * Must know if should open/close files before each + * call to psort()? If should--messy?? * - * Possible optimization: - * put the first xxx runs in quickly--problem here since - * I (perhaps prematurely) combined the 2 algorithms. - * Also, perhaps allocate tapes when needed. Split into 2 funcs. + * Possible optimization: + * put the first xxx runs in quickly--problem here since + * I (perhaps prematurely) combined the 2 algorithms. + * Also, perhaps allocate tapes when needed. Split into 2 funcs. */ static void -initialrun(Sort *node, bool *empty) +initialrun(Sort * node, bool * empty) { - /* register struct tuple *tup; */ - register struct tape *tp; - int baseruns; /* D:(a) */ - int extrapasses; /* EOF */ - - Assert(node != (Sort *) NULL); - Assert(PS(node) != (Psortstate *) NULL); - - tp = PS(node)->Tape; - - if ((bool)createrun(node, NULL, empty) != false) { - if (! PS(node)->using_tape_files) - inittapes(node); - extrapasses = 0; - } - else { - /* if empty or rows fit in memory, we never access tape stuff */ - if (*empty || ! PS(node)->using_tape_files) - return; - if (! PS(node)->using_tape_files) - inittapes(node); - extrapasses = 1 + (PS(node)->Tuples != NULL); /* (T != N) ? 2 : 1 */ - } - - for ( ; ; ) { - tp->tp_dummy--; - PS(node)->TotalDummy--; - if (tp->tp_dummy < (tp + 1)->tp_dummy) - tp++; - else if (tp->tp_dummy != 0) - tp = PS(node)->Tape; - else { - 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 = PS(node)->Tape; /* D4 */ - } /* D3 */ - if (extrapasses) - if (--extrapasses) { - dumptuples(tp->tp_file, node); - ENDRUN(tp->tp_file); - continue; - } else - break; - - if ((bool)createrun(node, tp->tp_file, empty) == false) - extrapasses = 1 + (PS(node)->Tuples != NULL); - /* D2 */ - } - for (tp = PS(node)->Tape + PS(node)->TapeRange; tp >= PS(node)->Tape; tp--) - rewind(tp->tp_file); /* D. */ + /* register struct tuple *tup; */ + register struct tape *tp; + int baseruns; /* D:(a) */ + int extrapasses;/* EOF */ + + Assert(node != (Sort *) NULL); + Assert(PS(node) != (Psortstate *) NULL); + + tp = PS(node)->Tape; + + if ((bool) createrun(node, NULL, empty) != false) + { + if (!PS(node)->using_tape_files) + inittapes(node); + extrapasses = 0; + } + else + { + /* if empty or rows fit in memory, we never access tape stuff */ + if (*empty || !PS(node)->using_tape_files) + return; + if (!PS(node)->using_tape_files) + inittapes(node); + extrapasses = 1 + (PS(node)->Tuples != NULL); /* (T != N) ? 2 : 1 */ + } + + for (;;) + { + tp->tp_dummy--; + PS(node)->TotalDummy--; + if (tp->tp_dummy < (tp + 1)->tp_dummy) + tp++; + else if (tp->tp_dummy != 0) + tp = PS(node)->Tape; + else + { + 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 = PS(node)->Tape;/* D4 */ + } /* D3 */ + if (extrapasses) + if (--extrapasses) + { + dumptuples(tp->tp_file, node); + ENDRUN(tp->tp_file); + continue; + } + else + break; + + if ((bool) createrun(node, tp->tp_file, empty) == false) + extrapasses = 1 + (PS(node)->Tuples != NULL); + /* D2 */ + } + 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, grabbing the tuples by - * executing the subplan passed in + * 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 + * Uses: + * Tuples, which should contain any tuples for this run * - * Returns: - * FALSE iff process through end of relation - * Tuples contains the tuples for the following run upon exit + * Returns: + * FALSE iff process through end of relation + * Tuples contains the tuples for the following run upon exit */ -static bool -createrun(Sort *node, FILE *file, bool *empty) +static bool +createrun(Sort * node, FILE * file, bool * empty) { - register HeapTuple lasttuple; - register HeapTuple tup; - struct leftist *nextrun; - 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(node) && PS(node)->Tuples != NULL) { - if (lasttuple != NULL) { - FREEMEM(node,lasttuple->t_len); - FREE(lasttuple); - TRACEMEM(createrun); - } - lasttuple = tup = gettuple(&PS(node)->Tuples, &junk, - &PS(node)->treeContext); - if (! PS(node)->using_tape_files) { - inittapes(node); - if (! file) - file = PS(node)->Tape->tp_file; /* was NULL */ - } - PUTTUP(node, tup, file); - TRACEOUT(createrun, tup); - } - if (LACKMEM(node)) - break; + register HeapTuple lasttuple; + register HeapTuple tup; + struct leftist *nextrun; + bool foundeor; + short junk; - /* 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 - */ + int cr_tuples = 0; /* Count tuples grabbed from + * plannode */ + TupleTableSlot *cr_slot; - cr_slot = ExecProcNode(outerPlan((Plan *)node), (Plan *)node); + Assert(node != (Sort *) NULL); + Assert(PS(node) != (Psortstate *) NULL); + + lasttuple = NULL; + nextrun = NULL; + foundeor = false; + for (;;) + { + while (LACKMEM(node) && PS(node)->Tuples != NULL) + { + if (lasttuple != NULL) + { + FREEMEM(node, lasttuple->t_len); + FREE(lasttuple); + TRACEMEM(createrun); + } + lasttuple = tup = gettuple(&PS(node)->Tuples, &junk, + &PS(node)->treeContext); + if (!PS(node)->using_tape_files) + { + inittapes(node); + if (!file) + file = PS(node)->Tape->tp_file; /* was NULL */ + } + PUTTUP(node, tup, file); + TRACEOUT(createrun, tup); + } + if (LACKMEM(node)) + break; + + /* + * 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++; + } - if (TupIsNull(cr_slot)) { - foundeor = true; - break; + IncrProcessed(); + USEMEM(node, tup->t_len); + TRACEMEM(createrun); + if (lasttuple != NULL && tuplecmp(tup, lasttuple, + &PS(node)->treeContext)) + puttuple(&nextrun, tup, 0, &PS(node)->treeContext); + else + puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext); } - else { - tup = tuplecopy(cr_slot->val); - ExecClearTuple(cr_slot); - PS(node)->tupcount++; - cr_tuples++; + if (lasttuple != NULL) + { + FREEMEM(node, lasttuple->t_len); + FREE(lasttuple); + TRACEMEM(createrun); } + dumptuples(file, node); + if (PS(node)->using_tape_files) + ENDRUN(file); + /* delimit the end of the run */ + PS(node)->Tuples = nextrun; - IncrProcessed(); - USEMEM(node,tup->t_len); - TRACEMEM(createrun); - if (lasttuple != NULL && tuplecmp(tup, lasttuple, - &PS(node)->treeContext)) - puttuple(&nextrun, tup, 0, &PS(node)->treeContext); - else - puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext); - } - if (lasttuple != NULL) { - FREEMEM(node,lasttuple->t_len); - FREE(lasttuple); - TRACEMEM(createrun); - } - dumptuples(file, node); - if (PS(node)->using_tape_files) - ENDRUN(file); - /* delimit the end of the run */ - 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} */ + /* 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() + * tuplecopy - see also tuple.c:palloctup() * - * This should eventually go there under that name? And this will - * then use palloc directly (see version -r1.2). + * This should eventually go there under that name? And this will + * then use palloc directly (see version -r1.2). */ -static HeapTuple +static HeapTuple tuplecopy(HeapTuple tup) { - HeapTuple rettup; - - if (!HeapTupleIsValid(tup)) { - return(NULL); /* just in case */ - } - rettup = (HeapTuple)palloc(tup->t_len); - memmove((char *)rettup, (char *)tup, tup->t_len); /* XXX */ - return(rettup); + HeapTuple rettup; + + if (!HeapTupleIsValid(tup)) + { + return (NULL); /* just in case */ + } + rettup = (HeapTuple) palloc(tup->t_len); + memmove((char *) rettup, (char *) tup, tup->t_len); /* XXX */ + return (rettup); } /* - * mergeruns - merges all runs from input tapes - * (polyphase merge Alg.D(D6)--Knuth, Vol.3, p271) + * mergeruns - merges all runs from input tapes + * (polyphase merge Alg.D(D6)--Knuth, Vol.3, p271) * - * Returns: - * file of tuples in order + * Returns: + * file of tuples in order */ -static FILE * -mergeruns(Sort *node) +static FILE * +mergeruns(Sort * node) { - register struct tape *tp; + register struct tape *tp; - Assert(node != (Sort *) NULL); - Assert(PS(node) != (Psortstate *) NULL); - Assert(PS(node)->using_tape_files == true); + 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 (--PS(node)->Level != 0) { - tp = tp->tp_prev; - rewind(tp->tp_file); - /* resettape(tp->tp_file); -not sufficient */ + tp = PS(node)->Tape + PS(node)->TapeRange; merge(node, tp); rewind(tp->tp_file); - } - return(tp->tp_file); + while (--PS(node)->Level != 0) + { + tp = tp->tp_prev; + rewind(tp->tp_file); + /* resettape(tp->tp_file); -not sufficient */ + merge(node, tp); + rewind(tp->tp_file); + } + return (tp->tp_file); } /* - * merge - handles a single merge of the tape - * (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271) + * merge - handles a single merge of the tape + * (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271) */ static void -merge(Sort *node, struct tape *dest) +merge(Sort * node, struct tape * dest) { - register HeapTuple tup; - register struct tape *lasttp; /* (TAPE[P]) */ - register struct tape *tp; - struct leftist *tuples; - FILE *destfile; - int times; /* runs left to merge */ - int outdummy; /* complete dummy runs */ - 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; - tp->tp_fib += times; - /* Tape[].tp_fib (A[]) is set to proper exit values */ - - if (PS(node)->TotalDummy < PS(node)->TapeRange)/* no complete dummy runs */ - outdummy = 0; - else { - 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; + register HeapTuple tup; + register struct tape *lasttp; /* (TAPE[P]) */ + register struct tape *tp; + struct leftist *tuples; + FILE *destfile; + int times; /* runs left to merge */ + int outdummy; /* complete dummy runs */ + 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_dummy -= outdummy; - tp->tp_dummy += outdummy; - 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 */ - tuples = NULL; - 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(node,tuplen); - TRACEMEM(merge); - SETTUPLEN(tup, tuplen); - 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--; - PS(node)->TotalDummy--; - } else { - GETLEN(tuplen, tp->tp_file); - tup = ALLOCTUP(tuplen); - USEMEM(node,tuplen); - TRACEMEM(merge); - SETTUPLEN(tup, tuplen); - GETTUP(node, tup, tuplen, tp->tp_file); - puttuple(&tuples, tup, tp - PS(node)->Tape, - &PS(node)->treeContext); + tp->tp_fib -= times; + tp->tp_fib += times; + /* Tape[].tp_fib (A[]) is set to proper exit values */ + + if (PS(node)->TotalDummy < PS(node)->TapeRange) /* no complete dummy + * runs */ + outdummy = 0; + else + { + 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; + 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 */ + tuples = NULL; + 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(node, tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + 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--; + PS(node)->TotalDummy--; + } + else + { + GETLEN(tuplen, tp->tp_file); + tup = ALLOCTUP(tuplen); + USEMEM(node, tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + 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, &PS(node)->treeContext); + PUTTUP(node, tup, destfile); + FREEMEM(node, tup->t_len); + FREE(tup); + TRACEMEM(merge); + GETLEN(tuplen, PS(node)->Tape[fromtape].tp_file); + if (tuplen == 0) + ; + else + { + tup = ALLOCTUP(tuplen); + USEMEM(node, tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + GETTUP(node, tup, tuplen, PS(node)->Tape[fromtape].tp_file); + puttuple(&tuples, tup, fromtape, &PS(node)->treeContext); + } } - } + ENDRUN(destfile); } - while (tuples != NULL) { - /* possible optimization by using count in tuples */ - tup = gettuple(&tuples, &fromtape, &PS(node)->treeContext); - PUTTUP(node, tup, destfile); - FREEMEM(node,tup->t_len); - FREE(tup); - TRACEMEM(merge); - GETLEN(tuplen, PS(node)->Tape[fromtape].tp_file); - if (tuplen == 0) - ; - else { - tup = ALLOCTUP(tuplen); - USEMEM(node,tuplen); - TRACEMEM(merge); - SETTUPLEN(tup, tuplen); - GETTUP(node, tup, tuplen, PS(node)->Tape[fromtape].tp_file); - puttuple(&tuples, tup, fromtape, &PS(node)->treeContext); - } - } - ENDRUN(destfile); - } - PS(node)->TotalDummy += outdummy; + PS(node)->TotalDummy += outdummy; } /* * dumptuples - stores all the tuples in tree into file */ static void -dumptuples(FILE *file, Sort *node) +dumptuples(FILE * file, Sort * node) { - 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 && PS(node)->tupcount) { - Assert(PS(node)->memtuples == NULL); - PS(node)->memtuples = palloc(PS(node)->tupcount * sizeof(HeapTuple)); - } - - 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, file); - FREEMEM(node,tup->t_len); - FREE(tup); + 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 && PS(node)->tupcount) + { + Assert(PS(node)->memtuples == NULL); + PS(node)->memtuples = palloc(PS(node)->tupcount * sizeof(HeapTuple)); } - else - PS(node)->memtuples[memtupindex++] = tup; - tp = newp; - } - *treep = NULL; + 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, file); + FREEMEM(node, tup->t_len); + FREE(tup); + } + 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. + * 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) +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; - } + 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 + * psort_markpos - saves current position in the merged sort file */ void -psort_markpos(Sort *node) +psort_markpos(Sort * node) { - Assert(node != (Sort *) NULL); - Assert(PS(node) != (Psortstate *) NULL); + Assert(node != (Sort *) NULL); + Assert(PS(node) != (Psortstate *) NULL); - PS(node)->psort_saved = PS(node)->psort_current; + PS(node)->psort_saved = PS(node)->psort_current; } /* - * psort_restorepos- restores current position in merged sort file to - * last saved position + * psort_restorepos- restores current position in merged sort file to + * last saved position */ void -psort_restorepos(Sort *node) +psort_restorepos(Sort * node) { - Assert(node != (Sort *) NULL); - Assert(PS(node) != (Psortstate *) NULL); + 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; + 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. + * 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) +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; + 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; + } } - } } /* - * gettape - handles access temporary files in polyphase merging + * gettape - handles access temporary files in polyphase merging * - * Optimizations: - * If guarenteed that only one sort running/process, - * can simplify the file generation--and need not store the - * name for later unlink. + * Optimizations: + * If guarenteed that only one sort running/process, + * can simplify the file generation--and need not store the + * name for later unlink. */ -struct tapelst { - char *tl_name; - int tl_fd; - struct tapelst *tl_next; +struct tapelst +{ + char *tl_name; + int tl_fd; + struct tapelst *tl_next; }; -static struct tapelst *Tapes = NULL; +static struct tapelst *Tapes = NULL; /* - * gettape - returns an open stream for writing/reading + * gettape - returns an open stream for writing/reading * - * Returns: - * Open stream for writing/reading. - * NULL if unable to open temporary file. + * Returns: + * Open stream for writing/reading. + * NULL if unable to open temporary file. */ -static FILE * +static FILE * gettape() { - 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)); - - sprintf(uniqueName, "%spg_psort.%d.%d", TEMPDIR, (int)getpid(), uniqueFileId); - uniqueFileId++; - - tapeinit = 1; - - tp->tl_name = palloc((unsigned)sizeof(uniqueName)); - - /* - * now, copy template with final null into palloc'd space - */ - - memmove(tp->tl_name, uniqueName, strlen(uniqueName)); - - - file = AllocateFile(tp->tl_name, "w+"); - if (file == NULL) - elog(WARN,"Open: %s in %s line %d, %s", tp->tl_name, - __FILE__, __LINE__, strerror(errno)); - - tp->tl_fd = fileno(file); - tp->tl_next = Tapes; - Tapes = tp; - return(file); + 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)); + + sprintf(uniqueName, "%spg_psort.%d.%d", TEMPDIR, (int) getpid(), uniqueFileId); + uniqueFileId++; + + tapeinit = 1; + + tp->tl_name = palloc((unsigned) sizeof(uniqueName)); + + /* + * now, copy template with final null into palloc'd space + */ + + memmove(tp->tl_name, uniqueName, strlen(uniqueName)); + + + file = AllocateFile(tp->tl_name, "w+"); + if (file == NULL) + elog(WARN, "Open: %s in %s line %d, %s", tp->tl_name, + __FILE__, __LINE__, strerror(errno)); + + tp->tl_fd = fileno(file); + tp->tl_next = Tapes; + Tapes = tp; + return (file); } /* - * resettape - resets the tape to size 0 + * resettape - resets the tape to size 0 */ #ifdef NOT_USED static void -resettape(FILE *file) +resettape(FILE * file) { - register struct tapelst *tp; - register int fd; - - Assert(PointerIsValid(file)); - - fd = fileno(file); - for (tp = Tapes; tp != NULL && tp->tl_fd != fd; tp = tp->tl_next) - ; - if (tp == NULL) - elog(WARN, "resettape: tape not found"); - - file = freopen(tp->tl_name, "w+", file); - if (file == NULL) { - elog(FATAL, "could not freopen temporary file"); - } + register struct tapelst *tp; + register int fd; + + Assert(PointerIsValid(file)); + + fd = fileno(file); + for (tp = Tapes; tp != NULL && tp->tl_fd != fd; tp = tp->tl_next) + ; + if (tp == NULL) + elog(WARN, "resettape: tape not found"); + + file = freopen(tp->tl_name, "w+", file); + if (file == NULL) + { + elog(FATAL, "could not freopen temporary file"); + } } + #endif /* - * distroytape - unlinks the tape + * distroytape - unlinks the tape * - * Efficiency note: - * More efficient to destroy more recently allocated tapes first. + * Efficiency note: + * More efficient to destroy more recently allocated tapes first. * - * Possible bugs: - * Exits instead of returning status, if given invalid tape. + * Possible bugs: + * Exits instead of returning status, if given invalid tape. */ static void -destroytape(FILE *file) +destroytape(FILE * file) { - register struct tapelst *tp, *tq; - register int fd; - - if ((tp = Tapes) == NULL) - elog(FATAL, "destroytape: tape not found"); - - if ((fd = fileno(file)) == tp->tl_fd) { - Tapes = tp->tl_next; - FreeFile(file); - unlink(tp->tl_name); - FREE(tp->tl_name); - FREE(tp); - } else - for ( ; ; ) { - if (tp->tl_next == NULL) + register struct tapelst *tp, + *tq; + register int fd; + + if ((tp = Tapes) == NULL) elog(FATAL, "destroytape: tape not found"); - if (tp->tl_next->tl_fd == fd) { + + if ((fd = fileno(file)) == tp->tl_fd) + { + Tapes = tp->tl_next; FreeFile(file); - tq = tp->tl_next; - tp->tl_next = tq->tl_next; - unlink(tq->tl_name); - FREE((tq->tl_name)); - FREE(tq); - break; - } - tp = tp->tl_next; + unlink(tp->tl_name); + FREE(tp->tl_name); + FREE(tp); } + else + for (;;) + { + if (tp->tl_next == NULL) + elog(FATAL, "destroytape: tape not found"); + if (tp->tl_next->tl_fd == fd) + { + FreeFile(file); + tq = tp->tl_next; + tp->tl_next = tq->tl_next; + unlink(tq->tl_name); + FREE((tq->tl_name)); + FREE(tq); + break; + } + tp = tp->tl_next; + } } |