diff --git a/manifest b/manifest
index 760bcab1a7..b79969bb5f 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Bug\sfixes.\s\sTrying\sto\smake\sit\sgo\sfaster.\s(CVS\s254)
-D 2001-09-18T02:02:23
+C Fix\sa\sproblem\sin\sGROUP\sBY\swith\smultiple\scolumns.\s(CVS\s255)
+D 2001-09-18T22:17:44
 F Makefile.in a7053596881af6f2590a816ad4eb8fbbf20724a7
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
 F VERSION 3861a21803fcd9eb92a403027b0da2bb7add4de1
@@ -35,8 +35,8 @@ F src/test2.c 0168b39225b768cfdadd534406f9dec58c27879e
 F src/test3.c f46bad555db7a6a25be332a96ac99e4d68a1b0c5
 F src/tokenize.c 2adf0568edf41b3d3c2fcb541ac49bd6e662da0c
 F src/update.c a1952ad5d53379fa2b2d12efae5993ddb85a1ddb
-F src/util.c a09c3c12aa0259c12e7385313f527895ba3cdbf9
-F src/vdbe.c 21f5d1c5418cbf740eefec10b875632e848eb7c1
+F src/util.c 2cd0bb9693e1c538a5c6f152b7d45cc8567e6724
+F src/vdbe.c efe564f482c94d361843c5975e2a5724cf0ca8af
 F src/vdbe.h 900b59b46afdfb9c048a2a31a4478f380ab8504e
 F src/where.c cce952b6a2459ac2296e3432876a4252d2fe3b87
 F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd
@@ -53,6 +53,7 @@ F test/insert2.test 252d7130d8cc20f649b31a4f503cd87e660abda8
 F test/lock.test 5b4d969ab92c88f8dc10d1b870a2e5fe51ee7f5f
 F test/main.test 085ece17913a487caacbc0a392638c958c83a75d
 F test/malloc.test f1400a8d002eb96f1ca0a34abe56d2ab3e324740
+F test/misc1.test 50a5ca3481fc1f3cd6b978bcd6ed04c06f26a1e6
 F test/pager.test 59bbc4e3d489529ed33db6e15595789e51056077
 F test/printf.test 93ecd43cc48e863a325560f36c3b2741f5fe0308
 F test/quick.test b6ec50f808efc06595fd324bf4f3fabadb9c7e9c
@@ -95,7 +96,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
 F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P b30f2b5e150a219c374f88d13386dbda190ad9ed
-R 07bb1a4a448dedf9e2b45c7aa5ee5ac1
+P 8f28a83abac59a2161d486c96386b8df726468d0
+R 716384b1a17cd98ef2ff77fe086995d1
 U drh
-Z ba1b972f8330f9b56b7e933eb997f9fb
+Z 8f168bc49c168254b9355cae69013087
diff --git a/manifest.uuid b/manifest.uuid
index 2b3a20e9fb..cb4b6e6068 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-8f28a83abac59a2161d486c96386b8df726468d0
\ No newline at end of file
+22132ce18cad31482cdb9b380cedc3f53bc532b8
\ No newline at end of file
diff --git a/src/util.c b/src/util.c
index e23561ee28..646f3423bc 100644
--- a/src/util.c
+++ b/src/util.c
@@ -14,7 +14,7 @@
 ** This file contains functions for allocating memory, comparing
 ** strings, and stuff like that.
 **
