]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Put in a generic hash table system in place of the various ad-hoc
authordrh <drh@noemail.net>
Sat, 22 Sep 2001 18:12:08 +0000 (18:12 +0000)
committerdrh <drh@noemail.net>
Sat, 22 Sep 2001 18:12:08 +0000 (18:12 +0000)
hash table scattered everywhere.  Except, the page hash table in
the pager is unchanged. (CVS 260)

FossilOrigin-Name: 9114420dd01d92cc8890046500a8806a297a4e65

Makefile.in
manifest
manifest.uuid
src/build.c
src/hash.c [new file with mode: 0644]
src/hash.h [new file with mode: 0644]
src/main.c
src/sqliteInt.h
src/vdbe.c

index fdfe635666a6aa6492f619d941a84b7f998b8863..6ba82e00e1d93c265cbdd0f2de3bbc4526070be1 100644 (file)
@@ -60,7 +60,7 @@ ENCODING = @ENCODING@
 
 # Object files for the SQLite library.
 #
-LIBOBJ = btree.o build.o delete.o expr.o insert.o \
+LIBOBJ = btree.o build.o delete.o expr.o hash.o insert.o \
          main.o os.o pager.o parse.o printf.o random.o select.o table.o \
          tokenize.o update.o util.o vdbe.o where.o tclsqlite.o
 
@@ -72,6 +72,7 @@ SRC = \
   $(TOP)/src/build.c \
   $(TOP)/src/delete.c \
   $(TOP)/src/expr.c \
+  $(TOP)/src/hash.c \
   $(TOP)/src/insert.c \
   $(TOP)/src/main.c \
   $(TOP)/src/os.c \
@@ -186,6 +187,9 @@ delete.o:   $(TOP)/src/delete.c $(HDR)
 expr.o:        $(TOP)/src/expr.c $(HDR)
        $(TCC) -c $(TOP)/src/expr.c
 
+hash.o:        $(TOP)/src/hash.c $(HDR)
+       $(TCC) -c $(TOP)/src/hash.c
+
 insert.o:      $(TOP)/src/insert.c $(HDR)
        $(TCC) -c $(TOP)/src/insert.c
 
index 839c45e2df234109e18000a2dbd69bb3fda295a0..5d36a68a4ab396c751b117b4b7ebd4c9636f3f08 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
-C Web\ssite\schanges\sprior\sto\srelease\s2.0-Alpha-2.\s(CVS\s259)
-D 2001-09-20T12:32:53
-F Makefile.in 0af884b48d7dbcd01d73a9d93cb3e19b34c9f087
+C Put\sin\sa\sgeneric\shash\stable\ssystem\sin\splace\sof\sthe\svarious\sad-hoc\nhash\stable\sscattered\severywhere.\s\sExcept,\sthe\spage\shash\stable\sin\nthe\spager\sis\sunchanged.\s(CVS\s260)
+D 2001-09-22T18:12:09
+F Makefile.in 18eea9a3486939fced70aa95b691be766c2c995d
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
 F VERSION 6942aa44940d2972bd72f671a631060106e77f7e
 F configure aad857a97ca28a584228869186eb4cd7dbebbb3a x
@@ -10,11 +10,13 @@ F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c 39da79b5a656870aa3ab72d40374fb38bd1bd12d
 F src/btree.h fcb08daab59fd81023204ac71955233e218443c2
-F src/build.c 072d6cf5b894c47d3fb8c11580eaa1a24528bca8
+F src/build.c 8af632a8024fa1132e4f40be48915083405614df
 F src/delete.c ca7ca9bf8b613730821c4a755030d1a020b5e067
 F src/expr.c 343a515a4abaf60e9e26c7412aa8c43fd3eae97d
+F src/hash.c bf36fb4cba114015123b0050f137d2c4553778a1
+F src/hash.h 5f6e7c04c46ed015ab4e01797c2049b4af5b006d
 F src/insert.c b34860ea58525754f18bde652f74161295ca2455
-F src/main.c 5e5794eea3316dd3a63c112ccdcc997b9118f345
+F src/main.c 49af06b7327c8b23b9331ce80b7e4bc9536ed2e1
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c faf9f484f3261c7650021cae79294338491f2cfb
 F src/os.h 0f478e2fef5ec1612f94b59b163d4807d4c77d6d
@@ -27,7 +29,7 @@ F src/select.c 7d90a6464906419fde96c0707a4cf4f3280db318
 F src/shell.c 8e573138074e0b9526fca59b3eac22bdf18ecc03
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in 5d78c86bd9a9b8bbba65f860fbaf71c1882d6030
-F src/sqliteInt.h 667126497697d58a114d9db492f38c99eadb36d7
+F src/sqliteInt.h ae90beff6acc510bf98c80908d86b0830933e507
 F src/table.c abd0adbe0fee39d995287b3bcccd908d174dfcac
 F src/tclsqlite.c 04a35d04f06046acc3944121dc6c36717f7f36d5
 F src/test1.c 3892caa5372789a6d17329dd595724e1f06cb6de
@@ -36,7 +38,7 @@ F src/test3.c f46bad555db7a6a25be332a96ac99e4d68a1b0c5
 F src/tokenize.c 2adf0568edf41b3d3c2fcb541ac49bd6e662da0c
 F src/update.c a1952ad5d53379fa2b2d12efae5993ddb85a1ddb
 F src/util.c 2a3491fd761b64cca849b07095076f482d119f9c
-F src/vdbe.c efe564f482c94d361843c5975e2a5724cf0ca8af
+F src/vdbe.c 1cf36bea586e659995545ac8ad9534e794f4296f
 F src/vdbe.h 900b59b46afdfb9c048a2a31a4478f380ab8504e
 F src/where.c cce952b6a2459ac2296e3432876a4252d2fe3b87
 F test/all.test a2320eb40b462f25bd3e33115b1cabf3791450dd
