1. Use qsort for first run

2. Limit number of tuples in leftist trees:
	- put one tuple from current tree to disk if limit reached;
	- end run creation if limit reached by nextrun.
3. Avoid mergeruns() if first run is single one!
This commit is contained in:
Vadim B. Mikheev 1997-09-18 05:37:31 +00:00
parent 303f6514bd
commit 712ea2507e
2 changed files with 261 additions and 86 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.8 1997/09/12 04:08:46 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.9 1997/09/18 05:37:30 vadim Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,15 +26,6 @@
#include "utils/psort.h"
#include "utils/lselect.h"
#define PUTTUP(TUP, FP) fwrite((char *)TUP, (TUP)->t_len, 1, FP)
/*
* USEMEM - record use of memory
* FREEMEM - record freeing of memory
*/
#define USEMEM(context,AMT) context->sortMem -= (AMT)
#define FREEMEM(context,AMT) context->sortMem += (AMT)
/*
* lmerge - merges two leftist trees into one
*
@ -149,8 +140,7 @@ gettuple(struct leftist ** treep,
else
*treep = lmerge(tp->lt_left, tp->lt_right, context);
FREEMEM(context, sizeof(struct leftist));
FREE(tp);
pfree (tp);
return (tup);
}
@ -173,7 +163,6 @@ puttuple(struct leftist ** treep,
register struct leftist *tp;
new1 = (struct leftist *) palloc((unsigned) sizeof(struct leftist));
USEMEM(context, sizeof(struct leftist));
new1->lt_dist = 1;
new1->lt_devnum = devnum;
new1->lt_tuple = newtuple;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.22 1997/09/15 14:28:42 vadim Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.23 1997/09/18 05:37:31 vadim Exp $
*
* NOTES
* Sorts the first relation into the second relation.
@ -64,15 +64,17 @@
#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 bool createfirstrun(Sort * node);
static bool createrun(Sort * node, FILE * file);
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 void initialrun(Sort * node);
static void inittapes(Sort * node);
static void merge(Sort * node, struct tape * dest);
static FILE *mergeruns(Sort * node);
static HeapTuple tuplecopy(HeapTuple tup);
static int _psort_cmp (HeapTuple *ltup, HeapTuple *rtup);
@ -80,6 +82,10 @@ static HeapTuple tuplecopy(HeapTuple tup);
static long shortzero = 0; /* used to delimit runs */
static TupleDesc PsortTupDesc;
static ScanKey PsortKeys; /* used by _psort_cmp */
static int PsortNkeys;
/*
* old psort global variables
*
@ -123,9 +129,8 @@ static long shortzero = 0; /* used to delimit runs */
* 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));
@ -142,17 +147,20 @@ psort_begin(Sort *node, int nkeys, ScanKey key)
PS(node)->treeContext.sortMem = SortMem * 1024;
PS(node)->Tuples = NULL;
PS(node)->lasttuple = NULL;
PS(node)->lt_tupcount = 0;
PS(node)->tupcount = 0;
PS(node)->using_tape_files = false;
PS(node)->psort_grab_file = NULL;
PS(node)->memtuples = NULL;
initialrun(node, &empty);
initialrun(node);
if (empty)
if (PS(node)->tupcount == 0)
return false;
if (PS(node)->using_tape_files)
if (PS(node)->using_tape_files && PS(node)->psort_grab_file == NULL)
PS(node)->psort_grab_file = mergeruns(node);
PS(node)->psort_current = 0;
@ -168,7 +176,7 @@ psort_begin(Sort *node, int nkeys, ScanKey key)
* number of allocated tapes
*/
static void
inittapes(Sort *node)
inittapes(Sort * node)
{
register int i;
register struct tape *tp;
@ -266,7 +274,7 @@ inittapes(Sort *node)
* Also, perhaps allocate tapes when needed. Split into 2 funcs.
*/
static void
initialrun(Sort *node, bool *empty)
initialrun(Sort * node)
{
/* register struct tuple *tup; */
register struct tape *tp;
@ -278,20 +286,27 @@ initialrun(Sort *node, bool *empty)
tp = PS(node)->Tape;
if ((bool) createrun(node, NULL, empty) != false)
if (createfirstrun(node))
{
if (!PS(node)->using_tape_files)
inittapes(node);
Assert (PS(node)->using_tape_files);
extrapasses = 0;
}
else
else /* all tuples fetched */
{
/* if empty or rows fit in memory, we never access tape stuff */
if (*empty || !PS(node)->using_tape_files)
if ( !PS(node)->using_tape_files ) /* empty or sorted in memory */
return;
if (!PS(node)->using_tape_files)
inittapes(node);
extrapasses = 1 + (PS(node)->Tuples != NULL); /* (T != N) ? 2 : 1 */
/*
* if PS(node)->Tuples == NULL then we have single (sorted) run
* which can be used as result grab file! So, we may avoid
* mergeruns - it will just copy this run to new file.
*/
if ( PS(node)->Tuples == NULL )
{
PS(node)->psort_grab_file = PS(node)->Tape->tp_file;
rewind (PS(node)->psort_grab_file);
return;
}
extrapasses = 2;
}
for (;;)
@ -328,7 +343,7 @@ initialrun(Sort *node, bool *empty)
else
break;
if ((bool) createrun(node, tp->tp_file, empty) == false)
if ((bool) createrun(node, tp->tp_file) == false)
extrapasses = 1 + (PS(node)->Tuples != NULL);
/* D2 */
}
@ -336,6 +351,138 @@ initialrun(Sort *node, bool *empty)
rewind(tp->tp_file); /* D. */
}
/*
* createfirstrun - tries to sort tuples in memory using qsort
* until LACKMEM; if not enough memory then switches
* to tape method
*
* Returns:
* FALSE iff process through end of relation
* Tuples contains the tuples for the following run upon exit
*/
static bool
createfirstrun(Sort *node)
{
HeapTuple tup;
bool foundeor = false;
HeapTuple *memtuples;
int t_last = -1;
int t_free = 1000;
TupleTableSlot *cr_slot;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
Assert(!PS(node)->using_tape_files);
Assert(PS(node)->memtuples == NULL);
Assert(PS(node)->tupcount == 0);
if (LACKMEM(node))
elog (FATAL, "psort: LACKMEM in createfirstrun");
memtuples = palloc(t_free * sizeof(HeapTuple));
for (;;)
{
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;
}
tup = tuplecopy(cr_slot->val);
ExecClearTuple(cr_slot);
IncrProcessed();
USEMEM(node, tup->t_len);
TRACEMEM(createfirstrun);
if ( t_free <= 0 )
{
t_free = 1000;
memtuples = repalloc (memtuples,
(t_last + t_free + 1) * sizeof (HeapTuple));
}
t_last++;
t_free--;
memtuples[t_last] = tup;
}
if ( t_last < 0 ) /* empty */
{
Assert (foundeor);
pfree (memtuples);
return (false);
}
t_last++;
PS(node)->tupcount = t_last;
PsortTupDesc = PS(node)->treeContext.tupDesc;
PsortKeys = PS(node)->treeContext.scanKeys;
PsortNkeys = PS(node)->treeContext.nKeys;
qsort (memtuples, t_last, sizeof (HeapTuple),
(int (*)(const void *,const void *))_psort_cmp);
if ( LACKMEM (node) ) /* in-memory sort is impossible */
{
register int t;
register int f;
FILE *file;
Assert (!foundeor);
inittapes(node);
file = PS(node)->Tape->tp_file;
/* put extra tuples into tape file */
if ( t_last > SortTuplesInTree )
{
register HeapTuple lasttuple;
t = t_last - SortTuplesInTree;
for (f = 0, lasttuple = NULL; f < t; f++)
{
if ( lasttuple )
{
FREEMEM(node, lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createfirstrun);
}
lasttuple = memtuples[f];
PUTTUP(node, lasttuple, file);
TRACEOUT(createfirstrun, lasttuple);
}
PS(node)->lasttuple = lasttuple;
}
else
{
PS(node)->lasttuple = NULL;
f = 0;
}
/* put rest of tuples into leftist tree for createrun */
for (t = t_last - 1 ; t >= f; t--)
puttuple(&PS(node)->Tuples, memtuples[t], 0, &PS(node)->treeContext);
PS(node)->lt_tupcount = t_last - f;
pfree (memtuples);
foundeor = !createrun (node, file);
}
else
{
Assert (foundeor);
PS(node)->memtuples = memtuples;
}
return (!foundeor);
}
/*
* createrun - places the next run on file, grabbing the tuples by
* executing the subplan passed in
@ -348,13 +495,15 @@ initialrun(Sort *node, bool *empty)
* Tuples contains the tuples for the following run upon exit
*/
static bool
createrun(Sort *node, FILE *file, bool *empty)
createrun(Sort * node, FILE * file)
{
register HeapTuple lasttuple;
register HeapTuple tup;
struct leftist *nextrun;
bool foundeor;
short junk;
int curr_tupcount = (PS(node)->Tuples != NULL) ? PS(node)->lt_tupcount : 0;
int next_tupcount = 0;
int cr_tuples = 0; /* Count tuples grabbed from plannode */
TupleTableSlot *cr_slot;
@ -362,33 +511,36 @@ createrun(Sort *node, FILE *file, bool *empty)
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
lasttuple = NULL;
lasttuple = PS(node)->lasttuple; /* !NULL if called from createfirstrun */
nextrun = NULL;
foundeor = false;
for (;;)
{
while (LACKMEM(node) && PS(node)->Tuples != NULL)
if ((LACKMEM(node) && PS(node)->Tuples != NULL) || curr_tupcount > SortTuplesInTree)
{
if (lasttuple != NULL)
do
{
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 (lasttuple != NULL)
{
FREEMEM(node, lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createrun);
}
lasttuple = tup = gettuple(&PS(node)->Tuples, &junk,
&PS(node)->treeContext);
Assert (PS(node)->using_tape_files);
PUTTUP(node, tup, file);
TRACEOUT(createrun, tup);
curr_tupcount--;
} while (LACKMEM(node) && PS(node)->Tuples != NULL);
}
if (LACKMEM(node))
break;
if ( next_tupcount >= SortTuplesInTree )
break;
/*
* About to call ExecProcNode, it can mess up the state if it
* eventually calls another Sort node. So must stow it away here
@ -416,9 +568,15 @@ createrun(Sort *node, FILE *file, bool *empty)
TRACEMEM(createrun);
if (lasttuple != NULL && tuplecmp(tup, lasttuple,
&PS(node)->treeContext))
{
puttuple(&nextrun, tup, 0, &PS(node)->treeContext);
next_tupcount++;
}
else
{
puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext);
curr_tupcount++;
}
}
if (lasttuple != NULL)
{
@ -427,15 +585,13 @@ createrun(Sort *node, FILE *file, bool *empty)
TRACEMEM(createrun);
}
dumptuples(file, node);
if (PS(node)->using_tape_files)
ENDRUN(file);
ENDRUN(file);
/* delimit the end of the run */
PS(node)->Tuples = nextrun;
PS(node)->lt_tupcount = next_tupcount;
PS(node)->lasttuple = NULL;
/* if we did not see any tuples, mark empty */
*empty = (cr_tuples > 0) ? false : true;
return ((bool) !foundeor); /* XXX - works iff bool is {0,1} */
return ((bool) ! foundeor); /* XXX - works iff bool is {0,1} */
}
/*
@ -466,7 +622,7 @@ tuplecopy(HeapTuple tup)
* file of tuples in order
*/
static FILE *
mergeruns(Sort *node)
mergeruns(Sort * node)
{
register struct tape *tp;
@ -493,7 +649,7 @@ mergeruns(Sort *node)
* (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]) */
@ -600,20 +756,15 @@ merge(Sort *node, struct tape * dest)
* 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));
}
Assert (PS(node)->using_tape_files);
tp = *treep;
while (tp != NULL)
@ -625,14 +776,9 @@ dumptuples(FILE *file, Sort *node)
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;
PUTTUP(node, tup, file);
FREEMEM(node, tup->t_len);
FREE(tup);
tp = newp;
}
@ -646,7 +792,7 @@ dumptuples(FILE *file, Sort *node)
* a NULL indicating the last tuple has been processed.
*/
HeapTuple
psort_grabtuple(Sort *node, bool *should_free)
psort_grabtuple(Sort * node, bool * should_free)
{
register HeapTuple tup;
long tuplen;
@ -691,7 +837,7 @@ psort_grabtuple(Sort *node, bool *should_free)
* 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);
@ -704,7 +850,7 @@ psort_markpos(Sort *node)
* last saved position
*/
void
psort_restorepos(Sort *node)
psort_restorepos(Sort * node)
{
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
@ -719,13 +865,14 @@ psort_restorepos(Sort *node)
* 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);
/*
* I'm changing this because if we are sorting a relation with no
* tuples, psortstate is NULL.
@ -818,7 +965,7 @@ gettape()
*/
#ifdef NOT_USED
static void
resettape(FILE *file)
resettape(FILE * file)
{
register struct tapelst *tp;
register int fd;
@ -850,7 +997,7 @@ resettape(FILE *file)
* Exits instead of returning status, if given invalid tape.
*/
static void
destroytape(FILE *file)
destroytape(FILE * file)
{
register struct tapelst *tp,
*tq;
@ -885,3 +1032,42 @@ destroytape(FILE *file)
tp = tp->tl_next;
}
}
static int
_psort_cmp (HeapTuple *ltup, HeapTuple *rtup)
{
register Datum lattr, rattr;
int nkey = 0;
int result = 0;
bool isnull1, isnull2;
while ( nkey < PsortNkeys && !result )
{
lattr = heap_getattr(*ltup, InvalidBuffer,
PsortKeys[nkey].sk_attno,
PsortTupDesc,
&isnull1);
rattr = heap_getattr(*rtup, InvalidBuffer,
PsortKeys[nkey].sk_attno,
PsortTupDesc,
&isnull2);
if ( isnull1 )
{
if ( isnull2 )
return (0);
return(1);
}
else if ( isnull2 )
return (-1);
if (PsortKeys[nkey].sk_flags & SK_COMMUTE)
{
if (!(result = -(long) (*PsortKeys[nkey].sk_func) (rattr, lattr)))
result = (long) (*PsortKeys[nkey].sk_func) (lattr, rattr);
}
else if (!(result = -(long) (*PsortKeys[nkey].sk_func) (lattr, rattr)))
result = (long) (*PsortKeys[nkey].sk_func) (rattr, lattr);
nkey++;
}
return (result);
}