-** $Id: util.c,v 1.25 2001/09/16 00:13:27 drh Exp $
+** $Id: util.c,v 1.26 2001/09/18 22:17:44 drh Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
@@ -390,10 +390,10 @@ static unsigned char UpperToLower[] = {
 */
 int sqliteHashNoCase(const char *z, int n){
   int h = 0;
-  int c;
   if( n<=0 ) n = strlen(z);
-  while( n-- > 0 && (c = *z++)!=0 ){
-    h = (h<<3) ^ h ^ UpperToLower[c];
+  while( n > 0  ){
+    h = (h<<3) ^ h ^ UpperToLower[*z++];
+    n--;
   }
   if( h<0 ) h = -h;
   return h;
diff --git a/src/vdbe.c b/src/vdbe.c
index aa05157bf4..cd49b9cbed 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.71 2001/09/18 02:02:23 drh Exp $
+** $Id: vdbe.c,v 1.72 2001/09/18 22:17:44 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -142,6 +142,7 @@ struct Agg {
 };
 struct AggElem {
   char *zKey;            /* The key to this AggElem */
+  int nKey;              /* Number of bytes in the key, including '\0' at end */
   AggElem *pHash;        /* Next AggElem with the same hash on zKey */
   AggElem *pNext;        /* Next AggElem in a list of them all */
   Mem aMem[1];           /* The values for this AggElem */
@@ -479,7 +480,7 @@ static void AggReset(Agg *p){
 ** Add the given AggElem to the hash array
 */
 static void AggEnhash(Agg *p, AggElem *pElem){
-  int h = sqliteHashNoCase(pElem->zKey, 0) % p->nHash;
+  int h = sqliteHashNoCase(pElem->zKey, pElem->nKey) % p->nHash;
   pElem->pHash = p->apHash[h];
   p->apHash[h] = pElem;
 }
@@ -509,18 +510,19 @@ static void AggRehash(Agg *p, int nHash){
 **
 ** Return 0 on success and 1 if memory is exhausted.
 */
-static int AggInsert(Agg *p, char *zKey){
+static int AggInsert(Agg *p, char *zKey, int nKey){
   AggElem *pElem;
   int i;
   if( p->nHash <= p->nElem*2 ){
     AggRehash(p, p->nElem*2 + 19);
   }
   if( p->nHash==0 ) return 1;
-  pElem = sqliteMalloc( sizeof(AggElem) + strlen(zKey) + 1 +
+  pElem = sqliteMalloc( sizeof(AggElem) + nKey +
                         (p->nMem-1)*sizeof(pElem->aMem[0]) );
   if( pElem==0 ) return 1;
   pElem->zKey = (char*)&pElem->aMem[p->nMem];
-  strcpy(pElem->zKey, zKey);
+  memcpy(pElem->zKey, zKey, nKey);
+  pElem->nKey = nKey;
   AggEnhash(p, pElem);
   pElem->pNext = p->pFirst;
   p->pFirst = pElem;
@@ -541,7 +543,7 @@ static AggElem *_AggInFocus(Agg *p){
   if( pFocus ){
     p->pCurrent = pFocus;
   }else{
-    AggInsert(p,"");
+    AggInsert(p,"",1);
     pFocus = p->pCurrent = p->pFirst;
   }
   return pFocus;
@@ -2311,6 +2313,22 @@ case OP_NewRecno: {
   if( VERIFY( i<0 || i>=p->nCursor || ) p->aCsr[i].pCursor==0 ){
     v = 0;
   }else{
+    /* A probablistic algorithm is used to locate an unused rowid.
+    ** We select a rowid at random and see if it exists in the table.
+    ** If it does not exist, we have succeeded.  If the random rowid
+    ** does exist, we select a new one and try again, up to 1000 times.
+    **
+    ** For a table with less than 2 billion entries, the probability
+    ** of not finding a unused rowid is about 1.0e-300.  This is a 
+    ** non-zero probability, but it is still vanishingly small and should
+    ** never cause a problem.  You are much, much more likely to have a
+    ** hardware failure than for this algorithm to fail.
+    **
+    ** To promote locality of reference for repetitive inserts, the
+    ** first few attempts at chosing a rowid pick values just a little
+    ** larger than the previous rowid.  This has been shown experimentally
+    ** to double the speed of the COPY operation.
+    */
     int res, rx, cnt;
     static int x = 0;
     union {
@@ -2319,7 +2337,7 @@ case OP_NewRecno: {
     } ux;
     cnt = 0;
     do{
-      if( x==0 || cnt>5 ){
+      if( cnt>5 ){
         x = sqliteRandomInteger();
       }else{
         x += sqliteRandomByte() + 1;
@@ -2332,7 +2350,7 @@ case OP_NewRecno: {
       v = ux.i;
       rx = sqliteBtreeMoveto(p->aCsr[i].pCursor, &v, sizeof(v), &res);
       cnt++;
-    }while( cnt<200 && rx==SQLITE_OK && res==0 );
+    }while( cnt<1000 && rx==SQLITE_OK && res==0 );
     if( rx==SQLITE_OK && res==0 ){
       rc = SQLITE_FULL;
       goto abort_due_to_error;
@@ -3457,16 +3475,16 @@ case OP_AggFocus: {
   if( p->agg.nHash<=0 ){
     pElem = 0;
   }else{
-    int h = sqliteHashNoCase(zKey, nKey-1) % p->agg.nHash;
+    int h = sqliteHashNoCase(zKey, nKey) % p->agg.nHash;
     for(pElem=p->agg.apHash[h]; pElem; pElem=pElem->pHash){
-      if( strcmp(pElem->zKey, zKey)==0 ) break;
+      if( pElem->nKey==nKey && memcmp(pElem->zKey, zKey, nKey)==0 ) break;
     }
   }
   if( pElem ){
     p->agg.pCurrent = pElem;
     pc = pOp->p2 - 1;
   }else{
-    AggInsert(&p->agg, zKey);
+    AggInsert(&p->agg, zKey, nKey);
     if( sqlite_malloc_failed ) goto no_mem;
   }
   POPSTACK;
diff --git a/test/misc1.test b/test/misc1.test
new file mode 100644
index 0000000000..cc2ec97c08
--- /dev/null
+++ b/test/misc1.test
@@ -0,0 +1,104 @@
+# 2001 September 15
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests for miscellanous features that were
+# left out of other test files.
+#
+# $Id: misc1.test,v 1.1 2001/09/18 22:17:45 drh Exp $
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Test the creation and use of tables that have a large number
+# of columns.
+#
+do_test misc1-1.1 {
+  set cmd "CREATE TABLE manycol(x0 text"
+  for {set i 1} {$i<=99} {incr i} {
+    append cmd ",x$i text"
+  }
+  append cmd ")";
+  execsql $cmd
+  set cmd "INSERT INTO manycol VALUES(0"
+  for {set i 1} {$i<=99} {incr i} {
+    append cmd ",$i"
+  }
+  append cmd ")";
+  execsql $cmd
+  execsql "SELECT x99 FROM manycol"
+} 99
+do_test misc1-1.2 {
+  execsql {SELECT x0, x10, x25, x50, x75 FROM manycol}
+} {0 10 25 50 75}
+do_test misc1-1.3 {
+  for {set j 100} {$j<=1000} {incr j 100} {
+    set cmd "INSERT INTO manycol VALUES($j"
+    for {set i 1} {$i<=99} {incr i} {
+      append cmd ",[expr {$i+$j}]"
+    }
+    append cmd ")"
+    execsql $cmd
+  }
+  execsql {SELECT x50 FROM manycol ORDER BY x80}
+} {50 150 250 350 450 550 650 750 850 950 1050}
+do_test misc1-1.4 {
+  execsql {SELECT x75 FROM manycol WHERE x50=350}
+} 375
+do_test misc1-1.5 {
+  execsql {SELECT x50 FROM manycol WHERE x99=599}
+} 550
+do_test misc1-1.6 {
+  execsql {CREATE INDEX manycol_idx1 ON manycol(x99)}
+  execsql {SELECT x50 FROM manycol WHERE x99=899}
+} 850
+do_test misc1-1.7 {
+  execsql {SELECT count(*) FROM manycol}
+} 11
+do_test misc1-1.8 {
+  execsql {DELETE FROM manycol WHERE x98=1234}
+  execsql {SELECT count(*) FROM manycol}
+} 11
+do_test misc1-1.9 {
+  execsql {DELETE FROM manycol WHERE x98=998}
+  execsql {SELECT count(*) FROM manycol}
+} 10
+do_test misc1-1.10 {
+  execsql {DELETE FROM manycol WHERE x99=500}
+  execsql {SELECT count(*) FROM manycol}
+} 10
+do_test misc1-1.11 {
+  execsql {DELETE FROM manycol WHERE x99=599}
+  execsql {SELECT count(*) FROM manycol}
+} 9
+
+# Check GROUP BY expressions that name two or more columns.
+#
+do_test misc1-2.1 {
+  execsql {
+    BEGIN TRANSACTION;
+    CREATE TABLE agger(one text, two text, three text, four text);
+    INSERT INTO agger VALUES(1, 'one', 'hello', 'yes');
+    INSERT INTO agger VALUES(2, 'two', 'howdy', 'no');
+    INSERT INTO agger VALUES(3, 'thr', 'howareya', 'yes');
+    INSERT INTO agger VALUES(4, 'two', 'lothere', 'yes');
+    INSERT INTO agger VALUES(5, 'one', 'atcha', 'yes');
+    INSERT INTO agger VALUES(6, 'two', 'hello', 'no');
+    COMMIT
+  }
+  execsql {SELECT count(*) FROM agger}
+} 6
+do_test misc1-2.2 {
+  execsql {SELECT sum(one), two, four FROM agger
+           GROUP BY two, four ORDER BY sum(one) desc}
+} {8 two no 6 one yes 4 two yes 3 thr yes}
+
+finish_test