@@ -95,7 +97,7 @@ F www/opcode.tcl 60222aeb57a7855b2582c374b8753cb5bb53c4ab
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
 F www/tclsqlite.tcl 13d50723f583888fc80ae1a38247c0ab415066fa
 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P 548c55e8498826834f946598baf72e2d4b7283b8
-R 1753929adc0f169566743fb2cccd868c
+P 13afb22409b3b58d4c4b97a9fac22c96153d77c0
+R 453b3eb283d27c63c0996482bd747b07
 U drh
-Z f2e9206a582317931d33fc08cbe53d59
+Z 39fe4015001e5b10c0870078a2610b25
index c0ab782c81f44b0e5400ed031ff0d77b9a438a4b..3b85bd8519b08d49eb7e374ac794983fe415c922 100644 (file)
@@ -1 +1 @@
-13afb22409b3b58d4c4b97a9fac22c96153d77c0
\ No newline at end of file
+9114420dd01d92cc8890046500a8806a297a4e65
\ No newline at end of file
index cb93215d09efbc42587f598a50ffd55df25a45e7..2f05e368b32ffc132f1038aa6894424ba3df00b9 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.37 2001/09/17 20:25:58 drh Exp $
+** $Id: build.c,v 1.38 2001/09/22 18:12:10 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -135,14 +135,7 @@ void sqliteExprDelete(Expr *p){
 ** of that table.  Return NULL if not found.
 */
 Table *sqliteFindTable(sqlite *db, char *zName){
-  Table *pTable;
-  int h;
-
-  h = sqliteHashNoCase(zName, 0) % N_HASH;
-  for(pTable=db->apTblHash[h]; pTable; pTable=pTable->pHash){
-    if( sqliteStrICmp(pTable->zName, zName)==0 ) return pTable;
-  }
-  return 0;
+  return sqliteHashFind(&db->tblHash, zName, strlen(zName)+1);
 }
 
 /*
@@ -151,14 +144,7 @@ Table *sqliteFindTable(sqlite *db, char *zName){
 ** of that index.  Return NULL if not found.
 */
 Index *sqliteFindIndex(sqlite *db, char *zName){
-  Index *p;
-  int h;
-
-  h = sqliteHashNoCase(zName, 0) % N_HASH;
-  for(p=db->apIdxHash[h]; p; p=p->pHash){
-    if( sqliteStrICmp(p->zName, zName)==0 ) return p;
-  }
-  return 0;
+  return sqliteHashFind(&db->idxHash, zName, strlen(zName)+1);
 }
 
 /*
@@ -170,24 +156,14 @@ Index *sqliteFindIndex(sqlite *db, char *zName){
 ** Unlinking from the Table must be done by the calling function.
 */
 static void sqliteDeleteIndex(sqlite *db, Index *pIndex){
-  int h;
   if( pIndex->zName && db ){
-    h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH;
-    if( db->apIdxHash[h]==pIndex ){
-      db->apIdxHash[h] = pIndex->pHash;
-    }else{
-      Index *p;
-      for(p=db->apIdxHash[h]; p && p->pHash!=pIndex; p=p->pHash){}
-      if( p && p->pHash==pIndex ){
-        p->pHash = pIndex->pHash;
-      }
-    }
+    sqliteHashInsert(&db->idxHash, pIndex->zName, strlen(pIndex->zName)+1, 0);
   }
   sqliteFree(pIndex);
 }
 
 /*
-** Unlink the given  index from its table, then remove
+** Unlink the given index from its table, then remove
 ** the index from the index hash table, and free its memory
 ** structures.
 */
@@ -240,16 +216,7 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
 */
 static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *pTable){
   if( pTable->zName && db ){
-    int h = sqliteHashNoCase(pTable->zName, 0) % N_HASH;
-    if( db->apTblHash[h]==pTable ){
-      db->apTblHash[h] = pTable->pHash;
-    }else{
-      Table *p;
-      for(p=db->apTblHash[h]; p && p->pHash!=pTable; p=p->pHash){}
-      if( p && p->pHash==pTable ){
-        p->pHash = pTable->pHash;
-      }
-    }
+    sqliteHashInsert(&db->tblHash, pTable->zName, strlen(pTable->zName)+1, 0);
   }
   sqliteDeleteTable(db, pTable);
 }
