]> granicus.if.org Git - postgresql/commitdiff
1. Use qsort for first run
authorVadim B. Mikheev <vadim4o@yahoo.com>
Thu, 18 Sep 1997 05:37:31 +0000 (05:37 +0000)
committerVadim B. Mikheev <vadim4o@yahoo.com>
Thu, 18 Sep 1997 05:37:31 +0000 (05:37 +0000)
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!

src/backend/utils/sort/lselect.c
src/backend/utils/sort/psort.c

index 1805bd3fd95248eb370d1ab3d40509bd453f7afd..ad8fc9764fcb6761923a7889093dcc612bde6486 100644 (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 $
  *
  *-------------------------------------------------------------------------
  */
 #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;
index c5fab144f01b95460d22ed777a38a50a6a9aeb9d..69485c40b6e78c9dd1ca7084bbb53497c41d799e 100644 (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.
 #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)->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;
-               if (!PS(node)->using_tape_files)
-                       inittapes(node);
-               extrapasses = 1 + (PS(node)->Tuples != NULL);   /* (T != N) ? 2 : 1 */
+               }
+               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)
-                       {
-                               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)
+                       do
                        {
-                               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);
+}