diff --git a/manifest b/manifest index 9822d3b945..8d12edeb61 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Have\ssqlite3_create_collation()\sreturn\sMISUSE\sif\spassed\san\sencoding\svalue\sother\sthan\sSQLITE_UTF8,\sSQLITE_UTF16LE,\sSQLITE_UTF16BE,\sSQLITE_UTF16\sor\sSQLITE_UTF16_ALIGNED.\s(CVS\s6558) -D 2009-04-28T15:35:39 +C Simplifications\sto\sthe\ssymbol\stable\simplementation\sin\shash.c.\s\sFor\svery\ssmall\nsymbol\stables\s(less\sthan\s10\sentries)\sa\ssimple\slinked\slist\sis\sused\sinstead\nof\sa\shash\stable.\s\sNumber\sof\shash\stable\sbuckets\sis\slimited\sto\sprevent\slarge\nallocations.\s(CVS\s6559) +D 2009-04-28T15:43:45 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in 583e87706abc3026960ed759aff6371faf84c211 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -118,8 +118,8 @@ F src/expr.c dd763d6dc8f8329e895440d436c28aa7b5b3595e F src/fault.c dc88c821842157460750d2d61a8a8b4197d047ff F src/func.c f667fe886309707c7178542073bb0ced00a9fae7 F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c -F src/hash.c 15d39cbe87de9b9991054750bd3e0861888b7b06 -F src/hash.h 28f38ebb1006a5beedcb013bcdfe31befe7437ae +F src/hash.c 407e5ca13cab32db529e2c364463a2512fb4d554 +F src/hash.h a63395d62fe898662cb9cdbedbcd2675f16c0410 F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb F src/insert.c 71286d081a919a27ef22eaeccbe2718f93dc6aa9 F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0 @@ -162,7 +162,7 @@ F src/select.c 40748e8044b79d41ba04ce1014ae45434ed452d3 F src/shell.c 0a11f831603f17fea20ca97133c0f64e716af4a7 F src/sqlite.h.in 4028ba942fa43afa7f8ade88fa9acedaa024ddeb F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h d34f80cf38227ce8ed5c519e748375bd9b40d8f7 +F src/sqliteInt.h 61be3597d7dce6b3d360a7185c760c3e4e968a59 F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d @@ -558,7 +558,7 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3 -F test/table.test bf1bc3d9634342a3470bdf64b6190e7445b6b8a6 +F test/table.test e47c9323396976389a15fa0cd22ce3a405433186 F test/tableapi.test 505031f15b18a750184d967d2c896cf88fcc969c F test/tclsqlite.test 8b1150d0486c4848c70d96422513a91c5342be0e F test/tempdb.test b88ac8a19823cf771d742bf61eef93ef337c06b1 @@ -725,7 +725,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 23d67af39227aeade2ea0292e1b8844eea580993 -R fcd591943dcfa775dbae9e00324b29d3 -U danielk1977 -Z c812cbe48ecf60026f66cd5f41f509d1 +P 7975b6f2ec7736b3656a264c2f7e7e95ce7a78e4 +R 104f4e6ccdfa7d3d3e3f8da9a9d08fd5 +U drh +Z b7cd4fee6ba60d43a3b23bd76deb0ece diff --git a/manifest.uuid b/manifest.uuid index e5e071d3a6..554b4f92d9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7975b6f2ec7736b3656a264c2f7e7e95ce7a78e4 \ No newline at end of file +5c737835dec9e6038b304c198aa14337a6f23c1c \ No newline at end of file diff --git a/src/hash.c b/src/hash.c index 3761fb5479..9d9be94e74 100644 --- a/src/hash.c +++ b/src/hash.c @@ -12,7 +12,7 @@ ** This is the implementation of generic hash-tables ** used in SQLite. ** -** $Id: hash.c,v 1.34 2009/04/28 13:01:09 drh Exp $ +** $Id: hash.c,v 1.35 2009/04/28 15:43:45 drh Exp $ */ #include "sqliteInt.h" #include @@ -60,7 +60,7 @@ void sqlite3HashClear(Hash *pH){ /* ** Hash and comparison functions when the mode is SQLITE_HASH_STRING */ -static int strHash(const void *pKey, int nKey){ +static unsigned int strHash(const void *pKey, int nKey){ const char *z = (const char *)pKey; int h = 0; assert( nKey>0 ); @@ -68,7 +68,7 @@ static int strHash(const void *pKey, int nKey){ h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++]; nKey--; } - return h & 0x7fffffff; + return h; } static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){ if( n1!=n2 ) return 1; @@ -76,7 +76,8 @@ static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){ } -/* Link an element into the hash table +/* Link pNew element into the hash table pH. If pEntry!=0 then also +** insert pNew into the pEntry hash bucket. */ static void insertElement( Hash *pH, /* The complete hash table */ @@ -84,7 +85,13 @@ static void insertElement( HashElem *pNew /* The element to be inserted */ ){ HashElem *pHead; /* First element already in pEntry */ - pHead = pEntry->chain; + if( pEntry ){ + pHead = pEntry->count ? pEntry->chain : 0; + pEntry->count++; + pEntry->chain = pNew; + }else{ + pHead = 0; + } if( pHead ){ pNew->next = pHead; pNew->prev = pHead->prev; @@ -97,44 +104,45 @@ static void insertElement( pNew->prev = 0; pH->first = pNew; } - pEntry->count++; - pEntry->chain = pNew; } /* Resize the hash table so that it cantains "new_size" buckets. -** "new_size" must be a power of 2. The hash table might fail -** to resize if sqlite3_malloc() fails. +** +** The hash table might fail to resize if sqlite3_malloc() fails or +** if the new size is the same as the prior size. +** Return TRUE if the resize occurs and false if not. */ -static void rehash(Hash *pH, int new_size){ +static int rehash(Hash *pH, unsigned int new_size){ struct _ht *new_ht; /* The new hash table */ HashElem *elem, *next_elem; /* For looping over existing elements */ -#ifdef SQLITE_MALLOC_SOFT_LIMIT +#if SQLITE_MALLOC_SOFT_LIMIT>0 if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){ new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht); } - if( new_size==pH->htsize ) return; + if( new_size==pH->htsize ) return 0; #endif - /* There is a call to sqlite3_malloc() inside rehash(). If there is - ** already an allocation at pH->ht, then if this malloc() fails it - ** is benign (since failing to resize a hash table is a performance - ** hit only, not a fatal error). + /* The inability to allocates space for a larger hash table is + ** a performance hit but it is not a fatal error. So mark the + ** allocation as a benign. */ - if( pH->htsize>0 ) sqlite3BeginBenignMalloc(); - new_ht = (struct _ht *)sqlite3MallocZero( new_size*sizeof(struct _ht) ); - if( pH->htsize>0 ) sqlite3EndBenignMalloc(); + sqlite3BeginBenignMalloc(); + new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) ); + sqlite3EndBenignMalloc(); - if( new_ht==0 ) return; + if( new_ht==0 ) return 0; sqlite3_free(pH->ht); pH->ht = new_ht; - pH->htsize = new_size; + pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); + memset(new_ht, 0, new_size*sizeof(struct _ht)); for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - int h = strHash(elem->pKey, elem->nKey) & (new_size-1); + unsigned int h = strHash(elem->pKey, elem->nKey) % new_size; next_elem = elem->next; insertElement(pH, &new_ht[h], elem); } + return 1; } /* This function (for internal use only) locates an element in an @@ -144,8 +152,8 @@ static void rehash(Hash *pH, int new_size){ 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. */ + int nKey, /* Bytes in key (not counting zero terminator) */ + unsigned int h /* The hash for this key. */ ){ HashElem *elem; /* Used to loop thru the element list */ int count; /* Number of elements left to test */ @@ -154,12 +162,15 @@ static HashElem *findElementGivenHash( struct _ht *pEntry = &pH->ht[h]; elem = pEntry->chain; count = pEntry->count; - while( count-- && elem ){ - if( strCompare(elem->pKey,elem->nKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; + }else{ + elem = pH->first; + count = pH->count; + } + while( count-- && ALWAYS(elem) ){ + if( strCompare(elem->pKey,elem->nKey,pKey,nKey)==0 ){ + return elem; } + elem = elem->next; } return 0; } @@ -170,7 +181,7 @@ static HashElem *findElementGivenHash( 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 */ + unsigned int h /* Hash value for the element */ ){ struct _ht *pEntry; if( elem->prev ){ @@ -181,13 +192,13 @@ static void removeElementGivenHash( if( elem->next ){ elem->next->prev = elem->prev; } - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; - } - pEntry->count--; - if( pEntry->count<=0 ){ - pEntry->chain = 0; + if( pH->ht ){ + pEntry = &pH->ht[h]; + if( pEntry->chain==elem ){ + pEntry->chain = elem->next; + } + pEntry->count--; + assert( pEntry->count>=0 ); } if( pH->copyKey ){ sqlite3_free(elem->pKey); @@ -201,28 +212,23 @@ static void removeElementGivenHash( } } -/* Attempt to locate an element of the hash table pH with a key -** that matches pKey,nKey. Return a pointer to the corresponding -** HashElem structure for this element if it is found, or NULL -** otherwise. -*/ -HashElem *sqlite3HashFindElem(const Hash *pH, const void *pKey, int nKey){ - int h; /* A hash on key */ - HashElem *elem; /* The element that matches key */ - - if( pH==0 || pH->ht==0 ) return 0; - h = strHash(pKey,nKey); - elem = findElementGivenHash(pH,pKey,nKey, h % pH->htsize); - return elem; -} - /* Attempt to locate an element of the hash table pH with a key ** that matches pKey,nKey. Return the data for this element if it is ** found, or NULL if there is no match. */ void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){ HashElem *elem; /* The element that matches key */ - elem = sqlite3HashFindElem(pH, pKey, nKey); + unsigned int h; /* A hash on key */ + + assert( pH!=0 ); + assert( pKey!=0 ); + assert( nKey>0 ); + if( pH->ht ){ + h = strHash(pKey, nKey) % pH->htsize; + }else{ + h = 0; + } + elem = findElementGivenHash(pH, pKey, nKey, h); return elem ? elem->data : 0; } @@ -242,34 +248,36 @@ void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){ ** element corresponding to "key" is removed from the hash table. */ void *sqlite3HashInsert(Hash *pH, const 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 */ + unsigned 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 */ assert( pH!=0 ); - hraw = strHash(pKey, nKey); + assert( pKey!=0 ); + assert( nKey>0 ); if( pH->htsize ){ - h = hraw % pH->htsize; - elem = findElementGivenHash(pH,pKey,nKey,h); - if( elem ){ - void *old_data = elem->data; - if( data==0 ){ - removeElementGivenHash(pH,elem,h); - }else{ - elem->data = data; - if( !pH->copyKey ){ - elem->pKey = (void *)pKey; - } - assert(nKey==elem->nKey); + h = strHash(pKey, nKey) % pH->htsize; + }else{ + h = 0; + } + elem = findElementGivenHash(pH,pKey,nKey,h); + if( elem ){ + void *old_data = elem->data; + if( data==0 ){ + removeElementGivenHash(pH,elem,h); + }else{ + elem->data = data; + if( !pH->copyKey ){ + elem->pKey = (void *)pKey; } - return old_data; + assert(nKey==elem->nKey); } + return old_data; } if( data==0 ) return 0; new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); if( new_elem==0 ) return data; - if( pH->copyKey && pKey!=0 ){ + if( pH->copyKey ){ new_elem->pKey = sqlite3Malloc( nKey ); if( new_elem->pKey==0 ){ sqlite3_free(new_elem); @@ -280,24 +288,17 @@ void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){ new_elem->pKey = (void*)pKey; } new_elem->nKey = nKey; + new_elem->data = data; pH->count++; - if( pH->htsize==0 ){ - rehash(pH, 128/sizeof(pH->ht[0])); - if( pH->htsize==0 ){ - pH->count = 0; - if( pH->copyKey ){ - sqlite3_free(new_elem->pKey); - } - sqlite3_free(new_elem); - return data; + if( pH->count>=10 && pH->count > 2*pH->htsize ){ + if( rehash(pH, pH->count*2) && pH->htsize ){ + h = strHash(pKey, nKey) % pH->htsize; } } - if( pH->count > pH->htsize ){ - rehash(pH,pH->htsize*2); + if( pH->ht ){ + insertElement(pH, &pH->ht[h], new_elem); + }else{ + insertElement(pH, 0, new_elem); } - assert( pH->htsize>0 ); - h = hraw % pH->htsize; - insertElement(pH, &pH->ht[h], new_elem); - new_elem->data = data; return 0; } diff --git a/src/hash.h b/src/hash.h index 16b391f10f..1e3b9a03ec 100644 --- a/src/hash.h +++ b/src/hash.h @@ -12,7 +12,7 @@ ** This is the header file for the generic hash-table implemenation ** used in SQLite. ** -** $Id: hash.h,v 1.12 2008/10/10 17:41:29 drh Exp $ +** $Id: hash.h,v 1.13 2009/04/28 15:43:45 drh Exp $ */ #ifndef _SQLITE_HASH_H_ #define _SQLITE_HASH_H_ @@ -25,12 +25,25 @@ typedef struct HashElem HashElem; ** 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 +** However, some of the "procedures" and "functions" for modifying and ** accessing this structure are really macros, so we can't really make ** this structure opaque. +** +** All elements of the hash table are on a single doubly-linked list. +** Hash.first points to the head of this list. +** +** There are Hash.htsize buckets. Each bucket points to a spot in +** the global doubly-linked list. The contents of the bucket are the +** element pointed to plus the next _ht.count-1 elements in the list. +** +** Hash.htsize and Hash.ht may be zero. In that case lookup is done +** by a linear search of the global list. For small tables, the +** Hash.ht table is never allocated because if there are few elements +** in the table, it is faster to do a linear search than to manage +** the hash table. */ struct Hash { - unsigned int copyKey: 1; /* True if copy of key made on insert */ + unsigned int copyKey : 1; /* True if copy of key made on insert */ unsigned int htsize : 31; /* Number of buckets in the hash table */ unsigned int count; /* Number of entries in this table */ HashElem *first; /* The first element of the array */ @@ -76,12 +89,12 @@ void sqlite3HashClear(Hash*); #define sqliteHashFirst(H) ((H)->first) #define sqliteHashNext(E) ((E)->next) #define sqliteHashData(E) ((E)->data) -#define sqliteHashKey(E) ((E)->pKey) -#define sqliteHashKeysize(E) ((E)->nKey) +/* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */ +/* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */ /* ** Number of entries in a hash table */ -#define sqliteHashCount(H) ((H)->count) +/* #define sqliteHashCount(H) ((H)->count) // NOT USED */ #endif /* _SQLITE_HASH_H_ */ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c3124757bc..e2b451b42e 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.861 2009/04/24 15:46:22 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.862 2009/04/28 15:43:45 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -145,10 +145,10 @@ #endif /* -** If SQLITE_MALLOC_SOFT_LIMIT is defined, then try to keep the +** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the ** sizes of memory allocations below this value where possible. */ -#if defined(SQLITE_POW2_MEMORY_SIZE) && !defined(SQLITE_MALLOC_SOFT_LIMIT) +#if !defined(SQLITE_MALLOC_SOFT_LIMIT) # define SQLITE_MALLOC_SOFT_LIMIT 1024 #endif diff --git a/test/table.test b/test/table.test index 55508e92d9..9d12296aaa 100644 --- a/test/table.test +++ b/test/table.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE TABLE statement. # -# $Id: table.test,v 1.50 2009/03/14 08:37:24 danielk1977 Exp $ +# $Id: table.test,v 1.51 2009/04/28 15:43:45 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -112,15 +112,16 @@ do_test table-2.1e { catchsql {CREATE TABLE IF NOT EXISTS test2(x UNIQUE, y TEXT PRIMARY KEY)} } {0 {}} do_test table-2.1f { +breakpoint execsql {DROP TABLE test2; SELECT name FROM sqlite_master WHERE type!='meta'} } {} # Verify that we cannot make a table with the same name as an index # do_test table-2.2a { - execsql {CREATE TABLE test2(one text); CREATE INDEX test3 ON test2(one)} - set v [catch {execsql {CREATE TABLE test3(two text)}} msg] - lappend v $msg + execsql {CREATE TABLE test2(one text)} + execsql {CREATE INDEX test3 ON test2(one)} + catchsql {CREATE TABLE test3(two text)} } {1 {there is already an index named test3}} do_test table-2.2b { db close