From beae319476c5d81e8f399010dc7dc5a2336f0606 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 22 Sep 2001 18:12:08 +0000 Subject: [PATCH] Put in a generic hash table system in place of the various ad-hoc hash table scattered everywhere. Except, the page hash table in the pager is unchanged. (CVS 260) FossilOrigin-Name: 9114420dd01d92cc8890046500a8806a297a4e65 --- Makefile.in | 6 +- manifest | 22 ++-- manifest.uuid | 2 +- src/build.c | 159 ++++++++++------------- src/hash.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++++ src/hash.h | 102 +++++++++++++++ src/main.c | 23 ++-- src/sqliteInt.h | 9 +- src/vdbe.c | 199 ++++++++--------------------- 9 files changed, 580 insertions(+), 270 deletions(-) create mode 100644 src/hash.c create mode 100644 src/hash.h diff --git a/Makefile.in b/Makefile.in index fdfe635666..6ba82e00e1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/manifest b/manifest index 839c45e2df..5d36a68a4a 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index c0ab782c81..3b85bd8519 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -13afb22409b3b58d4c4b97a9fac22c96153d77c0 \ No newline at end of file +9114420dd01d92cc8890046500a8806a297a4e65 \ No newline at end of file diff --git a/src/build.c b/src/build.c index cb93215d09..2f05e368b3 100644 --- a/src/build.c +++ b/src/build.c @@ -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 @@ -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; iapTblHash[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; iapIdxHash[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; iapTblHash[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; iapIdxHash[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; hapTblHash[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 index 0000000000..db854d7390 --- /dev/null +++ b/src/hash.c @@ -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 + +/* 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 index 0000000000..af5b5725d5 --- /dev/null +++ b/src/hash.h @@ -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_ */ diff --git a/src/main.c b/src/main.c index 51231af2ae..9087907723 100644 --- a/src/main.c +++ b/src/main.c @@ -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; iapTblHash[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; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ba439951c9..a8d2f81db1 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,9 +11,10 @@ ************************************************************************* ** 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 */ diff --git a/src/vdbe.c b/src/vdbe.c index cd49b9cbed..19fb9663c1 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.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 @@ -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; inMem; i++){ + HashElem *p; + for(p = sqliteHashFirst(&pAgg->hash); p; p = sqliteHashNext(p)){ + AggElem *pElem = sqliteHashData(p); + for(i=0; inMem; 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; inMem; 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; + AggInsert(p,"",1); + pElem = sqliteHashFirst(&p->hash); } - 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; - } - 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; inSet; 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; iagg.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 && inSet ){ - 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 && inSet &&) SetTest(&p->aSet[i], zStack[tos])){ + if( VERIFY( i>=0 && inSet &&) + 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 && inSet &&) !SetTest(&p->aSet[i], zStack[tos])){ + if(VERIFY( i>=0 && inSet &&) + sqliteHashFind(&p->aSet[i].hash, zStack[tos], aStack[tos].n)==0 ){ pc = pOp->p2 - 1; } POPSTACK;