mirror of
https://github.com/postgres/postgres.git
synced 2025-11-12 05:01:15 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
14
src/backend/utils/sort/Makefile.inc
Normal file
14
src/backend/utils/sort/Makefile.inc
Normal file
@@ -0,0 +1,14 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for utils/sort
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= lselect.c psort.c
|
||||
365
src/backend/utils/sort/lselect.c
Normal file
365
src/backend/utils/sort/lselect.c
Normal file
@@ -0,0 +1,365 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* 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.1.1.1 1996/07/09 06:22:10 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "c.h"
|
||||
|
||||
#include "storage/buf.h"
|
||||
#include "access/skey.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
#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)malloc((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 */
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
register struct leftist *root, *majorLeftist, *minorLeftist;
|
||||
int dist;
|
||||
|
||||
if (tuplecmp(pt->lt_tuple, qt->lt_tuple)) {
|
||||
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);
|
||||
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)
|
||||
{
|
||||
register struct leftist *left, *right;
|
||||
|
||||
if (! tuplecmp(root->lt_tuple, new1->lt_tuple)) {
|
||||
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);
|
||||
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 */
|
||||
{
|
||||
register 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);
|
||||
|
||||
FREEMEM(sizeof (struct leftist));
|
||||
FREE(tp);
|
||||
return(tup);
|
||||
}
|
||||
|
||||
/*
|
||||
* puttuple - inserts new tuple into tree
|
||||
*
|
||||
* Returns:
|
||||
* NULL iff failed ALLOC()
|
||||
*
|
||||
* Note:
|
||||
* Currently never returns NULL BUG
|
||||
*/
|
||||
int
|
||||
puttuple(struct leftist **treep, HeapTuple newtuple, int devnum)
|
||||
{
|
||||
register struct leftist *new1;
|
||||
register struct leftist *tp;
|
||||
|
||||
new1 = (struct leftist *) malloc((unsigned) sizeof (struct leftist));
|
||||
USEMEM(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);
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Returns:
|
||||
* 1 if left < right ;0 otherwise
|
||||
* Assumtions:
|
||||
*/
|
||||
int
|
||||
tuplecmp(HeapTuple ltup, HeapTuple rtup)
|
||||
{
|
||||
register char *lattr, *rattr;
|
||||
int nkey = 0;
|
||||
extern int Nkeys;
|
||||
extern ScanKey Key;
|
||||
int result = 0;
|
||||
bool isnull;
|
||||
|
||||
if (ltup == (HeapTuple)NULL)
|
||||
return(0);
|
||||
if (rtup == (HeapTuple)NULL)
|
||||
return(1);
|
||||
while (nkey < Nkeys && !result) {
|
||||
lattr = heap_getattr(ltup, InvalidBuffer,
|
||||
Key[nkey].sk_attno,
|
||||
RelationGetTupleDescriptor(SortRdesc),
|
||||
&isnull);
|
||||
if (isnull)
|
||||
return(0);
|
||||
rattr = heap_getattr(rtup, InvalidBuffer,
|
||||
Key[nkey].sk_attno,
|
||||
RelationGetTupleDescriptor(SortRdesc),
|
||||
&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);
|
||||
nkey++;
|
||||
}
|
||||
return (result == 1);
|
||||
}
|
||||
|
||||
#ifdef EBUG
|
||||
void
|
||||
checktree(struct leftist *tree)
|
||||
{
|
||||
int lnodes;
|
||||
int rnodes;
|
||||
|
||||
if (tree == NULL) {
|
||||
puts("Null tree.");
|
||||
return;
|
||||
}
|
||||
lnodes = checktreer(tree->lt_left, 1);
|
||||
rnodes = checktreer(tree->lt_right, 1);
|
||||
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))
|
||||
printf("%d:\tLeft child < parent.\n");
|
||||
if (rnodes > 0)
|
||||
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple))
|
||||
printf("%d:\tRight child < parent.\n");
|
||||
printf("Tree has %d nodes\n", 1 + lnodes + rnodes);
|
||||
}
|
||||
|
||||
int
|
||||
checktreer(struct leftist *tree, int level)
|
||||
{
|
||||
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);
|
||||
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)) {
|
||||
error = 1;
|
||||
printf("%d:\tLeft child < parent.\n");
|
||||
}
|
||||
if (rnodes > 0)
|
||||
if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple)) {
|
||||
error = 1;
|
||||
printf("%d:\tRight child < parent.\n");
|
||||
}
|
||||
if (error)
|
||||
return(-1 + -lnodes + -rnodes);
|
||||
return(1 + lnodes + rnodes);
|
||||
}
|
||||
#endif
|
||||
617
src/backend/utils/sort/psort.c
Normal file
617
src/backend/utils/sort/psort.c
Normal file
@@ -0,0 +1,617 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* psort.c--
|
||||
* 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.1.1.1 1996/07/09 06:22:10 scrappy Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Sorts the first relation into the second relation. The sort may
|
||||
* not be called twice simultaneously.
|
||||
*
|
||||
* Use the tape-splitting method (Knuth, Vol. III, pp281-86) in the future.
|
||||
*
|
||||
* Arguments? Variables?
|
||||
* MAXMERGE, MAXTAPES
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "c.h"
|
||||
|
||||
#include "executor/execdebug.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/relscan.h"
|
||||
#include "access/skey.h"
|
||||
#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 "utils/elog.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
#include "utils/psort.h"
|
||||
#include "utils/lselect.h"
|
||||
|
||||
#include "storage/fd.h"
|
||||
|
||||
#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 */
|
||||
|
||||
static int BytesRead; /* to keep track of # of IO */
|
||||
static int BytesWritten;
|
||||
|
||||
Relation SortRdesc; /* current tuples in memory */
|
||||
struct leftist *Tuples; /* current tuples in memory */
|
||||
|
||||
/*
|
||||
* psort - polyphase merge sort entry point
|
||||
*/
|
||||
void
|
||||
psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* TAPENO - number of tape in Tape
|
||||
*/
|
||||
|
||||
#define TAPENO(NODE) (NODE - Tape)
|
||||
#define TUPLENO(TUP) ((TUP == NULL) ? -1 : (int) TUP->t_iid)
|
||||
|
||||
/*
|
||||
* initpsort - initializes the tapes
|
||||
* - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270)
|
||||
* Returns:
|
||||
* number of allocated tapes
|
||||
*/
|
||||
void
|
||||
initpsort()
|
||||
{
|
||||
register int i;
|
||||
register struct tape *tp;
|
||||
|
||||
/*
|
||||
ASSERT(ntapes >= 3 && ntapes <= MAXTAPES,
|
||||
"initpsort: Invalid number of tapes to initialize.\n");
|
||||
*/
|
||||
|
||||
tp = 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;
|
||||
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);
|
||||
|
||||
Level = 1;
|
||||
TotalDummy = TapeRange;
|
||||
|
||||
SortMemory = SORTMEM;
|
||||
LastTuple = NULL;
|
||||
Tuples = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* resetpsort - resets (frees) malloc'd memory for an aborted Xaction
|
||||
*
|
||||
* Not implemented yet.
|
||||
*/
|
||||
void
|
||||
resetpsort()
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)\
|
||||
BytesWritten += (TUP)->t_len; \
|
||||
fwrite((char *)TUP, (TUP)->t_len, 1, FP)
|
||||
#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)malloc((unsigned)LEN))
|
||||
#define GETTUP(TUP, LEN, FP)\
|
||||
IncrProcessed(); \
|
||||
BytesRead += (LEN) - sizeof (shortzero); \
|
||||
fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, 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 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)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void
|
||||
initialrun(Relation rdesc)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
if ((bool)createrun(sdesc, tp->tp_file) != false)
|
||||
morepasses = 0;
|
||||
else
|
||||
morepasses = 1 + (Tuples != NULL); /* (T != N) ? 2 : 1 */
|
||||
|
||||
for ( ; ; ) {
|
||||
tp->tp_dummy--;
|
||||
TotalDummy--;
|
||||
if (tp->tp_dummy < (tp + 1)->tp_dummy)
|
||||
tp++;
|
||||
else if (tp->tp_dummy != 0)
|
||||
tp = Tape;
|
||||
else {
|
||||
Level++;
|
||||
baseruns = Tape[0].tp_fib;
|
||||
for (tp = Tape; tp - Tape < TapeRange; tp++) {
|
||||
TotalDummy +=
|
||||
(tp->tp_dummy = baseruns
|
||||
+ (tp + 1)->tp_fib
|
||||
- tp->tp_fib);
|
||||
tp->tp_fib = baseruns
|
||||
+ (tp + 1)->tp_fib;
|
||||
}
|
||||
tp = Tape; /* D4 */
|
||||
} /* D3 */
|
||||
if (morepasses)
|
||||
if (--morepasses) {
|
||||
dumptuples(tp->tp_file);
|
||||
ENDRUN(tp->tp_file);
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
if ((bool)createrun(sdesc, tp->tp_file) == false)
|
||||
morepasses = 1 + (Tuples != NULL);
|
||||
/* D2 */
|
||||
}
|
||||
for (tp = Tape + TapeRange; tp >= Tape; tp--)
|
||||
rewind(tp->tp_file); /* D. */
|
||||
heap_endscan(sdesc);
|
||||
}
|
||||
|
||||
/*
|
||||
* createrun - places the next run on file
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
bool
|
||||
createrun(HeapScanDesc sdesc, FILE *file)
|
||||
{
|
||||
register HeapTuple lasttuple;
|
||||
register HeapTuple btup, tup;
|
||||
struct leftist *nextrun;
|
||||
Buffer b;
|
||||
bool foundeor;
|
||||
short junk;
|
||||
|
||||
lasttuple = NULL;
|
||||
nextrun = NULL;
|
||||
foundeor = false;
|
||||
for ( ; ; ) {
|
||||
while (LACKMEM() && Tuples != NULL) {
|
||||
if (lasttuple != NULL) {
|
||||
FREEMEM(lasttuple->t_len);
|
||||
FREE(lasttuple);
|
||||
TRACEMEM(createrun);
|
||||
}
|
||||
lasttuple = tup = gettuple(&Tuples, &junk);
|
||||
PUTTUP(tup, file);
|
||||
TRACEOUT(createrun, tup);
|
||||
}
|
||||
if (LACKMEM())
|
||||
break;
|
||||
btup = heap_getnext(sdesc, 0, &b);
|
||||
if (!HeapTupleIsValid(btup)) {
|
||||
foundeor = true;
|
||||
break;
|
||||
}
|
||||
IncrProcessed();
|
||||
tup = tuplecopy(btup, sdesc->rs_rd, b);
|
||||
USEMEM(tup->t_len);
|
||||
TRACEMEM(createrun);
|
||||
if (lasttuple != NULL && tuplecmp(tup, lasttuple))
|
||||
puttuple(&nextrun, tup, 0);
|
||||
else
|
||||
puttuple(&Tuples, tup, 0);
|
||||
ReleaseBuffer(b);
|
||||
}
|
||||
if (lasttuple != NULL) {
|
||||
FREEMEM(lasttuple->t_len);
|
||||
FREE(lasttuple);
|
||||
TRACEMEM(createrun);
|
||||
}
|
||||
dumptuples(file);
|
||||
ENDRUN(file);
|
||||
/* delimit the end of the run */
|
||||
Tuples = nextrun;
|
||||
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).
|
||||
*/
|
||||
HeapTuple
|
||||
tuplecopy(HeapTuple tup, Relation rdesc, Buffer b)
|
||||
{
|
||||
HeapTuple rettup;
|
||||
|
||||
if (!HeapTupleIsValid(tup)) {
|
||||
return(NULL); /* just in case */
|
||||
}
|
||||
rettup = (HeapTuple)malloc(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)
|
||||
*
|
||||
* Returns:
|
||||
* file of tuples in order
|
||||
*/
|
||||
FILE *
|
||||
mergeruns()
|
||||
{
|
||||
register struct tape *tp;
|
||||
|
||||
tp = Tape + TapeRange;
|
||||
merge(tp);
|
||||
rewind(tp->tp_file);
|
||||
while (--Level != 0) {
|
||||
tp = tp->tp_prev;
|
||||
rewind(tp->tp_file);
|
||||
/* resettape(tp->tp_file); -not sufficient */
|
||||
merge(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)
|
||||
*/
|
||||
void
|
||||
merge(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;
|
||||
|
||||
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 (TotalDummy < TapeRange) /* no complete dummy runs */
|
||||
outdummy = 0;
|
||||
else {
|
||||
outdummy = 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;
|
||||
/* do not add the outdummy runs yet */
|
||||
times -= outdummy;
|
||||
}
|
||||
destfile = dest->tp_file;
|
||||
while (times-- != 0) { /* merge one run */
|
||||
tuples = NULL;
|
||||
if (TotalDummy == 0)
|
||||
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
||||
GETLEN(tuplen, tp->tp_file);
|
||||
tup = ALLOCTUP(tuplen);
|
||||
USEMEM(tuplen);
|
||||
TRACEMEM(merge);
|
||||
SETTUPLEN(tup, tuplen);
|
||||
GETTUP(tup, tuplen, tp->tp_file);
|
||||
puttuple(&tuples, tup, TAPENO(tp));
|
||||
}
|
||||
else {
|
||||
for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) {
|
||||
if (tp->tp_dummy != 0) {
|
||||
tp->tp_dummy--;
|
||||
TotalDummy--;
|
||||
} else {
|
||||
GETLEN(tuplen, tp->tp_file);
|
||||
tup = ALLOCTUP(tuplen);
|
||||
USEMEM(tuplen);
|
||||
TRACEMEM(merge);
|
||||
SETTUPLEN(tup, tuplen);
|
||||
GETTUP(tup, tuplen, tp->tp_file);
|
||||
puttuple(&tuples, tup, TAPENO(tp));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (tuples != NULL) {
|
||||
/* possible optimization by using count in tuples */
|
||||
tup = gettuple(&tuples, &fromtape);
|
||||
PUTTUP(tup, destfile);
|
||||
FREEMEM(tup->t_len);
|
||||
FREE(tup);
|
||||
TRACEMEM(merge);
|
||||
GETLEN(tuplen, Tape[fromtape].tp_file);
|
||||
if (tuplen == 0)
|
||||
;
|
||||
else {
|
||||
tup = ALLOCTUP(tuplen);
|
||||
USEMEM(tuplen);
|
||||
TRACEMEM(merge);
|
||||
SETTUPLEN(tup, tuplen);
|
||||
GETTUP(tup, tuplen, Tape[fromtape].tp_file);
|
||||
puttuple(&tuples, tup, fromtape);
|
||||
}
|
||||
}
|
||||
ENDRUN(destfile);
|
||||
}
|
||||
TotalDummy += outdummy;
|
||||
}
|
||||
|
||||
/*
|
||||
* endpsort - creates the new relation and unlinks the tape files
|
||||
*/
|
||||
void
|
||||
endpsort(Relation rdesc, FILE *file)
|
||||
{
|
||||
register struct tape *tp;
|
||||
register HeapTuple tup;
|
||||
long tuplen;
|
||||
|
||||
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);
|
||||
FREE(tup);
|
||||
SortMemory -= tuplen;
|
||||
}
|
||||
for (tp = Tape + TapeRange; tp >= Tape; tp--)
|
||||
destroytape(tp->tp_file);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct tapelst {
|
||||
char *tl_name;
|
||||
int tl_fd;
|
||||
struct tapelst *tl_next;
|
||||
};
|
||||
|
||||
static struct tapelst *Tapes = NULL;
|
||||
static char Tempfile[MAXPGPATH] = TEMPDIR;
|
||||
|
||||
/*
|
||||
* gettape - returns an open stream for writing/reading
|
||||
*
|
||||
* Returns:
|
||||
* Open stream for writing/reading.
|
||||
* NULL if unable to open temporary file.
|
||||
*/
|
||||
FILE *
|
||||
gettape()
|
||||
{
|
||||
register struct tapelst *tp;
|
||||
FILE *file;
|
||||
static int tapeinit = 0;
|
||||
char *mktemp();
|
||||
|
||||
tp = (struct tapelst *)malloc((unsigned)sizeof (struct tapelst));
|
||||
if (!tapeinit) {
|
||||
Tempfile[sizeof (TEMPDIR) - 1] = '/';
|
||||
memmove(Tempfile + sizeof(TEMPDIR), TAPEEXT, sizeof (TAPEEXT));
|
||||
tapeinit = 1;
|
||||
}
|
||||
tp->tl_name = malloc((unsigned)sizeof(Tempfile));
|
||||
/*
|
||||
* now, copy template with final null into malloc'd space
|
||||
*/
|
||||
memmove(tp->tl_name, Tempfile, sizeof (TEMPDIR) + sizeof (TAPEEXT));
|
||||
mktemp(tp->tl_name);
|
||||
|
||||
AllocateFile();
|
||||
file = fopen(tp->tl_name, "w+");
|
||||
if (file == NULL) {
|
||||
/* XXX this should not happen */
|
||||
FreeFile();
|
||||
FREE(tp->tl_name);
|
||||
FREE(tp);
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
tp->tl_fd = fileno(file);
|
||||
tp->tl_next = Tapes;
|
||||
Tapes = tp;
|
||||
return(file);
|
||||
}
|
||||
|
||||
/*
|
||||
* resettape - resets the tape to size 0
|
||||
*/
|
||||
void
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* distroytape - unlinks the tape
|
||||
*
|
||||
* Efficiency note:
|
||||
* More efficient to destroy more recently allocated tapes first.
|
||||
*
|
||||
* Possible bugs:
|
||||
* Exits instead of returning status, if given invalid tape.
|
||||
*/
|
||||
void
|
||||
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;
|
||||
fclose(file);
|
||||
FreeFile();
|
||||
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) {
|
||||
fclose(file);
|
||||
FreeFile();
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user