@@ -269,31 +236,37 @@ static void sqliteUnlinkAndDeleteTable(sqlite *db, Table *pTable){
 ** See also: sqliteRollbackInternalChanges()
 */
 void sqliteCommitInternalChanges(sqlite *db){
-  int i;
+  Hash toDelete;
+  HashElem *pElem;
   if( (db->flags & SQLITE_InternChanges)==0 ) return;
+  sqliteHashInit(&toDelete, SQLITE_HASH_POINTER, 0);
   db->schema_cookie = db->next_cookie;
-  for(i=0; i<N_HASH; i++){
-    Table *pTable, *pNext;
-    for(pTable = db->apTblHash[i]; pTable; pTable=pNext){
-      pNext = pTable->pHash;
-      if( pTable->isDelete ){
-        sqliteUnlinkAndDeleteTable(db, pTable);
-      }else if( pTable->isCommit==0 ){
-        pTable->isCommit = 1;
-      }
+  for(pElem=sqliteHashFirst(&db->tblHash); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pTable = sqliteHashData(pElem);
+    if( pTable->isDelete ){
+      sqliteHashInsert(&toDelete, pTable, 0, pTable);
+    }else{
+      pTable->isCommit = 1;
     }
   }
-  for(i=0; i<N_HASH; i++){
-    Index *pIndex, *pNext;
-    for(pIndex = db->apIdxHash[i]; pIndex; pIndex=pNext){
-      pNext = pIndex->pHash;
-      if( pIndex->isDelete ){
-        sqliteUnlinkAndDeleteIndex(db, pIndex);
-      }else if( pIndex->isCommit==0 ){
-        pIndex->isCommit = 1;
-      }
+  for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pTable = sqliteHashData(pElem);
+    sqliteUnlinkAndDeleteTable(db, pTable);
+  }
+  sqliteHashClear(&toDelete);
+  for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pIndex = sqliteHashData(pElem);
+    if( pIndex->isDelete ){
+      sqliteHashInsert(&toDelete, pIndex, 0, pIndex);
+    }else{
+      pIndex->isCommit = 1;
     }
   }
+  for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
+    Index *pIndex = sqliteHashData(pElem);
+    sqliteUnlinkAndDeleteIndex(db, pIndex);
+  }
+  sqliteHashClear(&toDelete);
   db->flags &= ~SQLITE_InternChanges;
 }
 
@@ -306,31 +279,37 @@ void sqliteCommitInternalChanges(sqlite *db){
 ** See also: sqliteCommitInternalChanges()
 */
 void sqliteRollbackInternalChanges(sqlite *db){
-  int i;
+  Hash toDelete;
+  HashElem *pElem;
   if( (db->flags & SQLITE_InternChanges)==0 ) return;
+  sqliteHashInit(&toDelete, SQLITE_HASH_POINTER, 0);
   db->next_cookie = db->schema_cookie;
-  for(i=0; i<N_HASH; i++){
-    Table *pTable, *pNext;
-    for(pTable = db->apTblHash[i]; pTable; pTable=pNext){
-      pNext = pTable->pHash;
-      if( !pTable->isCommit ){
-        sqliteUnlinkAndDeleteTable(db, pTable);
-      }else if( pTable->isDelete ){
-        pTable->isDelete = 0;
-      }
+  for(pElem=sqliteHashFirst(&db->tblHash); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pTable = sqliteHashData(pElem);
+    if( !pTable->isCommit ){
+      sqliteHashInsert(&toDelete, pTable, 0, pTable);
+    }else{
+      pTable->isDelete = 0;
     }
   }
-  for(i=0; i<N_HASH; i++){
-    Index *pIndex, *pNext;
-    for(pIndex = db->apIdxHash[i]; pIndex; pIndex=pNext){
-      pNext = pIndex->pHash;
-      if( !pIndex->isCommit ){
-        sqliteUnlinkAndDeleteIndex(db, pIndex);
-      }else if( pIndex->isDelete ){
-        pIndex->isDelete = 0;
-      }
+  for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pTable = sqliteHashData(pElem);
+    sqliteUnlinkAndDeleteTable(db, pTable);
+  }
+  sqliteHashClear(&toDelete);
+  for(pElem=sqliteHashFirst(&db->idxHash); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pIndex = sqliteHashData(pElem);
+    if( !pIndex->isCommit ){
+      sqliteHashInsert(&toDelete, pIndex, 0, pIndex);
+    }else{
+      pIndex->isDelete = 0;
     }
   }
+  for(pElem=sqliteHashFirst(&toDelete); pElem; pElem=sqliteHashNext(pElem)){
+    Index *pIndex = sqliteHashData(pElem);
+    sqliteUnlinkAndDeleteIndex(db, pIndex);
+  }
+  sqliteHashClear(&toDelete);
   db->flags &= ~SQLITE_InternChanges;
 }
 
@@ -383,7 +362,6 @@ void sqliteStartTable(Parse *pParse, Token *pStart, Token *pName){
   pTable = sqliteMalloc( sizeof(Table) );
   if( pTable==0 ) return;
   pTable->zName = zName;
-  pTable->pHash = 0;
   pTable->nCol = 0;
   pTable->aCol = 0;
   pTable->pIndex = 0;
@@ -484,7 +462,6 @@ static void changeCookie(sqlite *db){
 */
 void sqliteEndTable(Parse *pParse, Token *pEnd){
   Table *p;
-  int h;
   sqlite *db = pParse->db;
 
   if( pEnd==0 || pParse->nErr || sqlite_malloc_failed ) return;
@@ -494,9 +471,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd){
   /* Add the table to the in-memory representation of the database
   */
   if( pParse->explain==0 ){
-    h = sqliteHashNoCase(p->zName, 0) % N_HASH;
-    p->pHash = db->apTblHash[h];
-    db->apTblHash[h] = p;
+    sqliteHashInsert(&db->tblHash, p->zName, strlen(p->zName)+1, p);
     pParse->pNewTable = 0;
     db->nTable++;
     db->flags |= SQLITE_InternChanges;
@@ -671,7 +646,7 @@ void sqliteCreateIndex(
   Table *pTab;     /* Table to be indexed */
   Index *pIndex;   /* The index to be created */
   char *zName = 0;
-  int i, j, h;
+  int i, j;
   Token nullId;    /* Fake token for an empty ID list */
   sqlite *db = pParse->db;
 
@@ -769,9 +744,8 @@ void sqliteCreateIndex(
   */
   if( pParse->explain==0 ){
     if( pName!=0 ){
-      h = sqliteHashNoCase(pIndex->zName, 0) % N_HASH;
-      pIndex->pHash = db->apIdxHash[h];
-      db->apIdxHash[h] = pIndex;
+      char *zName = pIndex->zName;;
+      sqliteHashInsert(&db->idxHash, zName, strlen(zName)+1, pIndex);
     }
     pIndex->pNext = pTab->pIndex;
     pTab->pIndex = pIndex;
@@ -1168,15 +1142,14 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
   if( zName ){
     sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, zName, 0);
   }else{
-    int h;
     Table *pTab;
     Index *pIdx;
-    for(h=0; h<N_HASH; h++){
-      for(pTab=db->apTblHash[h]; pTab; pTab=pTab->pHash){
-        sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pTab->zName, 0);
-        for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-          sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pIdx->zName, 0);
-        }
+    HashElem *pE;
+    for(pE=sqliteHashFirst(&db->tblHash); pE; pE=sqliteHashNext(pE)){
+      pTab = sqliteHashData(pE);
+      sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pTab->zName, 0);
+      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+        sqliteVdbeAddOp(v, OP_Reorganize, 0, 0, pIdx->zName, 0);
       }
     }
   }
diff --git a/src/hash.c b/src/hash.c
new file mode 100644 (file)
index 0000000..db854d7
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+** 2001 September 22
+**
+** 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 is the implementation of generic hash-tables
+** used in SQLite.
+**
+** $Id: hash.c,v 1.1 2001/09/22 18:12:10 drh Exp $
+*/
+#include "sqliteInt.h"
+#include <assert.h>
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+*/
+void sqliteHashInit(Hash *new, int keyClass, int copyKey){
+  assert( new!=0 );
+  assert( keyClass>=SQLITE_HASH_INT && keyClass<=SQLITE_HASH_BINARY );
+  new->keyClass = keyClass;
+  new->copyKey = copyKey &&
+                (keyClass==SQLITE_HASH_STRING || keyClass==SQLITE_HASH_BINARY);
+  new->first = 0;
+  new->count = 0;
+  new->htsize = 0;
+  new->ht = 0;
+}
+
+/* Remove all entries from a hash table.  Reclaim all memory.
+*/
+void sqliteHashClear(Hash *pH){
+  HashElem *elem;         /* For looping over all elements of the table */
+
+  assert( pH!=0 );
+  elem = pH->first;
+  pH->first = 0;
+  if( pH->ht ) sqliteFree(pH->ht);
+  pH->ht = 0;
+  pH->htsize = 0;
+  while( elem ){
+    HashElem *next_elem = elem->next;
+    if( pH->copyKey && elem->pKey ){
+      sqliteFree(elem->pKey);
+    }
+    sqliteFree(elem);
+    elem = next_elem;
+  }
+  pH->count = 0;
+}
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_INT
+*/
+static int intHash(const void *pKey, int nKey){
+  return nKey ^ (nKey<<8) ^ (nKey>>8);
+}
+static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  return n2 - n1;
+}
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
+*/
+static int ptrHash(const void *pKey, int nKey){
+  nKey = (int)pKey;
+  return nKey ^ (nKey<<8) ^ (nKey>>8);
+}
+static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  return ((int)pKey2) - (int)pKey1;
+}
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_STRING
+*/
+static int strHash(const void *pKey, int nKey){
+  return sqliteHashNoCase((const char*)pKey, nKey); 
+}
+static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  if( n1!=n2 ) return n2-n1;
+  return sqliteStrNICmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
+*/
+static int binHash(const void *pKey, int nKey){
+  int h = 0;
+  const char *z = (const char *)pKey;
+  while( nKey-- > 0 ){
+    h = (h<<3) ^ h ^ *(z++);
+  }
+  if( h<0 ) h = -h;
+  return h;
+}
+static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+  if( n1!=n2 ) return n2-n1;
+  return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+*/
+static int (*hashFunction(int keyClass))(const void*,int){
+  switch( keyClass ){
+    case SQLITE_HASH_INT:     return intHash;
+    case SQLITE_HASH_POINTER: return ptrHash;
+    case SQLITE_HASH_STRING:  return strHash;
+    case SQLITE_HASH_BINARY:  return binHash;;
+    default: break;
+  }
+  return 0;
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+*/
+static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
+  switch( keyClass ){
+    case SQLITE_HASH_INT:     return intCompare;
+    case SQLITE_HASH_POINTER: return ptrCompare;
+    case SQLITE_HASH_STRING:  return strCompare;
+    case SQLITE_HASH_BINARY:  return binCompare;
+    default: break;
+  }
+  return 0;
+}
+
+
+/* Resize the hash table. new_size must be a power of 2.
+** The hash table might fail to resize if sqliteMalloc() fails.
+*/
+static void rehash(Hash *pH, int new_size){
+  struct _ht *new_ht;            /* The new hash table */
+  HashElem *elem, *next_elem;    /* For looping over existing elements */
+  HashElem *x;                   /* Element being copied to new hash table */
+  int (*xHash)(const void*,int); /* The hash function */
+
+  assert( (new_size & (new_size-1))==0 );
+  new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) );
+  if( new_ht==0 ) return;
+  if( pH->ht ) sqliteFree(pH->ht);
+  pH->ht = new_ht;
+  pH->htsize = new_size;
+  xHash = hashFunction(pH->keyClass);
+  for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+    int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+    next_elem = elem->next;
+    x = new_ht[h].chain;
+    if( x ){
+      elem->next = x;
+      elem->prev = x->prev;
+      if( x->prev ) x->prev->next = elem;
+      else          pH->first = elem;
+      x->prev = elem;
+    }else{
+      elem->next = pH->first;
+      if( pH->first ) pH->first->prev = elem;
+      elem->prev = 0;
+      pH->first = elem;
+    }
+    new_ht[h].chain = elem;
+    new_ht[h].count++;
+  }
+}
+
+/* This function (for internal use only) locates an element in an
+** pH that matches the given key.  The hash for this key has
+** already been computed and is passed as the 3rd parameter.
+*/
+static HashElem *findElementGivenHash(
+  const Hash *pH,     /* The pH to be searched */
+  const void *pKey,   /* The key we are searching for */
+  int nKey,
+  int h               /* The hash for this key. */
+){
+  HashElem *elem;                /* Used to loop thru the element list */
+  int count;                     /* Number of elements left to test */
+  int (*xCompare)(const void*,int,const void*,int);  /* comparison function */
+
+  if( pH->ht ){
+    elem = pH->ht[h].chain;
+    count = pH->ht[h].count;
+    xCompare = compareFunction(pH->keyClass);
+    while( count-- && elem ){
+      if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ 
+        return elem;
+      }
+      elem = elem->next;
+    }
+  }
+  return 0;
+}
+
+/* Remove a single entry from the pH given a pointer to that
+** element and a hash on the element's key.
+*/
+static void removeElementGivenHash(
+  Hash *pH,         /* The pH containing "elem" */
+  HashElem* elem,   /* The element to be removed from the pH */
+  int h              /* Hash value for the element */
+){
+  if( elem->prev ){
+    elem->prev->next = elem->next; 
+  }else{
+    pH->first = elem->next;
+  }
+  if( elem->next ){
+    elem->next->prev = elem->prev;
+  }
+  if( pH->ht[h].chain==elem ){
+    pH->ht[h].chain = elem->next;
+  }
+  pH->ht[h].count--;
+  if( pH->ht[h].count<=0 ){
+    pH->ht[h].chain = 0;
+  }
+  if( pH->copyKey && elem->pKey ){
+    sqliteFree(elem->pKey);
+  }
+  sqliteFree( elem );
+  pH->count--;
+}
+
+/* Attempt to locate an element of the associative pH with a key
+** that matches "key".  Return the data for this element if it is
+** found, or NULL if no match is found.
+*/
+void *sqliteHashFind(const Hash *pH, const void *pKey, int nKey){
+  int h;             /* A hash on key */
+  HashElem *elem;    /* The element that matches key */
+  int (*xHash)(const void*,int);  /* The hash function */
+
+  if( pH==0 || pH->ht==0 ) return 0;
+  xHash = hashFunction(pH->keyClass);
+  assert( xHash!=0 );
+  h = (*xHash)(pKey,nKey);
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
+  return elem ? elem->data : 0;
+}
+
+/* Insert an element into the pH.  The key will be "key" and
+** the data will be "data".
+**
+** If no pH element exists with a matching key, then a new
+** pH element is created.  The key is copied (using the copy
+** function of the key class) into the new element.  NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the pH.
+*/
+void *sqliteHashInsert(Hash *pH, void *pKey, int nKey, void *data){
+  int hraw;             /* Raw hash value of the key */
+  int h;                /* the hash of the key modulo hash table size */
+  HashElem *elem;       /* Used to loop thru the element list */
+  HashElem *new_elem;   /* New element added to the pH */
+  int (*xHash)(const void*,int);  /* The hash function */
+
+  assert( pH!=0 );
+  xHash = hashFunction(pH->keyClass);
+  assert( xHash!=0 );
+  hraw = (*xHash)(pKey, nKey);
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  h = hraw & (pH->htsize-1);
+  elem = findElementGivenHash(pH,pKey,nKey,h);
+  if( elem ){
+    void *old_data = elem->data;
+    if( data==0 ){
+      removeElementGivenHash(pH,elem,h);
+    }else{
+      elem->data = data;
+    }
+    return old_data;
+  }
+  if( data==0 ) return 0;
+  new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) );
+  if( new_elem==0 ) return 0;
+  if( pH->copyKey && pKey!=0 ){
+    new_elem->pKey = sqliteMalloc( nKey );
+    if( new_elem->pKey==0 ){
+      sqliteFree(new_elem);
+      return 0;
+    }
+    memcpy((void*)new_elem->pKey, pKey, nKey);
+  }else{
+    new_elem->pKey = pKey;
+  }
+  new_elem->nKey = nKey;
+  pH->count++;
+  if( pH->htsize==0 ) rehash(pH,8);
+  if( pH->htsize==0 ){
+    pH->count = 0;
+    sqliteFree(new_elem);
+    return 0;
+  }
+  if( pH->count > pH->htsize ){
+    rehash(pH,pH->htsize*2);
+  }
+  assert( (pH->htsize & (pH->htsize-1))==0 );
+  h = hraw & (pH->htsize-1);
+  elem = pH->ht[h].chain;
+  if( elem ){
+    new_elem->next = elem;
+    new_elem->prev = elem->prev;
+    if( elem->prev ){ elem->prev->next = new_elem; }
+    else            { pH->first = new_elem; }
+    elem->prev = new_elem;
+  }else{
+    new_elem->next = pH->first;
+    new_elem->prev = 0;
+    if( pH->first ){ pH->first->prev = new_elem; }
+    pH->first = new_elem;
+  }
+  pH->ht[h].count++;
+  pH->ht[h].chain = new_elem;
+  new_elem->data = data;
+  return 0;
+}
diff --git a/src/hash.h b/src/hash.h
new file mode 100644 (file)
index 0000000..af5b572
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+** 2001 September 22
+**
+** 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 is the header file for the generic hash-table implemenation
+** used in SQLite.
+**
+** $Id: hash.h,v 1.1 2001/09/22 18:12:10 drh Exp $
+*/
+#ifndef _SQLITE_HASH_H_
+#define _SQLITE_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct Hash Hash;
+typedef struct HashElem HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly.  Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct Hash {
+  char keyClass;          /* SQLITE_HASH_INT, ..._STRING, or _BINARY */
+  char copyKey;           /* True if copy of key made on insert */
+  int count;              /* Number of entries in this table */
+  HashElem *first;        /* The first element of the array */
+  int htsize;             /* Number of buckets in the hash table */
+  struct _ht {            /* the hash table */
+    int count;               /* Number of entries with this hash */
+    HashElem *chain;         /* Pointer to first entry with this hash */
+  } *ht;
+};
+
+/* Each element in the hash table is an instance of the following 
+** structure.  All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct HashElem {
+  HashElem *next, *prev;   /* Next and previous elements in the table */
+  void *data;              /* Data associated with this element */
+  void *pKey; int nKey;    /* Key associated with this element */
+};
+
+/*
+** There are 4 different modes of operation for a hash table:
+**
+**   SQLITE_HASH_INT         nKey is used as the key and pKey is ignored.
+**
+**   SQLITE_HASH_POINTER     pKey is used as the key and nKey is ignored.
+**
+**   SQLITE_HASH_STRING      pKey points to a string that is nKey bytes long
+**                           (including the null-terminator, if any).  Case
+**                           is ignored in comparisons.
+**
+**   SQLITE_HASH_BINARY      pKey points to binary data nKey bytes long. 
+**                           memcmp() is used to compare keys.
+**
+** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY
+** if the copyKey parameter to HashInit is 1.  
+*/
+#define SQLITE_HASH_INT       1
+#define SQLITE_HASH_POINTER   2
+#define SQLITE_HASH_STRING    3
+#define SQLITE_HASH_BINARY    4
+
+/*
+** Access routines.  To delete, insert a NULL pointer.
+*/
+void sqliteHashInit(Hash*, int keytype, int copyKey);
+void *sqliteHashInsert(Hash*, void *pKey, int nKey, void *pData);
+void *sqliteHashFind(const Hash*, const void *pKey, int nKey);
+void sqliteHashClear(Hash*);
+
+/*
+** Macros for looping over all elements of a hash table.  The idiom is
+** like this:
+**
+**   Hash h;
+**   HashElem *p;
+**   ...
+**   for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){
+**     SomeStructure *pData = sqliteHashData(p);
+**     // do something with pData
+**   }
+*/
+#define sqliteHashFirst(H)  ((H)->first)
+#define sqliteHashNext(E)   ((E)->next)
+#define sqliteHashData(E)   ((E)->data)
+
+#endif /* _SQLITE_HASH_H_ */
index 51231af2aed5466e492ba8f1f7b26acef864f444..9087907723b90651e023c3efeef867e2b8966d73 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.39 2001/09/19 13:22:40 drh Exp $
+** $Id: main.c,v 1.40 2001/09/22 18:12:10 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -249,6 +249,8 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
   db = sqliteMalloc( sizeof(sqlite) );
   if( pzErrMsg ) *pzErrMsg = 0;
   if( db==0 ) goto no_mem_on_open;
+  sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
+  sqliteHashInit(&db->idxHash, SQLITE_HASH_STRING, 0);
   
   /* Open the backend database driver */
   rc = sqliteBtreeOpen(zFilename, mode, MAX_PAGES, &db->pBe);
@@ -298,17 +300,16 @@ no_mem_on_open:
 ** changed the schema and this process needs to reread it.
 */
 static void clearHashTable(sqlite *db){
-  int i;
-  for(i=0; i<N_HASH; i++){
-    Table *pNext, *pList = db->apTblHash[i];
-    db->apTblHash[i] = 0;
-    while( pList ){
-      pNext = pList->pHash;
-      pList->pHash = 0;
-      sqliteDeleteTable(db, pList);
-      pList = pNext;
-    }
+  HashElem *pElem;
+  Hash temp1;
+  temp1 = db->tblHash;
+  sqliteHashInit(&db->tblHash, SQLITE_HASH_STRING, 0);
+  sqliteHashClear(&db->idxHash);
+  for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+    Table *pTbl = sqliteHashData(pElem);
+    sqliteDeleteTable(db, pTbl);
   }
+  sqliteHashClear(&temp1);
   db->flags &= ~SQLITE_Initialized;
 }
 
index ba439951c9e00fa4ba238f9e894f01a4a2b1e030..a8d2f81db11f09a6b42e968642aa6231e5931d5c 100644 (file)
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.52 2001/09/17 20:25:58 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.53 2001/09/22 18:12:10 drh Exp $
 */
 #include "sqlite.h"
