Simplifications to the symbol table implementation in hash.c. For very small

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
This commit is contained in:
drh 2009-04-28 15:43:45 +00:00
parent ebb329397c
commit 8a1e594c9f
6 changed files with 124 additions and 109 deletions

View File

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

View File

@ -1 +1 @@
7975b6f2ec7736b3656a264c2f7e7e95ce7a78e4
5c737835dec9e6038b304c198aa14337a6f23c1c

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

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

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

View 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