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
This commit is contained in:
drh 2001-09-22 18:12:08 +00:00
parent 4aa85c42b3
commit beae319476
9 changed files with 580 additions and 270 deletions

View 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

View File

@ -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

View File

@ -1 +1 @@
13afb22409b3b58d4c4b97a9fac22c96153d77c0
9114420dd01d92cc8890046500a8806a297a4e65

View 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);
}
}
}

328
src/hash.c Normal file
View File

@ -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;
}

102
src/hash.h Normal file
View File

@ -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_ */

View 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;
}

View File

@ -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 */

View 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;
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; 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;