+#include "hash.h"
 #include "vdbe.h"
 #include "parse.h"
 #include "btree.h"
@@ -145,8 +146,8 @@ struct sqlite {
   int nTable;                   /* Number of tables in the database */
   void *pBusyArg;               /* 1st Argument to the busy callback */
   int (*xBusyCallback)(void *,const char*,int);  /* The busy callback */
-  Table *apTblHash[N_HASH];     /* All tables of the database */
-  Index *apIdxHash[N_HASH];     /* All indices of the database */
+  Hash tblHash;                 /* All tables indexed by name */
+  Hash idxHash;                 /* All (named) indices indexed by name */
 };
 
 /*
@@ -179,7 +180,6 @@ struct Column {
 */
 struct Table {
   char *zName;     /* Name of the table */
-  Table *pHash;    /* Next table with same hash on zName */
   int nCol;        /* Number of columns in this table */
   Column *aCol;    /* Information about each column */
   Index *pIndex;   /* List of SQL indexes on this table. */
@@ -210,7 +210,6 @@ struct Table {
 */
 struct Index {
   char *zName;     /* Name of this index */
-  Index *pHash;    /* Next index with the same hash on zName */
   int nColumn;     /* Number of columns in the table used by this index */
   int *aiColumn;   /* Which columns are used by this index.  1st is 0 */
   Table *pTable;   /* The SQL table being indexed */
index cd49b9cbedcb2b55b8e9a0260edd670740be334e..19fb9663c1cd08c3257d0f5e5e4046e9e9da7c75 100644 (file)
@@ -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.72 2001/09/18 22:17:44 drh Exp $
+** $Id: vdbe.c,v 1.73 2001/09/22 18:12:10 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -133,19 +133,15 @@ typedef struct Mem Mem;
 typedef struct Agg Agg;
 typedef struct AggElem AggElem;
 struct Agg {
-  int nMem;              /* Number of values stored in each AggElem */
-  AggElem *pCurrent;     /* The AggElem currently in focus */
-  int nElem;             /* The number of AggElems */
-  int nHash;             /* Number of slots in apHash[] */
-  AggElem **apHash;      /* A hash array for looking up AggElems by zKey */
-  AggElem *pFirst;       /* A list of all AggElems */
+  int nMem;            /* Number of values stored in each AggElem */
+  AggElem *pCurrent;   /* The AggElem currently in focus */
+  HashElem *pSearch;   /* The hash element for pCurrent */
+  Hash hash;           /* Hash table of all aggregate elements */
 };
 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 */
+  char *zKey;          /* The key to this AggElem */
+  int nKey;            /* Number of bytes in the key, including '\0' at end */
+  Mem aMem[1];         /* The values for this AggElem */
 };
 
 /*
@@ -155,15 +151,8 @@ struct AggElem {
 **            x.y IN ('hi','hoo','hum')
 */
 typedef struct Set Set;
-typedef struct SetElem SetElem;
 struct Set {
-  SetElem *pAll;         /* All elements of this set */
-  SetElem *apHash[41];   /* A hash array for all elements in this set */
-};
-struct SetElem {
-  SetElem *pHash;        /* Next element with the same hash on zKey */
-  SetElem *pNext;        /* Next element in a list of them all */
-  char zKey[1];          /* Value of this key */
+  Hash hash;             /* A set is just a hash table */
 };
 
 /*
@@ -460,49 +449,22 @@ int sqliteVdbeMakeLabel(Vdbe *p){
 /*
 ** Reset an Agg structure.  Delete all its contents.
 */
-static void AggReset(Agg *p){
+static void AggReset(Agg *pAgg){
   int i;
-  while( p->pFirst ){
-    AggElem *pElem = p->pFirst;
-    p->pFirst = pElem->pNext;
-    for(i=0; i<p->nMem; i++){
+  HashElem *p;
+  for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){
+    AggElem *pElem = sqliteHashData(p);
+    for(i=0; i<pAgg->nMem; i++){
       if( pElem->aMem[i].s.flags & STK_Dyn ){
         sqliteFree(pElem->aMem[i].z);
       }
     }
     sqliteFree(pElem);
   }
-  sqliteFree(p->apHash);
-  memset(p, 0, sizeof(*p));
-}
-
-/*
-** Add the given AggElem to the hash array
-*/
-static void AggEnhash(Agg *p, AggElem *pElem){
-  int h = sqliteHashNoCase(pElem->zKey, pElem->nKey) % p->nHash;
-  pElem->pHash = p->apHash[h];
-  p->apHash[h] = pElem;
-}
-
-/*
-** Change the size of the hash array to the amount given.
-*/
-static void AggRehash(Agg *p, int nHash){
-  int size;
-  AggElem *pElem;
-  if( p->nHash==nHash ) return;
-  size = nHash * sizeof(AggElem*);
-  p->apHash = sqliteRealloc(p->apHash, size );
-  if( p->apHash==0 ){
-    AggReset(p);
-    return;
-  }
-  memset(p->apHash, 0, size);
-  p->nHash = nHash;
-  for(pElem=p->pFirst; pElem; pElem=pElem->pNext){
-    AggEnhash(p, pElem);
-  }
+  sqliteHashClear(&pAgg->hash);
+  pAgg->pCurrent = 0;
+  pAgg->pSearch = 0;
+  pAgg->nMem = 0;
 }
 
 /*
@@ -513,24 +475,17 @@ static void AggRehash(Agg *p, int nHash){
 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) + nKey +
                         (p->nMem-1)*sizeof(pElem->aMem[0]) );
   if( pElem==0 ) return 1;
   pElem->zKey = (char*)&pElem->aMem[p->nMem];
   memcpy(pElem->zKey, zKey, nKey);
   pElem->nKey = nKey;
-  AggEnhash(p, pElem);
-  pElem->pNext = p->pFirst;
-  p->pFirst = pElem;
-  p->nElem++;
-  p->pCurrent = pElem;
+  sqliteHashInsert(&p->hash, pElem->zKey, pElem->nKey, pElem);
   for(i=0; i<p->nMem; i++){
     pElem->aMem[i].s.flags = STK_Null;
   }
+  p->pCurrent = pElem;
   return 0;
 }
 
@@ -539,59 +494,12 @@ static int AggInsert(Agg *p, char *zKey, int nKey){
 */
 #define AggInFocus(P)   ((P).pCurrent ? (P).pCurrent : _AggInFocus(&(P)))
 static AggElem *_AggInFocus(Agg *p){
-  AggElem *pFocus = p->pFirst;
-  if( pFocus ){
-    p->pCurrent = pFocus;
-  }else{
-    AggInsert(p,"",1);
-    pFocus = p->pCurrent = p->pFirst;
-  }
-  return pFocus;
-}
-
-/*
-** Erase all information from a Set
-*/
-static void SetClear(Set *p){
-  SetElem *pElem, *pNext;
-  for(pElem=p->pAll; pElem; pElem=pNext){
-    pNext = pElem->pNext;
-    sqliteFree(pElem);
-  }
-  memset(p, 0, sizeof(*p));
-}
-
-/*
-** Insert a new element into the set
-*/
-static void SetInsert(Set *p, char *zKey){
-  SetElem *pElem;
-  int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash);
-  for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){
-    if( strcmp(pElem->zKey, zKey)==0 ) return;
-  }
-  pElem = sqliteMalloc( sizeof(*pElem) + strlen(zKey) );
+  HashElem *pElem = sqliteHashFirst(&p->hash);
   if( pElem==0 ){
-    SetClear(p);
-    return;
-  }
-  strcpy(pElem->zKey, zKey);
-  pElem->pNext = p->pAll;
-  p->pAll = pElem;
-  pElem->pHash = p->apHash[h];
-  p->apHash[h] = pElem;
-}
-
-/*
-** Return TRUE if an element is in the set.  Return FALSE if not.
-*/
-static int SetTest(Set *p, char *zKey){
-  SetElem *pElem;
-  int h = sqliteHashNoCase(zKey, 0) % ArraySize(p->apHash);
-  for(pElem=p->apHash[h]; pElem; pElem=pElem->pHash){
-    if( strcmp(pElem->zKey, zKey)==0 ) return 1;
+    AggInsert(p,"",1);
+    pElem = sqliteHashFirst(&p->hash);
   }
-  return 0;
+  return pElem ? sqliteHashData(pElem) : 0;
 }
 
 /*
@@ -827,7 +735,7 @@ static void Cleanup(Vdbe *p){
   p->nLineAlloc = 0;
   AggReset(&p->agg);
   for(i=0; i<p->nSet; i++){
-    SetClear(&p->aSet[i]);
+    sqliteHashClear(&p->aSet[i].hash);
   }
   sqliteFree(p->aSet);
   p->aSet = 0;
@@ -1055,6 +963,11 @@ int sqliteVdbeExec(
   aStack = p->aStack;
   p->tos = -1;
 
+  /* Initialize the aggregrate hash table.
+  */
+  sqliteHashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0);
+  p->agg.pSearch = 0;
+
   rc = SQLITE_OK;
 #ifdef MEMORY_DEBUG
   if( access("vdbe_trace",0)==0 ){
@@ -3472,14 +3385,7 @@ case OP_AggFocus: {
   if( Stringify(p, tos) ) goto no_mem;
   zKey = zStack[tos]; 
   nKey = aStack[tos].n;
-  if( p->agg.nHash<=0 ){
-    pElem = 0;
-  }else{
-    int h = sqliteHashNoCase(zKey, nKey) % p->agg.nHash;
-    for(pElem=p->agg.apHash[h]; pElem; pElem=pElem->pHash){
-      if( pElem->nKey==nKey && memcmp(pElem->zKey, zKey, nKey)==0 ) break;
-    }
-  }
+  pElem = sqliteHashFind(&p->agg.hash, zKey, nKey);
   if( pElem ){
     p->agg.pCurrent = pElem;
     pc = pOp->p2 - 1;
@@ -3587,25 +3493,15 @@ case OP_AggGet: {
 ** in between an AggNext and an AggReset.
 */
 case OP_AggNext: {
-  if( p->agg.nHash ){
-    p->agg.nHash = 0;
-    sqliteFree(p->agg.apHash);
-    p->agg.apHash = 0;
-    p->agg.pCurrent = p->agg.pFirst;
-  }else if( p->agg.pCurrent==p->agg.pFirst && p->agg.pCurrent!=0 ){
-    int i;
-    AggElem *pElem = p->agg.pCurrent;
-    for(i=0; i<p->agg.nMem; i++){
-      if( pElem->aMem[i].s.flags & STK_Dyn ){
-        sqliteFree(pElem->aMem[i].z);
-      }
-    }
-    p->agg.pCurrent = p->agg.pFirst = pElem->pNext;
-    sqliteFree(pElem);
-    p->agg.nElem--;
+  if( p->agg.pSearch==0 ){
+    p->agg.pSearch = sqliteHashFirst(&p->agg.hash);
+  }else{
+    p->agg.pSearch = sqliteHashNext(p->agg.pSearch);
   }
-  if( p->agg.pCurrent==0 ){
-    pc = pOp->p2-1;
+  if( p->agg.pSearch==0 ){
+    pc = pOp->p2 - 1;
+  } else {
+    p->agg.pCurrent = sqliteHashData(p->agg.pSearch);
   }
   break;
 }
@@ -3617,7 +3513,7 @@ case OP_AggNext: {
 case OP_SetClear: {
   int i = pOp->p1;
   if( i>=0 && i<p->nSet ){
-    SetClear(&p->aSet[i]);
+    sqliteHashClear(&p->aSet[i].hash);
   }
   break;
 }
@@ -3631,18 +3527,21 @@ case OP_SetClear: {
 case OP_SetInsert: {
   int i = pOp->p1;
   if( p->nSet<=i ){
+    int k;
     p->aSet = sqliteRealloc(p->aSet, (i+1)*sizeof(p->aSet[0]) );
     if( p->aSet==0 ) goto no_mem;
-    memset(&p->aSet[p->nSet], 0, sizeof(p->aSet[0])*(i+1 - p->nSet));
+    for(k=p->nSet; k<=i; k++){
+      sqliteHashInit(&p->aSet[k].hash, SQLITE_HASH_BINARY, 1);
+    }
     p->nSet = i+1;
   }
   if( pOp->p3 ){
-    SetInsert(&p->aSet[i], pOp->p3);
+    sqliteHashInsert(&p->aSet[i].hash, pOp->p3, strlen(pOp->p3)+1, p);
   }else{
     int tos = p->tos;
     if( tos<0 ) goto not_enough_stack;
     if( Stringify(p, tos) ) goto no_mem;
-    SetInsert(&p->aSet[i], zStack[tos]);
+    sqliteHashInsert(&p->aSet[i].hash, zStack[tos], aStack[tos].n, p);
     POPSTACK;
   }
   if( sqlite_malloc_failed ) goto no_mem;
@@ -3660,7 +3559,8 @@ case OP_SetFound: {
   int tos = p->tos;
   VERIFY( if( tos<0 ) goto not_enough_stack; )
   if( Stringify(p, tos) ) goto no_mem;
-  if( VERIFY( i>=0 && i<p->nSet &&) SetTest(&p->aSet[i], zStack[tos])){
+  if( VERIFY( i>=0 && i<p->nSet &&) 
+       sqliteHashFind(&p->aSet[i].hash, zStack[tos], aStack[tos].n)){
     pc = pOp->p2 - 1;
   }
   POPSTACK;
@@ -3678,7 +3578,8 @@ case OP_SetNotFound: {
   int tos = p->tos;
   VERIFY( if( tos<0 ) goto not_enough_stack; )
   if( Stringify(p, tos) ) goto no_mem;
-  if(VERIFY( i>=0 && i<p->nSet &&) !SetTest(&p->aSet[i], zStack[tos])){
+  if(VERIFY( i>=0 && i<p->nSet &&)
+       sqliteHashFind(&p->aSet[i].hash, zStack[tos], aStack[tos].n)==0 ){
     pc = pOp->p2 - 1;
   }
   POPSTACK;