]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Simplifications to the symbol table implementation in hash.c. For very small
authordrh <drh@noemail.net>
Tue, 28 Apr 2009 15:43:45 +0000 (15:43 +0000)
committerdrh <drh@noemail.net>
Tue, 28 Apr 2009 15:43:45 +0000 (15:43 +0000)
symbol tables (less than 10 entries) a simple linked list is used instead
of a hash table.  Number of hash table buckets is limited to prevent large
allocations. (CVS 6559)

FossilOrigin-Name: 5c737835dec9e6038b304c198aa14337a6f23c1c

manifest
manifest.uuid
src/hash.c
src/hash.h
src/sqliteInt.h
test/table.test

index 9822d3b9458007b06a811bf00e2241593427d13f..8d12edeb61f6f6ae5a77f4364865971e7260c027 100644 (file)
--- 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
index e5e071d3a63c18239a5d862e6f44894bb89ceebd..554b4f92d9a69fcef34f8d86402eeb98944d0f01 100644 (file)
@@ -1 +1 @@
-7975b6f2ec7736b3656a264c2f7e7e95ce7a78e4
\ No newline at end of file
+5c737835dec9e6038b304c198aa14337a6f23c1c
\ No newline at end of file
index 3761fb547917d9a602cc9161fd01c312f72fe831..9d9be94e748bb17c08478a849c62635f6eb021f6 100644 (file)
@@ -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 <assert.h>
@@ -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;
 }
index 16b391f10fa144eb2e0fc1cb97bff8b9eff96738..1e3b9a03ec6287d7427f4a4ca90833cade339140 100644 (file)
@@ -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_ */
index c3124757bc2f80e482a4efbe8e035bce2c24c107..e2b451b42ef29c4876af97bd6ea5c2623166d811 100644 (file)
@@ -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_
 #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
 
index 55508e92d98145f60e0bc8f8f3f10018a840a7b2..9d12296aaa2a35e6ad2282c30cdfabdd7b6d6ec3 100644 (file)
@@ -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