Commit 887afac1 authored by Tom Lane's avatar Tom Lane

Remove now-dead sort modules.

parent 26c48b5e
/*-------------------------------------------------------------------------
*
* lselect.c
* leftist tree selection algorithm (linked priority queue--Knuth, Vol.3,
* pp.150-52)
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.19 1999/07/17 20:18:16 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "utils/lselect.h"
/*
* lmerge - merges two leftist trees into one
*
* Note:
* Enforcing the rule that pt->lt_dist >= qt->lt_dist may
* simplifify much of the code. Removing recursion will not
* speed up code significantly.
*/
struct leftist *
lmerge(struct leftist * pt, struct leftist * qt, LeftistContext context)
{
struct leftist *root,
*majorLeftist,
*minorLeftist;
int dist;
if (tuplecmp(pt->lt_tuple, qt->lt_tuple, context))
{
root = pt;
majorLeftist = qt;
}
else
{
root = qt;
majorLeftist = pt;
}
if (root->lt_left == NULL)
root->lt_left = majorLeftist;
else
{
if ((minorLeftist = root->lt_right) != NULL)
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;
root->lt_left = majorLeftist;
}
else
{
root->lt_dist = 1 + majorLeftist->lt_dist;
root->lt_right = majorLeftist;
}
}
return root;
}
static struct leftist *
linsert(struct leftist * root, struct leftist * new1, LeftistContext context)
{
struct leftist *left,
*right;
if (!tuplecmp(root->lt_tuple, new1->lt_tuple, context))
{
new1->lt_left = root;
return new1;
}
left = root->lt_left;
right = root->lt_right;
if (right == NULL)
{
if (left == NULL)
root->lt_left = new1;
else
{
root->lt_right = new1;
root->lt_dist = 2;
}
return root;
}
right = linsert(right, new1, context);
if (right->lt_dist < left->lt_dist)
{
root->lt_dist = 1 + left->lt_dist;
root->lt_left = right;
root->lt_right = left;
}
else
{
root->lt_dist = 1 + right->lt_dist;
root->lt_right = right;
}
return root;
}
/*
* gettuple - returns tuple at top of tree (Tuples)
*
* Returns:
* tuple at top of tree, NULL if failed ALLOC()
* *devnum is set to the devnum of tuple returned
* *treep is set to the new tree
*
* Note:
* *treep must not be NULL
* NULL is currently never returned BUG
*/
HeapTuple
gettuple(struct leftist ** treep,
short *devnum, /* device from which tuple came */
LeftistContext context)
{
struct leftist *tp;
HeapTuple tup;
tp = *treep;
tup = tp->lt_tuple;
*devnum = tp->lt_devnum;
if (tp->lt_dist == 1) /* lt_left == NULL */
*treep = tp->lt_left;
else
*treep = lmerge(tp->lt_left, tp->lt_right, context);
pfree(tp);
return tup;
}
/*
* puttuple - inserts new tuple into tree
*
* Returns:
* NULL iff failed ALLOC()
*
* Note:
* Currently never returns NULL BUG
*/
void
puttuple(struct leftist ** treep,
HeapTuple newtuple,
short devnum,
LeftistContext context)
{
struct leftist *new1;
struct leftist *tp;
new1 = (struct leftist *) palloc((unsigned) sizeof(struct leftist));
new1->lt_dist = 1;
new1->lt_devnum = devnum;
new1->lt_tuple = newtuple;
new1->lt_left = NULL;
new1->lt_right = NULL;
if ((tp = *treep) == NULL)
*treep = new1;
else
*treep = linsert(tp, new1, context);
return;
}
/*
* tuplecmp - Compares two tuples with respect CmpList
*
* Returns:
* 1 if left < right ;0 otherwise
* Assumtions:
*/
int
tuplecmp(HeapTuple ltup, HeapTuple rtup, LeftistContext context)
{
int nkey;
int result = 0;
if (ltup == (HeapTuple) NULL)
return 0;
if (rtup == (HeapTuple) NULL)
return 1;
for (nkey = 0; nkey < context->nKeys; nkey++)
{
ScanKey thisKey = & context->scanKeys[nkey];
Datum lattr,
rattr;
bool lisnull,
risnull;
lattr = heap_getattr(ltup, thisKey->sk_attno,
context->tupDesc, &lisnull);
rattr = heap_getattr(rtup, thisKey->sk_attno,
context->tupDesc, &risnull);
if (lisnull)
{
if (risnull)
continue; /* treat two nulls as equal */
return 0; /* else, a null sorts after all else */
}
if (risnull)
return 1;
if (thisKey->sk_flags & SK_COMMUTE)
{
if (!(result =
(long) (*fmgr_faddr(&thisKey->sk_func)) (rattr, lattr)))
result =
-(long) (*fmgr_faddr(&thisKey->sk_func)) (lattr, rattr);
}
else
{
if (!(result =
(long) (*fmgr_faddr(&thisKey->sk_func)) (lattr, rattr)))
result =
-(long) (*fmgr_faddr(&thisKey->sk_func)) (rattr, lattr);
}
if (result)
break;
}
return result == 1;
}
#ifdef EBUG
void
checktree(struct leftist * tree, LeftistContext context)
{
int lnodes;
int rnodes;
if (tree == NULL)
{
puts("Null tree.");
return;
}
lnodes = checktreer(tree->lt_left, 1, context);
rnodes = checktreer(tree->lt_right, 1, context);
if (lnodes < 0)
{
lnodes = -lnodes;
puts("0:\tBad left side.");
}
if (rnodes < 0)
{
rnodes = -rnodes;
puts("0:\tBad right side.");
}
if (lnodes == 0)
{
if (rnodes != 0)
puts("0:\tLeft and right reversed.");
if (tree->lt_dist != 1)
puts("0:\tDistance incorrect.");
}
else if (rnodes == 0)
{
if (tree->lt_dist != 1)
puts("0:\tDistance incorrect.");
}
else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist)
{
puts("0:\tLeft and right reversed.");
if (tree->lt_dist != 1 + tree->lt_left->lt_dist)
puts("0:\tDistance incorrect.");
}
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, context))
printf("%d:\tLeft child < parent.\n");
if (rnodes > 0)
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, LeftistContext context)
{
int lnodes,
rnodes;
int error = 0;
if (tree == NULL)
return 0;
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:\tBad left side.\n", level);
}
if (rnodes < 0)
{
error = 1;
rnodes = -rnodes;
printf("%d:\tBad right side.\n", level);
}
if (lnodes == 0)
{
if (rnodes != 0)
{
error = 1;
printf("%d:\tLeft and right reversed.\n", level);
}
if (tree->lt_dist != 1)
{
error = 1;
printf("%d:\tDistance incorrect.\n", level);
}
}
else if (rnodes == 0)
{
if (tree->lt_dist != 1)
{
error = 1;
printf("%d:\tDistance incorrect.\n", level);
}
}
else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist)
{
error = 1;
printf("%d:\tLeft and right reversed.\n", level);
if (tree->lt_dist != 1 + tree->lt_left->lt_dist)
printf("%d:\tDistance incorrect.\n", level);
}
else if (tree->lt_dist != 1 + tree->lt_right->lt_dist)
{
error = 1;
printf("%d:\tDistance incorrect.\n", level);
}
if (lnodes > 0)
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, context))
{
error = 1;
printf("%d:\tRight child < parent.\n");
}
if (error)
return -1 + -lnodes + -rnodes;
return 1 + lnodes + rnodes;
}
#endif
/*-------------------------------------------------------------------------
*
* psort.c
* Polyphase merge sort.
*
* See Knuth, volume 3, for more than you want to know about this algorithm.
*
* NOTES
*
* This needs to be generalized to handle index tuples as well as heap tuples,
* so that the near-duplicate code in nbtsort.c can be eliminated. Also,
* I think it's got memory leak problems.
*
* Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.58 1999/10/16 19:49:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
#include "postgres.h"
#include "access/heapam.h"
#include "access/relscan.h"
#include "executor/execdebug.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "utils/logtape.h"
#include "utils/lselect.h"
#include "utils/psort.h"
#define MAXTAPES 7 /* See Knuth Fig. 70, p273 */
struct tape
{
int tp_dummy; /* (D) */
int tp_fib; /* (A) */
int tp_tapenum; /* (TAPE) */
struct tape *tp_prev;
};
/*
* Private state of a Psort operation. The "psortstate" field in a Sort node
* points to one of these. This replaces a lot of global variables that used
* to be here...
*/
typedef struct Psortstate
{
LeftistContextData treeContext;
int TapeRange; /* number of tapes less 1 (T) */
int Level; /* Knuth's l */
int TotalDummy; /* sum of tp_dummy across all tapes */
struct tape Tape[MAXTAPES];
LogicalTapeSet *tapeset; /* logtape.c object for tapes in a temp file */
int BytesRead; /* I/O statistics (useless) */
int BytesWritten;
int tupcount;
struct leftist *Tuples; /* current tuple tree */
int psort_grab_tape; /* tape number of finished output data */
long psort_current; /* array index (only used if not tape) */
/* psort_saved(_offset) holds marked position for mark and restore */
long psort_saved; /* could be tape block#, or array index */
int psort_saved_offset; /* lower bits of psort_saved, if tape */
bool using_tape_files;
bool all_fetched; /* this is for cursors */
HeapTuple *memtuples;
} Psortstate;
/*
* PS - Macro to access and cast psortstate from a Sort node
*/
#define PS(N) ((Psortstate *)(N)->psortstate)
static bool createfirstrun(Sort *node);
static bool createrun(Sort *node, int desttapenum);
static void dumptuples(Sort *node, int desttapenum);
static void initialrun(Sort *node);
static void inittapes(Sort *node);
static void merge(Sort *node, struct tape * dest);
static int mergeruns(Sort *node);
static int _psort_cmp(HeapTuple *ltup, HeapTuple *rtup);
/* these are used by _psort_cmp, and are set just before calling qsort() */
static TupleDesc PsortTupDesc;
static ScanKey PsortKeys;
static int PsortNkeys;
/*
* tlenzero is used to write a zero to delimit runs, tlendummy is used
* to read in length words that we don't care about.
*
* both vars must have the same size as HeapTuple->t_len
*/
static unsigned int tlenzero = 0;
static unsigned int tlendummy;
/*
* psort_begin
*
* polyphase merge sort entry point. Sorts the subplan
* into memory or a temporary file. After
* this is called, calling the interface function
* psort_grabtuple iteratively will get you the sorted
* tuples. psort_end releases storage when done.
*
* Allocates and initializes sort node's psort state.
*/
bool
psort_begin(Sort *node, int nkeys, ScanKey key)
{
AssertArg(nkeys >= 1);
AssertArg(key[0].sk_attno != 0);
AssertArg(key[0].sk_procedure != 0);
node->psortstate = (void *) palloc(sizeof(struct Psortstate));
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)->tapeset = NULL;
PS(node)->BytesRead = 0;
PS(node)->BytesWritten = 0;
PS(node)->tupcount = 0;
PS(node)->Tuples = NULL;
PS(node)->using_tape_files = false;
PS(node)->all_fetched = false;
PS(node)->psort_grab_tape = -1;
PS(node)->memtuples = NULL;
initialrun(node);
if (PS(node)->tupcount == 0)
return false;
if (PS(node)->using_tape_files && PS(node)->psort_grab_tape == -1)
PS(node)->psort_grab_tape = mergeruns(node);
PS(node)->psort_current = 0L;
PS(node)->psort_saved = 0L;
PS(node)->psort_saved_offset = 0;
return true;
}
/*
* inittapes - initializes the tapes
* - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
*
* This is called only if we have found we don't have room to sort in memory.
*/
static void
inittapes(Sort *node)
{
int i;
struct tape *tp;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
PS(node)->tapeset = LogicalTapeSetCreate(MAXTAPES);
tp = PS(node)->Tape;
for (i = 0; i < MAXTAPES; i++)
{
tp->tp_dummy = 1;
tp->tp_fib = 1;
tp->tp_tapenum = i;
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;
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
* TRYGETLEN - reads the length of the next tuple, if any
* GETLEN - reads the length of the next tuple, must be one
* ALLOCTUP - returns space for the new tuple
* GETTUP - reads the tuple
*
* Note:
* LEN field must be as HeapTuple->t_len; FP is a stream
*/
#define PUTTUP(NODE, TUP, TAPE) \
( \
(TUP)->t_len += HEAPTUPLESIZE, \
PS(NODE)->BytesWritten += (TUP)->t_len, \
LogicalTapeWrite(PS(NODE)->tapeset, (TAPE), (void*)(TUP), (TUP)->t_len), \
LogicalTapeWrite(PS(NODE)->tapeset, (TAPE), (void*)&((TUP)->t_len), sizeof(tlendummy)), \
(TUP)->t_len -= HEAPTUPLESIZE \
)
#define ENDRUN(NODE, TAPE) \
LogicalTapeWrite(PS(NODE)->tapeset, (TAPE), (void *)&tlenzero, sizeof(tlenzero))
#define TRYGETLEN(NODE, LEN, TAPE) \
(LogicalTapeRead(PS(NODE)->tapeset, (TAPE), \
(void *) &(LEN), sizeof(tlenzero)) == sizeof(tlenzero) \
&& (LEN) != 0)
#define GETLEN(NODE, LEN, TAPE) \
do { \
if (! TRYGETLEN(NODE, LEN, TAPE)) \
elog(ERROR, "psort: unexpected end of data"); \
} while(0)
static void GETTUP(Sort *node, HeapTuple tup, unsigned int len, int tape)
{
IncrProcessed();
PS(node)->BytesRead += len;
if (LogicalTapeRead(PS(node)->tapeset, tape,
((char *) tup) + sizeof(tlenzero),
len - sizeof(tlenzero)) != len - sizeof(tlenzero))
elog(ERROR, "psort: unexpected end of data");
tup->t_len = len - HEAPTUPLESIZE;
tup->t_data = (HeapTupleHeader) ((char *) tup + HEAPTUPLESIZE);
if (LogicalTapeRead(PS(node)->tapeset, tape,
(void *) &tlendummy,
sizeof(tlendummy)) != sizeof(tlendummy))
elog(ERROR, "psort: unexpected end of data");
}
#define ALLOCTUP(LEN) ((HeapTuple) palloc(LEN))
#define FREE(x) pfree((char *) (x))
/*
* 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 <= BLCKSZ) /* 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)
*
* Explanation:
* 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.
*
* 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.
*/
static void
initialrun(Sort *node)
{
struct tape *tp;
int baseruns; /* D:(a) */
int extrapasses; /* EOF */
int tapenum;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
tp = PS(node)->Tape;
if (createfirstrun(node))
{
Assert(PS(node)->using_tape_files);
extrapasses = 0;
}
else
{
/* all tuples fetched */
if (!PS(node)->using_tape_files) /* empty or sorted in
* memory */
return;
/*
* 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_tape = PS(node)->Tape[0].tp_tapenum;
/* freeze and rewind the finished output tape */
LogicalTapeFreeze(PS(node)->tapeset, PS(node)->psort_grab_tape);
return;
}
extrapasses = 2;
}
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(node, tp->tp_tapenum);
ENDRUN(node, tp->tp_tapenum);
continue;
}
else
break;
}
if (createrun(node, tp->tp_tapenum) == false)
extrapasses = 1 + (PS(node)->Tuples != NULL);
/* D2 */
}
/* End of step D2: rewind all output tapes to prepare for merging */
for (tapenum = 0; tapenum < PS(node)->TapeRange; tapenum++)
LogicalTapeRewind(PS(node)->tapeset, tapenum, false);
}
/*
* 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(ERROR, "psort: LACKMEM before 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 = heap_copytuple(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 */
{
int t;
Assert(!foundeor);
inittapes(node);
/* put tuples into leftist tree for createrun */
for (t = t_last - 1; t >= 0; t--)
puttuple(&PS(node)->Tuples, memtuples[t], 0, &PS(node)->treeContext);
pfree(memtuples);
foundeor = ! createrun(node, PS(node)->Tape->tp_tapenum);
}
else
{
Assert(foundeor);
PS(node)->memtuples = memtuples;
}
return !foundeor;
}
/*
* createrun
*
* Create the next run and write it to desttapenum, grabbing the tuples by
* executing the subplan passed in
*
* 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
*/
static bool
createrun(Sort *node, int desttapenum)
{
HeapTuple lasttuple;
HeapTuple tup;
TupleTableSlot *cr_slot;
HeapTuple *memtuples;
int t_last = -1;
int t_free = 1000;
bool foundeor = false;
short junk;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
Assert(PS(node)->using_tape_files);
lasttuple = NULL;
memtuples = palloc(t_free * sizeof(HeapTuple));
for (;;)
{
while (LACKMEM(node) && PS(node)->Tuples != NULL)
{
if (lasttuple != NULL)
{
FREEMEM(node, lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createrun);
}
lasttuple = gettuple(&PS(node)->Tuples, &junk,
&PS(node)->treeContext);
PUTTUP(node, lasttuple, desttapenum);
TRACEOUT(createrun, lasttuple);
}
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 = heap_copytuple(cr_slot->val);
ExecClearTuple(cr_slot);
PS(node)->tupcount++;
}
IncrProcessed();
USEMEM(node, tup->t_len);
TRACEMEM(createrun);
if (lasttuple != NULL && tuplecmp(tup, lasttuple,
&PS(node)->treeContext))
{
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;
}
else
puttuple(&PS(node)->Tuples, tup, 0, &PS(node)->treeContext);
}
if (lasttuple != NULL)
{
FREEMEM(node, lasttuple->t_len);
FREE(lasttuple);
TRACEMEM(createrun);
}
dumptuples(node, desttapenum);
ENDRUN(node, desttapenum); /* delimit the end of the run */
t_last++;
/* put tuples for the next run into leftist tree */
if (t_last >= 1)
{
int t;
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);
for (t = t_last - 1; t >= 0; t--)
puttuple(&PS(node)->Tuples, memtuples[t], 0, &PS(node)->treeContext);
}
pfree(memtuples);
return !foundeor;
}
/*
* mergeruns - merges all runs from input tapes
* (polyphase merge Alg.D(D6)--Knuth, Vol.3, p271)
*
* Returns:
* tape number of finished tape containing all tuples in order
*/
static int
mergeruns(Sort *node)
{
struct tape *tp;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
Assert(PS(node)->using_tape_files);
tp = PS(node)->Tape + PS(node)->TapeRange;
merge(node, tp);
while (--PS(node)->Level != 0)
{
/* rewind output tape to use as new input */
LogicalTapeRewind(PS(node)->tapeset, tp->tp_tapenum, false);
tp = tp->tp_prev;
/* rewind new output tape and prepare it for write pass */
LogicalTapeRewind(PS(node)->tapeset, tp->tp_tapenum, true);
merge(node, tp);
}
/* freeze and rewind the final output tape */
LogicalTapeFreeze(PS(node)->tapeset, tp->tp_tapenum);
return tp->tp_tapenum;
}
/*
* 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)
{
HeapTuple tup;
struct tape *lasttp; /* (TAPE[P]) */
struct tape *tp;
struct leftist *tuples;
int desttapenum;
int times; /* runs left to merge */
int outdummy; /* complete dummy runs */
short fromtape;
unsigned int tuplen;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
Assert(PS(node)->using_tape_files);
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;
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;
}
desttapenum = dest->tp_tapenum;
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(node, tuplen, tp->tp_tapenum);
tup = ALLOCTUP(tuplen);
USEMEM(node, tuplen);
TRACEMEM(merge);
GETTUP(node, tup, tuplen, tp->tp_tapenum);
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(node, tuplen, tp->tp_tapenum);
tup = ALLOCTUP(tuplen);
USEMEM(node, tuplen);
TRACEMEM(merge);
GETTUP(node, tup, tuplen, tp->tp_tapenum);
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, desttapenum);
FREEMEM(node, tup->t_len);
FREE(tup);
TRACEMEM(merge);
if (TRYGETLEN(node, tuplen, PS(node)->Tape[fromtape].tp_tapenum))
{
tup = ALLOCTUP(tuplen);
USEMEM(node, tuplen);
TRACEMEM(merge);
GETTUP(node, tup, tuplen, PS(node)->Tape[fromtape].tp_tapenum);
puttuple(&tuples, tup, fromtape, &PS(node)->treeContext);
}
}
ENDRUN(node, desttapenum);
}
PS(node)->TotalDummy += outdummy;
}
/*
* dumptuples - stores all the tuples remaining in tree to dest tape
*/
static void
dumptuples(Sort *node, int desttapenum)
{
LeftistContext context = &PS(node)->treeContext;
struct leftist **treep = &PS(node)->Tuples;
struct leftist *tp;
struct leftist *newp;
HeapTuple tup;
Assert(PS(node)->using_tape_files);
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);
pfree(tp);
PUTTUP(node, tup, desttapenum);
FREEMEM(node, tup->t_len);
FREE(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, bool *should_free)
{
HeapTuple tup;
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
if (PS(node)->using_tape_files == true)
{
unsigned int tuplen;
*should_free = true;
if (ScanDirectionIsForward(node->plan.state->es_direction))
{
if (PS(node)->all_fetched)
return NULL;
if (TRYGETLEN(node, tuplen, PS(node)->psort_grab_tape))
{
tup = ALLOCTUP(tuplen);
GETTUP(node, tup, tuplen, PS(node)->psort_grab_tape);
return tup;
}
else
{
PS(node)->all_fetched = true;
return NULL;
}
}
/* Backward.
*
* if all tuples are fetched already then we return last tuple,
* else - tuple before last returned.
*/
if (PS(node)->all_fetched)
{
/*
* Assume seek position is pointing just past the zero tuplen
* at the end of file; back up and fetch last tuple's ending
* length word. If seek fails we must have a completely empty
* file.
*/
if (! LogicalTapeBackspace(PS(node)->tapeset,
PS(node)->psort_grab_tape,
2 * sizeof(tlendummy)))
return NULL;
GETLEN(node, tuplen, PS(node)->psort_grab_tape);
PS(node)->all_fetched = false;
}
else
{
/*
* Back up and fetch prev tuple's ending length word.
* If seek fails, assume we are at start of file.
*/
if (! LogicalTapeBackspace(PS(node)->tapeset,
PS(node)->psort_grab_tape,
sizeof(tlendummy)))
return NULL;
GETLEN(node, tuplen, PS(node)->psort_grab_tape);
/*
* Back up to get ending length word of tuple before it.
*/
if (! LogicalTapeBackspace(PS(node)->tapeset,
PS(node)->psort_grab_tape,
tuplen + 2*sizeof(tlendummy)))
{
/* If fail, presumably the prev tuple is the first in the file.
* Back up so that it becomes next to read in forward direction
* (not obviously right, but that is what in-memory case does)
*/
if (! LogicalTapeBackspace(PS(node)->tapeset,
PS(node)->psort_grab_tape,
tuplen + sizeof(tlendummy)))
elog(ERROR, "psort_grabtuple: too big last tuple len in backward scan");
return NULL;
}
GETLEN(node, tuplen, PS(node)->psort_grab_tape);
}
/*
* Now we have the length of the prior tuple, back up and read it.
* Note: GETTUP expects we are positioned after the initial length
* word of the tuple, so back up to that point.
*/
if (! LogicalTapeBackspace(PS(node)->tapeset,
PS(node)->psort_grab_tape,
tuplen))
elog(ERROR, "psort_grabtuple: too big tuple len in backward scan");
tup = ALLOCTUP(tuplen);
GETTUP(node, tup, tuplen, PS(node)->psort_grab_tape);
return tup;
}
else
{
*should_free = false;
if (ScanDirectionIsForward(node->plan.state->es_direction))
{
if (PS(node)->psort_current < PS(node)->tupcount)
return PS(node)->memtuples[PS(node)->psort_current++];
else
{
PS(node)->all_fetched = true;
return NULL;
}
}
/* Backward */
if (PS(node)->psort_current <= 0)
return NULL;
/*
* if all tuples are fetched already then we return last tuple,
* else - tuple before last returned.
*/
if (PS(node)->all_fetched)
PS(node)->all_fetched = false;
else
{
PS(node)->psort_current--; /* last returned tuple */
if (PS(node)->psort_current <= 0)
return NULL;
}
return PS(node)->memtuples[PS(node)->psort_current - 1];
}
}
/*
* psort_markpos - saves current position in the merged sort file
*
* XXX I suspect these need to save & restore the all_fetched flag as well!
*/
void
psort_markpos(Sort *node)
{
Assert(node != (Sort *) NULL);
Assert(PS(node) != (Psortstate *) NULL);
if (PS(node)->using_tape_files == true)
LogicalTapeTell(PS(node)->tapeset,
PS(node)->psort_grab_tape,
& PS(node)->psort_saved,
& PS(node)->psort_saved_offset);
else
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)
{
if (! LogicalTapeSeek(PS(node)->tapeset,
PS(node)->psort_grab_tape,
PS(node)->psort_saved,
PS(node)->psort_saved_offset))
elog(ERROR, "psort_restorepos failed");
}
else
PS(node)->psort_current = PS(node)->psort_saved;
}
/*
* psort_end
*
* Release resources and clean up.
*/
void
psort_end(Sort *node)
{
/* node->cleaned is probably redundant? */
if (!node->cleaned && PS(node) != (Psortstate *) NULL)
{
if (PS(node)->tapeset)
LogicalTapeSetClose(PS(node)->tapeset);
if (PS(node)->memtuples)
pfree(PS(node)->memtuples);
/* XXX what about freeing leftist tree and tuples in memory? */
NDirectFileRead += (int) ceil((double) PS(node)->BytesRead / BLCKSZ);
NDirectFileWrite += (int) ceil((double) PS(node)->BytesWritten / BLCKSZ);
pfree((void *) node->psortstate);
node->psortstate = NULL;
node->cleaned = TRUE;
}
}
void
psort_rescan(Sort *node)
{
/*
* If subnode is to be rescanned then free our previous results
*/
if (((Plan *) node)->lefttree->chgParam != NULL)
{
psort_end(node);
node->cleaned = false; /* huh? */
}
else if (PS(node) != (Psortstate *) NULL)
{
PS(node)->all_fetched = false;
PS(node)->psort_current = 0;
PS(node)->psort_saved = 0L;
PS(node)->psort_saved_offset = 0;
if (PS(node)->using_tape_files == true)
LogicalTapeRewind(PS(node)->tapeset,
PS(node)->psort_grab_tape,
false);
}
}
static int
_psort_cmp(HeapTuple *ltup, HeapTuple *rtup)
{
Datum lattr,
rattr;
int nkey;
int result = 0;
bool isnull1,
isnull2;
for (nkey = 0; nkey < PsortNkeys && !result; nkey++)
{
lattr = heap_getattr(*ltup,
PsortKeys[nkey].sk_attno,
PsortTupDesc,
&isnull1);
rattr = heap_getattr(*rtup,
PsortKeys[nkey].sk_attno,
PsortTupDesc,
&isnull2);
if (isnull1)
{
if (!isnull2)
result = 1;
}
else if (isnull2)
result = -1;
else if (PsortKeys[nkey].sk_flags & SK_COMMUTE)
{
if (!(result = -(long) (*fmgr_faddr(&PsortKeys[nkey].sk_func)) (rattr, lattr)))
result = (long) (*fmgr_faddr(&PsortKeys[nkey].sk_func)) (lattr, rattr);
}
else if (!(result = -(long) (*fmgr_faddr(&PsortKeys[nkey].sk_func)) (lattr, rattr)))
result = (long) (*fmgr_faddr(&PsortKeys[nkey].sk_func)) (rattr, lattr);
}
return result;
}
/*-------------------------------------------------------------------------
*
* qsort.h
*
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: qsort.h,v 1.7 1999/02/13 23:21:32 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef QSORT_H
#define QSORT_H
extern void pg_qsort(void *bot,
size_t nmemb,
size_t size,
int (*compar) (void *, void *));
#endif /* QSORT_H */
/*-------------------------------------------------------------------------
*
* lselect.h
* definitions for the replacement selection algorithm.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: lselect.h,v 1.14 1999/07/17 20:18:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef LSELECT_H
#define LSELECT_H
#include "utils/syscache.h"
struct leftist
{
short lt_dist; /* distance to leaf/empty node */
short lt_devnum; /* device number of tuple */
HeapTuple lt_tuple;
struct leftist *lt_left;
struct leftist *lt_right;
};
/* 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,
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, LeftistContext context);
extern int checktreer(struct leftist * tree, int level, LeftistContext context);
#endif /* EBUG */
#endif /* LSELECT_H */
/*-------------------------------------------------------------------------
*
* psort.h
* Polyphase merge sort.
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: psort.h,v 1.23 1999/10/16 19:49:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PSORT_H
#define PSORT_H
#include "access/htup.h"
#include "access/skey.h"
#include "nodes/plannodes.h"
extern bool psort_begin(Sort *node, int nkeys, ScanKey key);
extern HeapTuple psort_grabtuple(Sort *node, bool *should_free);
extern void psort_markpos(Sort *node);
extern void psort_restorepos(Sort *node);
extern void psort_end(Sort *node);
extern void psort_rescan(Sort *node);
#endif /* PSORT_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment