diff --git a/Makefile.in b/Makefile.in index 4556180a0a..f186f8ee84 100644 --- a/Makefile.in +++ b/Makefile.in @@ -47,7 +47,7 @@ LIBREADLINE = @TARGET_READLINE_LIBS@ # Object files for the SQLite library. # -LIBOBJ = build.o dbbe.o dbbegdbm.o delete.o expr.o insert.o \ +LIBOBJ = build.o dbbe.o dbbegdbm.o dbbemem.o delete.o expr.o insert.o \ main.o parse.o printf.o select.o table.o tokenize.o update.o \ util.o vdbe.o where.o tclsqlite.o @@ -57,6 +57,7 @@ SRC = \ $(TOP)/src/build.c \ $(TOP)/src/dbbe.c \ $(TOP)/src/dbbegdbm.c \ + $(TOP)/src/dbbemem.c \ $(TOP)/src/dbbe.h \ $(TOP)/src/dbbemem.c \ $(TOP)/src/delete.c \ @@ -122,6 +123,9 @@ dbbe.o: $(TOP)/src/dbbe.c $(HDR) dbbegdbm.o: $(TOP)/src/dbbegdbm.c $(HDR) $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c +dbbemem.o: $(TOP)/src/dbbemem.c $(HDR) + $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbemem.c + main.o: $(TOP)/src/main.c $(HDR) $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c diff --git a/VERSION b/VERSION index 2ac9634d32..5b09c67c20 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.13 +1.0.14 diff --git a/manifest b/manifest index f60825a333..b0c85bad25 100644 --- a/manifest +++ b/manifest @@ -1,17 +1,17 @@ -C Version\s1.0.13\s(CVS\s490) -D 2000-10-19T02:00:00 +C Added\sthe\s"memory:"\sdriver\s(CVS\s158) +D 2000-10-19T14:10:08 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 -F Makefile.in e52c865acc544f539820b2c3efad5af6e6a0c533 +F Makefile.in 0b1fdafa55e1bf4d3a4f5213544130e66ef32052 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 -F VERSION 8029dd7a9837bd0f6a598758411f7b68ecc8d433 +F VERSION 9c65d78e59cfa4cd1ee3aab6a1775f71371a5996 F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 F src/build.c e2ceba852dc45ca899e68a042b29c3daab011575 -F src/dbbe.c 5f8f9aa18a17e728e604dc48bc435111ce7d4f73 +F src/dbbe.c 7e01384320075bf1d3e7fb54984df73435908809 F src/dbbe.h d175a04b35ea75078274e059dcbcbf7c1262d42a F src/dbbegdbm.c 4ac7222afff0cf91014803f8791740b6da825a2b -F src/dbbemem.c eb3d79be7105bd80069815ee499c8e8682876378 +F src/dbbemem.c ce8f5ce6b93c5f916e3238956b204fccf2cafda3 F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065 F src/expr.c e8e350d7baa33bd9ed8701c159eaba5e912e0adb F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958 @@ -31,29 +31,29 @@ F src/util.c 811e0ad47f842c16555aaf361b26dab7221c1a6c F src/vdbe.c a876c75429903acb9167b741b0513ef0198f6001 F src/vdbe.h 140cdec3c56f70483e169f8ae657bd90f9fd6e98 F src/where.c 3dfad2ffd0aa994d5eceac88852f7189c8d1d3c8 -F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7 +F test/all.test 4d7a1652fb65cabc6660fedd0ddb138ea78da624 F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb F test/dbbe.test bd2cd9fe84c6d69b6ae42ac5f55b1e940bdca886 F test/delete.test 402ee3ccb6e544582d24c573ef70b34d09583ae7 F test/expr.test 48273bf48a15d226c35829f702af4254c0ff6795 F test/func.test 02aed8845b98bde1043dda97455de1d37238ebb3 F test/in.test 2c560c0f55fb777029fd9bb5378f2997582aa603 -F test/index.test 950be6116122c6e2db7c2c345eabcdb854ced1d0 +F test/index.test ee060ef8912be47ba616e50cce7985259a68d58a F test/insert.test 66f4c3bd600fec8eb1e733b928cbe6fa885eff0c F test/insert2.test 732405e30331635af8d159fccabe835eea5cd0c6 -F test/lock.test 4b334f4978cf68321b76f1854b6ee2232e4659e5 +F test/lock.test f56cf41d29d2c4cbaa6239424b5b0ee844c273a0 F test/main.test b7366cc6f3690915a11834bc1090deeff08acaf9 F test/select1.test 68ff778c24fc8982e63dda37acb5b0396913adf7 -F test/select2.test 45c28211702b5c82b06dd624a112ea17417f343c +F test/select2.test 0c24b9bb8825ebb96e6cc65f1eb61bace0e02aa0 F test/select3.test a9234b8424b6c6d71de534f43b91ade9be68e9cc F test/select4.test cb5374d7c87680e294ac749307459a5cc547609d F test/select5.test e2b9d51d88cbd6c307c2c05b0ef55fe7ba811ac2 F test/sort.test d582086c4bb7df3fbf50aa72e69d7e235e9f8e31 F test/subselect.test bf8b251a92fb091973c1c469ce499dc9648a41d5 -F test/table.test 620cd72a6c29da3b9153d15c9e94abbbb282373b -F test/tester.tcl ad57d7e8114b0691eb5e943b9dabbd68119b8e2c +F test/table.test eaa25951c0f18615763cd3dc248ea4bc38739c05 +F test/tester.tcl 59edb045efc11478be291182c0455b790c00043a F test/update.test 62f6ce99ff31756aab0ca832ff6d34c5a87b6250 -F test/vacuum.test 8becf5cfeb897108b35cdd996793e7f1df2f28fd +F test/vacuum.test 2127748ff4ddb409212efbb6d9fb9c469ea1b49c F test/where.test bbab5a308055fb6087dc23d600b4ad2b72797397 F tool/gdbmdump.c 529e67c78d920606ba196326ea55b57b75fcc82b F tool/lemon.c b13a31798574af881753d38f4da7d505929259c3 @@ -66,7 +66,7 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8 F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6 F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be F www/c_interface.tcl 1a0b13d056625e4acb59b67edc360cfd9c92ba90 -F www/changes.tcl 08e23dd0438b5b5ef3a67dbf57e065186343c9be +F www/changes.tcl 742f6ab4eeb9b74814169a242b4769b843769506 F www/crosscompile.tcl bee79c34f6c3f162ec1c6f5294e79f73651d27ee F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9 F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0 @@ -76,7 +76,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl ae101d5f7c07dcc59770e2a84aae09025fab2dad F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520 -P 979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c -R f2349cd3f015fde9fcbd6e3d9e05118d +P b9c84fa57912d7d777b4ebefc12627d80a2fc378 +R e2499a42784d4a08fc7792702e632947 U drh -Z 01572c0f8e01066746bb3a9054777b1b +Z 90f27b216d91b6f176f776431859d864 diff --git a/manifest.uuid b/manifest.uuid index 5002f68297..fb47449c91 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b9c84fa57912d7d777b4ebefc12627d80a2fc378 \ No newline at end of file +54d60c68dc83410e911b828a680772541c44e9df \ No newline at end of file diff --git a/src/dbbe.c b/src/dbbe.c index 9a3f4b3254..43948c1e11 100644 --- a/src/dbbe.c +++ b/src/dbbe.c @@ -30,7 +30,7 @@ ** relatively simple to convert to a different database such ** as NDBM, SDBM, or BerkeleyDB. ** -** $Id: dbbe.c,v 1.20 2000/10/19 01:49:02 drh Exp $ +** $Id: dbbe.c,v 1.21 2000/10/19 14:10:09 drh Exp $ */ #include "sqliteInt.h" @@ -56,11 +56,9 @@ Dbbe *sqliteDbbeOpen( if( strncmp(zName, "gdbm:", 5)==0 ){ return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg); } -#if 0 if( strncmp(zName, "memory:", 7)==0 ){ extern Dbbe *sqliteMemOpen(const char*,int,int,char**); return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg); } -#endif return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg); } diff --git a/src/dbbemem.c b/src/dbbemem.c index 439230767d..8ce8b1d75e 100644 --- a/src/dbbemem.c +++ b/src/dbbemem.c @@ -1,5 +1,5 @@ /* -** Copyright (c) 1999, 2000 D. Richard Hipp +** Copyright (c) 2000 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public @@ -26,15 +26,320 @@ ** sqlite and the code that does the actually reading and writing ** of information to the disk. ** -** This file implements a backend that constructs a database in -** memory using hash tables. Nothing is ever read or written -** to disk. Everything is forgotten when the program exits. +** This file uses an in-memory hash talbe as the database backend. ** -** $Id: dbbemem.c,v 1.1 2000/10/11 19:28:52 drh Exp $ +** $Id: dbbemem.c,v 1.2 2000/10/19 14:10:09 drh Exp $ */ #include "sqliteInt.h" +#include +#include +#include #include + +typedef struct Array Array; +typedef struct ArrayElem ArrayElem; +typedef struct Datum Datum; + +/* A complete associative array 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 Array { + int count; /* Number of entries in the array */ + ArrayElem *first; /* The first element of the array */ + int htsize; /* Number of buckets in the hash table */ + struct _Array_ht { /* the hash table */ + int count; /* Number of entries with this hash */ + ArrayElem *chain; /* Pointer to first entry with this hash */ + } *ht; +}; + +/* +** An instance of the following structure stores a single key or +** data element. +*/ +struct Datum { + int n; + void *p; +}; + +/* Each element in the associative array 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 ArrayElem { + ArrayElem *next, *prev; /* Next and previous elements in the array */ + Datum key, data; /* Key and data for this element */ +}; + +/* Some routines are so simple that they can be implemented as macros +** These are given first. */ + +/* Return the number of entries in the array */ +#define ArrayCount(X) ((X)->count) + +/* Return a pointer to the first element of the array */ +#define ArrayFirst(X) ((X)->first) + +/* Return a pointer to the next (or previous) element of the array */ +#define ArrayNext(X) ((X)->next) +#define ArrayPrev(X) ((X)->prev) + +/* Return TRUE if the element given is the last element in the array */ +#define ArrayIsLast(X) ((X)->next==0) +#define ArrayIsFirst(X) ((X)->prev==0) + +/* Return the data or key for an element of the array */ +#define ArrayData(X) ((X)->data.p) +#define ArrayDataSize(X) ((X)->data.n) +#define ArrayKey(X) ((X)->key.p) +#define ArrayKeySize(X) ((X)->key.n) + +/* Turn bulk memory into an associative array object by initializing the +** fields of the Array structure. +*/ +static void ArrayInit(Array *new){ + new->first = 0; + new->count = 0; + new->htsize = 0; + new->ht = 0; +} + +/* Remove all entries from an associative array. Reclaim all memory. +** This is the opposite of ArrayInit(). +*/ +static void ArrayClear(Array *array){ + ArrayElem *elem; /* For looping over all elements of the array */ + + elem = array->first; + array->first = 0; + array->count = 0; + if( array->ht ) sqliteFree(array->ht); + array->ht = 0; + array->htsize = 0; + while( elem ){ + ArrayElem *next_elem = elem->next; + sqliteFree(elem); + elem = next_elem; + } +} + +/* +** Generate a hash from an N-byte key +*/ +static int ArrayHash(Datum d){ + int h = 0; + while( d.n-- > 0 ){ + h = (h<<9) ^ (h<<3) ^ h ^ *(((char*)d.p)++); + } + if( h<0 ) h = -h; + return h; +} + +/* Resize the hash table for a Array array +*/ +static void ArrayRehash(Array *array, int new_size){ + struct _Array_ht *new_ht; /* The new hash table */ + ArrayElem *elem, *next_elem; /* For looping over existing elements */ + int i; /* Loop counter */ + ArrayElem *x; /* Element being copied to new hash table */ + + new_ht = sqliteMalloc( new_size*sizeof(struct _Array_ht) ); + if( array->ht ) sqliteFree(array->ht); + array->ht = new_ht; + array->htsize = new_size; + for(i=new_size-1; i>=0; i--){ + new_ht[i].count = 0; + new_ht[i].chain = 0; + } + for(elem=array->first, array->first=0; elem; elem = next_elem){ + int h = ArrayHash(elem->key) & (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 array->first = elem; + x->prev = elem; + }else{ + elem->next = array->first; + if( array->first ) array->first->prev = elem; + elem->prev = 0; + array->first = elem; + } + new_ht[h].chain = elem; + new_ht[h].count++; + } +} + +/* This function (for internal use only) locates an element in an +** array that matches the given key. The hash for this key has +** already been computed and is passed as the 3rd parameter. +*/ +static ArrayElem *ArrayFindElementGivenHash( + const Array *array, /* The array to be searched */ + const Datum key, /* The key we are searching for */ + int h /* The hash for this key. */ +){ + ArrayElem *elem; /* Used to loop thru the element list */ + int count; /* Number of elements left to test */ + + if( array->count ){ + elem = array->ht[h].chain; + count = array->ht[h].count; + while( count-- && elem ){ + if( elem->key.n==key.n && memcmp(elem->key.p,key.p,key.n)==0 ){ + return elem; + } + elem = elem->next; + } + } + return 0; +} + + +/* Attempt to locate an element of the associative array with a key +** that matches "key". Return the ArrayElement if found and NULL if +** if no match. +*/ +static ArrayElem *ArrayFindElement(const Array *array, Datum key){ + int h; /* A hash on key */ + if( array->count==0 ) return 0; + h = ArrayHash(key); + return ArrayFindElementGivenHash(array, key, h & (array->htsize-1)); +} + +/* Remove a single entry from the array given a pointer to that +** element and a hash on the element's key. +*/ +static void ArrayRemoveElementGivenHash( + Array *array, /* The array containing "elem" */ + ArrayElem* elem, /* The element to be removed from the array */ + int h /* Hash value for the element */ +){ + if( elem->prev ){ + elem->prev->next = elem->next; + }else{ + array->first = elem->next; + } + if( elem->next ){ + elem->next->prev = elem->prev; + } + if( array->ht[h].chain==elem ){ + array->ht[h].chain = elem->next; + } + array->ht[h].count--; + if( array->ht[h].count<=0 ){ + array->ht[h].chain = 0; + } + sqliteFree( elem ); + array->count--; +} + +/* Attempt to locate an element of the associative array with a key +** that matches "key". Return the data for this element if it is +** found, or NULL if no match is found. +*/ +static Datum ArrayFind(const Array *array, Datum key){ + int h; /* A hash on key */ + ArrayElem *elem; /* The element that matches key */ + static Datum nil = {0, 0}; + + if( array->count==0 ) return nil; + h = ArrayHash(key); + elem = ArrayFindElementGivenHash(array, key, h & (array->htsize-1)); + return elem ? elem->data : nil; +} + +/* Insert an element into the array. The key will be "key" and +** the data will be "data". +** +** If no array element exists with a matching key, then a new +** array element is created. The key is copied into the new element. +** But only a pointer to the data is stored. 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 array. +*/ +static Datum ArrayInsert(Array *array, Datum key, Datum data){ + int hraw; /* Raw hash value of the key */ + int h; /* the hash of the key modulo hash table size */ + ArrayElem *elem; /* Used to loop thru the element list */ + ArrayElem *new_elem; /* New element added to the array */ + Datum rv; /* Return value */ + static Datum nil = {0, 0}; + + hraw = ArrayHash(key); + h = hraw & (array->htsize-1); + elem = ArrayFindElementGivenHash(array,key,h); + if( elem ){ + Datum old_data = elem->data; + if( data.p==0 ){ + ArrayRemoveElementGivenHash(array,elem,h); + }else{ + elem->data = data; + } + return old_data; + } + if( data.p==0 ) return nil; + new_elem = (ArrayElem*)sqliteMalloc( sizeof(ArrayElem) + key.n ); + if( new_elem==0 ) return nil; + new_elem->key.n = key.n; + new_elem->key.p = (void*)&new_elem[1]; + memcpy(new_elem->key.p, key.p, key.n); + array->count++; + if( array->htsize==0 ) ArrayRehash(array,4); + if( array->count > array->htsize ){ + ArrayRehash(array,array->htsize*2); + } + h = hraw & (array->htsize-1); + elem = array->ht[h].chain; + if( elem ){ + new_elem->next = elem; + new_elem->prev = elem->prev; + if( elem->prev ){ elem->prev->next = new_elem; } + else { array->first = new_elem; } + elem->prev = new_elem; + }else{ + new_elem->next = array->first; + new_elem->prev = 0; + if( array->first ){ array->first->prev = new_elem; } + array->first = new_elem; + } + array->ht[h].count++; + array->ht[h].chain = new_elem; + new_elem->data = data; + rv.p = 0; + rv.n = 0; + return rv; +} + +/* +** Information about each open database table is an instance of this +** structure. There will only be one such structure for each +** table. If the VDBE opens the same table twice (as will happen +** for a self-join, for example) then two DbbeCursor structures are +** created but there is only a single MTable structure. +*/ +typedef struct MTable MTable; +struct MTable { + char *zName; /* Name of the table */ + int delOnClose; /* Delete when closing */ + Array data; /* The data in this stable */ +}; + /* ** The following structure holds the current state of the RC4 algorithm. ** We use RC4 as a random number generator. Each call to RC4 gives @@ -50,63 +355,32 @@ struct rc4 { }; /* -** Key or data is stored as an instance of the following +** The following structure contains all information used by GDBM +** database driver. This is a subclass of the Dbbe structure. */ -typedef struct datum { - void *dptr; /* The data */ - int dsize; /* Number of bytes of data */ - void *kptr; /* The key */ - int ksize; /* Number of bytes of key */ - datum *pHash; /* Next datum with the same hash */ -} - -/* -** Information about each open database table is an instance of this -** structure. There will only be one such structure for each -** table. If the VDBE opens the same table twice (as will happen -** for a self-join, for example) then two DbbeCursor structures are -** created but there is only a single BeFile structure with an -** nRef of 2. -*/ -typedef struct BeFile BeFile; -struct BeFile { - char *zName; /* Name of the table */ - BeFile *pNext, *pPrev; /* Next and previous on list of all tables */ - BeFile *pHash; /* Next table with same hash on zName */ - int nRef; /* Number of cursor that have this file open */ - int delOnClose; /* Delete when the last cursor closes this file */ - int nRec; /* Number of entries in the hash table */ - int nHash; /* Number of slots in the hash table */ - datum **aHash; /* The hash table */ -}; - -/* -** The complete database is an instance of the following structure. -*/ -struct Dbbe { - BeFile *pOpen; /* List of open tables */ +typedef struct Dbbex Dbbex; +struct Dbbex { + Dbbe dbbe; /* The base class */ + Array tables; /* All tables of the database */ int nTemp; /* Number of temporary files created */ FILE **apTemp; /* Space to hold temporary file pointers */ char **azTemp; /* Names of the temporary files */ struct rc4 rc4; /* The random number generator */ - BeFile aHash[331]; /* Hash table of tables */ }; /* ** An cursor into a database file is an instance of the following structure. -** There can only be a single BeFile structure for each disk file, but +** There can only be a single MTable structure for each disk file, but ** there can be multiple DbbeCursor structures. Each DbbeCursor represents -** a cursor pointing to a particular part of the open BeFile. The -** BeFile.nRef field hold a count of the number of DbbeCursor structures +** a cursor pointing to a particular part of the open MTable. The +** MTable.nRef field hold a count of the number of DbbeCursor structures ** associated with the same disk file. */ struct DbbeCursor { - Dbbe *pBe; /* The database of which this record is a part */ - BeFile *pFile; /* The database file for this table */ - datum *pRec; /* Most recently used key and data */ - int h; /* Hash of pRec */ + Dbbex *pBe; /* The database of which this record is a part */ + MTable *pTble; /* The database file for this table */ + ArrayElem *elem; /* Most recently accessed record */ int needRewind; /* Next key should be the first */ - int readPending; /* The fetch hasn't actually been done yet */ }; /* @@ -146,65 +420,36 @@ static int rc4byte(struct rc4 *p){ } /* -** This routine opens a new database. For the GDBM driver -** implemented here, the database name is the name of the directory -** containing all the files of the database. -** -** If successful, a pointer to the Dbbe structure is returned. -** If there are errors, an appropriate error message is left -** in *pzErrMsg and NULL is returned. +** Forward declaration */ -Dbbe *sqliteDbbeOpen( - const char *zName, /* The name of the database */ - int writeFlag, /* True if we will be writing to the database */ - int createFlag, /* True to create database if it doesn't exist */ - char **pzErrMsg /* Write error messages (if any) here */ -){ - Dbbe *pNew; - time_t now; - - pNew = sqliteMalloc(sizeof(Dbbe)); - if( pNew==0 ){ - sqliteSetString(pzErrMsg, "out of memory", 0); - return 0; - } - pNew->pOpen = 0; - time(&now); - rc4init(&pNew->rc4, (char*)&now, sizeof(now)); - return pNew; -} +static void sqliteMemCloseCursor(DbbeCursor *pCursr); /* -** Free all of the memory associated with a single BeFile structure. -** It is assumed that this BeFile structure has already been unlinked -** from its database. +** Erase all the memory of an MTable */ -static void sqliteDbbeFreeFile(BeFile *pFile){ - int i; - for(i=0; inHash; i++){ - datum *pDatum, *pNextDatum; - for(pDatum = pFile->aHash[i]; pDatum; pDatum=pNextDatum){ - pNextDatum = pDatum->pHash; - sqliteFree(pDatum->dptr); - sqliteFree(pDatum); - } +static void deleteMTable(MTable *p){ + ArrayElem *i; + for(i=ArrayFirst(&p->data); i; i=ArrayNext(i)){ + void *data = ArrayData(i); + sqliteFree(data); } - sqliteFree(pFile->zName); - sqliteFree(pFile->aHash); - memset(pFile, 0, sizeof(*pFile)); - sqliteFree(pFile); + ArrayClear(&p->data); + sqliteFree(p); } /* ** Completely shutdown the given database. Close all files. Free all memory. */ -void sqliteDbbeClose(Dbbe *pBe){ - BeFile *pFile, *pNext; +static void sqliteMemClose(Dbbe *pDbbe){ + Dbbex *pBe = (Dbbex*)pDbbe; + MTable *pTble, *pNext; int i; - for(pFile=pBe->pOpen; pFile; pFile=pNext){ - pNext = pFile->pNext; - sqliteDbbeFreeFile(pFile); + ArrayElem *j, *k; + for(j=ArrayFirst(&pBe->tables); j; j=ArrayNext(j)){ + pTble = ArrayData(j); + deleteMTable(pTble); } + ArrayClear(&pBe->tables); for(i=0; inTemp; i++){ if( pBe->apTemp[i]!=0 ){ unlink(pBe->azTemp[i]); @@ -243,29 +488,23 @@ static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){ } /* -** Hash a NULL-terminated string. +** Translate the name of an SQL table (or index) into its +** canonical name. +** +** Space to hold the canonical name is obtained from +** sqliteMalloc() and must be freed by the calling function. */ -static int sqliteStrHash(const char *z){ - int h = 0; - while( *z ){ - h = (h<<3) ^ h ^ *(z++); +static char *sqliteNameOfTable(const char *zTable){ + char *zNew = 0; + int i, c; + sqliteSetString(&zNew, zTable, 0); + if( zNew==0 ) return 0; + for(i=0; (c = zNew[i])!=0; i++){ + if( isupper(c) ){ + zNew[i] = tolower(c); + } } - if( h<0 ) h = -h; - return h; -} - -/* -** Locate a file in a database -*/ -static BeFile *sqliteDbbeFindFile(Dbbe *pBe, const char *zFile){ - int h; - BeFile *pFile; - - h = sqliteStrHash(zFile) % (sizeof(pBe->aHash)/sizeof(pBe->aHash[0])); - for(pFile=pBe->aHash[h]; pFile; pFile=pFile->pHash){ - if( strcmp(pFile->zName, zFile)==0 ) break; - } - return pFile; + return zNew; } /* @@ -292,283 +531,163 @@ static BeFile *sqliteDbbeFindFile(Dbbe *pBe, const char *zFile){ ** a cursor to that temporary file is opened. The temporary file ** will be deleted from the disk when it is closed. */ -int sqliteDbbeOpenCursor( - Dbbe *pBe, /* The database the table belongs to */ +static int sqliteMemOpenCursor( + Dbbe *pDbbe, /* The database the table belongs to */ const char *zTable, /* The SQL name of the file to be opened */ int writeable, /* True to open for writing */ DbbeCursor **ppCursr /* Write the resulting table pointer here */ ){ - char *zFile; /* Name of the table file */ DbbeCursor *pCursr; /* The new table cursor */ - BeFile *pFile; /* The underlying data file for this table */ + char *zName; /* Canonical table name */ + MTable *pTble; /* The underlying data file for this table */ int rc = SQLITE_OK; /* Return value */ int rw_mask; /* Permissions mask for opening a table */ int mode; /* Mode for opening a table */ + Dbbex *pBe = (Dbbex*)pDbbe; *ppCursr = 0; pCursr = sqliteMalloc( sizeof(*pCursr) ); if( pCursr==0 ) return SQLITE_NOMEM; if( zTable ){ - zFile = sqliteStrDup(zTable); - pFile = sqliteDbbeFindFile(zFile); + Datum key; + zName = sqliteNameOfTable(zTable); + key.p = zName; + key.n = strlen(zName); + pTble = ArrayFind(&pBe->tables, key).p; }else{ - pFile = 0; - zFile = 0; + zName = 0; + pTble = 0; } - if( pFile==0 ){ - pFile = sqliteMalloc( sizeof(*pFile) ); - if( pFile==0 ){ - sqliteFree(zFile); + if( pTble==0 ){ + pTble = sqliteMalloc( sizeof(*pTble) ); + if( pTble==0 ){ + sqliteFree(zName); return SQLITE_NOMEM; } - if( zFile ){ - if( !writeable || pBe->write ){ - pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0); - }else{ - pFile->dbf = 0; - } + if( zName ){ + Datum ins_key, ins_data; + pTble->zName = zName; + pTble->delOnClose = 0; + ins_data.p = pTble; + ins_data.n = sizeof( *pTble ); + ins_key.p = zName; + ins_key.n = strlen(zName); + ArrayInsert(&pBe->tables, ins_key, ins_data); }else{ - int limit; - struct rc4 *pRc4; - char zRandom[50]; - pRc4 = &pBe->rc4; - zFile = 0; - limit = 5; - while( 1 ){ - randomName(&pBe->rc4, zRandom, "_temp_table_"); - sqliteFree(zFile); - zFile = sqliteStrDup(zRandom); - if( sqliteDbbeFindFile(pBe, zFile)==0 ) break; - } - pFile->delOnClose = 1; + pTble->zName = 0; + pTble->delOnClose = 1; } - pFile->zName = zFile; - pFile->nRef = 1; - pFile->pPrev = 0; - if( pBe->pOpen ){ - pBe->pOpen->pPrev = pFile; - } - pFile->pNext = pBe->pOpen; - pBe->pOpen = pFile; + ArrayInit(&pTble->data); }else{ - sqliteFree(zFile); - pFile->nRef++; + sqliteFree(zName); } pCursr->pBe = pBe; - pCursr->pFile = pFile; - pCursr->readPending = 0; + pCursr->pTble = pTble; pCursr->needRewind = 1; - if( rc!=SQLITE_OK ){ - sqliteDbbeCloseCursor(pCursr); - *ppCursr = 0; - }else{ - *ppCursr = pCursr; - } + *ppCursr = pCursr; return rc; } /* -** Unlink a file from the database +** Drop a table from the database. The file on the disk that corresponds +** to this table is deleted. */ -static void sqliteDbbeUnlinkFile(Dbbe *pBe, BeFile *pFile){ - int h = sqliteStrHash(pFile->zName) % - (sizeof(pBe->aHash)/sizeof(pBe->aHash[0]))); - if( pBe->aHash[h]==pFile ){ - pBe->aHash[h] = pFile->pHash; - }else{ - BeFile *pProbe; - for(pProbe=pBe->aHash[h]; pProbe; pProbe=pProbe->pHash){ - if( pProbe->pHash==pFile ){ - pProbe->pHash = pFile->pHash; - break; - } - } +static void sqliteMemDropTable(Dbbe *pDbbe, const char *zTable){ + char *zName; /* Name of the table file */ + Datum key, data; + MTable *pTble; + ArrayElem *i; + Dbbex *pBe = (Dbbex*)pDbbe; + + zName = sqliteNameOfTable(zTable); + key.p = zName; + key.n = strlen(zName); + pTble = ArrayFind(&pBe->tables, key).p; + if( pTble ){ + data.p = 0; + data.n = 0; + ArrayInsert(&pBe->tables, key, data); + deleteMTable(pTble); } + sqliteFree(zName); } /* -** Drop a table from the database. The file that corresponds -** to this table is deleted. +** Close a cursor previously opened by sqliteMemOpenCursor(). +** +** There can be multiple cursors pointing to the same open file. +** The underlying file is not closed until all cursors have been +** closed. This routine decrements the MTable.nref field of the +** underlying file and closes the file when nref reaches 0. */ -void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){ - File *pFile; - - pFile = sqliteDbbeFindFile(pBe, zTable); - if( pFile ){ - sqliteDbbeUnlinkFile(pFile); - sqliteDbbeFreeFile(pFile); +static void sqliteMemCloseCursor(DbbeCursor *pCursr){ + MTable *pTble; + Dbbex *pBe; + if( pCursr==0 ) return; + pTble = pCursr->pTble; + pBe = pCursr->pBe; + if( pTble->delOnClose ){ + deleteMTable(pTble); } + sqliteFree(pCursr); } /* ** Reorganize a table to reduce search times and disk usage. */ -int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){ +static int sqliteMemReorganizeTable(Dbbe *pBe, const char *zTable){ + /* Do nothing */ return SQLITE_OK; } -/* -** Close a cursor previously opened by sqliteDbbeOpenCursor(). -** -** There can be multiple cursors pointing to the same open file. -** The underlying file is not closed until all cursors have been -** closed. This routine decrements the BeFile.nref field of the -** underlying file and closes the file when nref reaches 0. -*/ -void sqliteDbbeCloseCursor(DbbeCursor *pCursr){ - BeFile *pFile; - Dbbe *pBe; - if( pCursr==0 ) return; - pFile = pCursr->pFile; - pBe = pCursr->pBe; - pFile->nRef--; - if( pFile->nRef<=0 ){ - if( pFile->pPrev ){ - pFile->pPrev->pNext = pFile->pNext; - }else{ - pBe->pOpen = pFile->pNext; - } - if( pFile->pNext ){ - pFile->pNext->pPrev = pFile->pPrev; - } - if( pFile->delOnClose ){ - sqliteDbbeUnlinkFile(pFile); - sqliteDbbeFreeFile(pFile); - } - } - memset(pCursr, 0, sizeof(*pCursr)); - sqliteFree(pCursr); -} - -/* -** Compute a hash on binary data -*/ -static int sqliteBinaryHash(int n, char *z){ - int h = 0; - while( n-- ){ - h = (h<<9) ^ (h<<3) ^ h ^ *(z++); - } - if( h<0 ) h = -h; - return h; -} - -/* -** Resize the hash table -*/ -static void sqliteDbbeRehash(BeFile *pFile, int nHash){ - int i, h; - datum *pRec, *pNextRec; - datum **aHash; - - if( nHash<1 ) return; - aHash = sqliteMalloc( sizeof(aHash[0])*nHash ); - if( aHash==0 ) return; - for(i=0; inHash; i++){ - for(pRec=pFile->aHash[i]; pRec; pRec=pNextRec){ - pNextRec = pRec->pHash; - h = sqliteBinaryHash(pRec->ksize, pRec->kptr) % nHash; - pRec->pHash = aHash[h]; - aHash[h] = pRec; - } - } - sqliteFree(pFile->aHash); - pFile->aHash = aHash; - pFile->nHash = nHash; -} - -/* -** Locate a datum in a file. Create it if it isn't already there and -** the createFlag is set. -*/ -static datum **sqliteDbbeLookup( - BeFile *pFile, /* Where to look */ - int nKey, /* The size of the key */ - char *pKey, /* The key */ - int *pH, /* Write the hash line here */ - int createFlag /* Create a new entry if this is true */ -){ - int h; - datum **ppRec = 0; - datum *pNew; - if( pFile->nHash>0 ){ - h = sqliteBinaryHash(nKey, pKey) % pFile->nHash; - ppRec = &pFile->aHash[h]; - while( *ppRec ){ - if( (**ppRec).ksize==nKey && memcpy((**ppRec).kptr, pKey, nKey)==0 ){ - if( *pH ) *pH = h; - return ppRec; - } - } - } - if( createFlag==0 ) return 0; - if( (pFile->nRec + 1) > pFile->nHash*2 ){ - int nHash = (pFile->nRec + 1)*4; - if( nHash<51 ) nHash = 51; - sqliteDbbeRehash(pFile, nHash); - if( pFile->nHash==0 ) return 0; - } - h = sqliteBinaryHash(nKey, pKey) % pFile->nHash; - pNew = sqliteMalloc( sizeof(*pNew) + nKey ); - if( pNew==0 ) return 0; - pNew->kptr = (void*)&pNew[1]; - pNew->ksize = nKey; - memcpy(pNew->kptr, pkey, nKey); - pNew->pHash = pFile->aHash[h]; - pFile->aHash[h] = pNew; - pNew->dsize = 0; - pNew->dptr = 0; - pFile->nRec++; - if( pH ) *pH = h; - return &pFile->aHash[h]; -} - /* ** Fetch a single record from an open cursor. Return 1 on success ** and 0 on failure. */ -int sqliteDbbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){ - datum **ppRec; - ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 0); - if( ppRec ){ - pCursr->pRec = *ppRec; - } - return pCursr->pRec!=0; +static int sqliteMemFetch(DbbeCursor *pCursr, int nKey, char *pKey){ + Datum key; + key.n = nKey; + key.p = pKey; + pCursr->elem = ArrayFindElement(&pCursr->pTble->data, key); + return pCursr->elem!=0; } /* ** Return 1 if the given key is already in the table. Return 0 ** if it is not. */ -int sqliteDbbeTest(DbbeCursor *pCursr, int nKey, char *pKey){ - return sqliteDbbeFetch(pCursr, nKey, pKey); +static int sqliteMemTest(DbbeCursor *pCursr, int nKey, char *pKey){ + return sqliteMemFetch(pCursr, nKey, pKey); } /* ** Copy bytes from the current key or data into a buffer supplied by ** the calling function. Return the number of bytes copied. */ -int sqliteDbbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){ +static +int sqliteMemCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){ int n; - datum *pRec; - if( (pRec = pCursor->pRec)==0 || offset>=pRec->ksize ) return 0; - if( offset+size>pRec->ksize ){ - n = pRec->ksize - offset; + if( pCursr->elem==0 ) return 0; + if( offset>=ArrayKeySize(pCursr->elem) ) return 0; + if( offset+size>ArrayKeySize(pCursr->elem) ){ + n = ArrayKeySize(pCursr->elem) - offset; }else{ n = size; } - memcpy(zBuf, pRec->kptr[offset], n); + memcpy(zBuf, &((char*)ArrayKey(pCursr->elem))[offset], n); return n; } -int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){ +static +int sqliteMemCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){ int n; - datum *pRec; - if( (pRec = pCursr->pRec)==0 || offset>=pRec->dsize ) return 0; - if( offset+size>pRec->dsize ){ - n = pRec->dsize - offset; + if( pCursr->elem==0 ) return 0; + if( offset>=ArrayDataSize(pCursr->elem) ) return 0; + if( offset+size>ArrayDataSize(pCursr->elem) ){ + n = ArrayDataSize(pCursr->elem) - offset; }else{ n = size; } - memcpy(zBuf, &pRec->dptr[offset], n); + memcpy(zBuf, &((char*)ArrayData(pCursr->elem))[offset], n); return n; } @@ -576,32 +695,34 @@ int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){ ** Return a pointer to bytes from the key or data. The data returned ** is ephemeral. */ -char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){ - datum *pRec; - if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->ksize ) return ""; - return &pRec->kptr[offset]; +static char *sqliteMemReadKey(DbbeCursor *pCursr, int offset){ + if( pCursr->elem==0 || offset<0 || offset>=ArrayKeySize(pCursr->elem) ){ + return ""; + } + return &((char*)ArrayKey(pCursr->elem))[offset]; } -char *sqliteDbbeReadData(DbbeCursor *pCursr, int offset){ - datum *pRec; - if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->dsize ) return ""; - return &pRec->dptr[offset]; +static char *sqliteMemReadData(DbbeCursor *pCursr, int offset){ + if( pCursr->elem==0 || offset<0 || offset>=ArrayDataSize(pCursr->elem) ){ + return ""; + } + return &((char*)ArrayData(pCursr->elem))[offset]; } /* ** Return the total number of bytes in either data or key. */ -int sqliteDbbeKeyLength(DbbeCursor *pCursr){ - return pCursr->pRec ? pCursor->pRec->ksize : 0; +static int sqliteMemKeyLength(DbbeCursor *pCursr){ + return pCursr->elem ? ArrayKeySize(pCursr->elem) : 0; } -int sqliteDbbeDataLength(DbbeCursor *pCursr){ - return pCursr->pRec ? pCursor->pRec->dsize : 0; +static int sqliteMemDataLength(DbbeCursor *pCursr){ + return pCursr->elem ? ArrayDataSize(pCursr->elem) : 0; } /* ** Make is so that the next call to sqliteNextKey() finds the first ** key of the table. */ -int sqliteDbbeRewind(DbbeCursor *pCursr){ +static int sqliteMemRewind(DbbeCursor *pCursr){ pCursr->needRewind = 1; return SQLITE_OK; } @@ -610,42 +731,26 @@ int sqliteDbbeRewind(DbbeCursor *pCursr){ ** Read the next key from the table. Return 1 on success. Return ** 0 if there are no more keys. */ -int sqliteDbbeNextKey(DbbeCursor *pCursr){ - int h; - BeFile *pFile; - if( pCursr==0 || (pFile = pCursr->pFile)==0 || pFile->nHash==0 ){ - return 0; - } - if( pCursr->needRewind ){ - pCursr->pRec = 0; - pCursr->h = -1; - } - if( pCursr->pRec ){ - pCursr->pRec = pCursr->pRec->pHash; - } - if( pCursr->pRec==0 ){ - for(h=pCursr->h; hnHash && pFile->aHash[h]==0; h++){} - if( h>=pFile->nHash ){ - pCursr->h = -1; - return 0; - }else{ - pCursr->h = h; - pCursr->pRec = pFile->aHash[h]; - return 1; - } +static int sqliteMemNextKey(DbbeCursor *pCursr){ + if( pCursr->needRewind || pCursr->elem==0 ){ + pCursr->elem = ArrayFirst(&pCursr->pTble->data); + pCursr->needRewind = 0; + }else{ + pCursr->elem = ArrayNext(pCursr->elem); } + return pCursr->elem!=0; } /* ** Get a new integer key. */ -int sqliteDbbeNew(DbbeCursor *pCursr){ +static int sqliteMemNew(DbbeCursor *pCursr){ int iKey; + Datum key; int go = 1; int i; struct rc4 *pRc4; - if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1; pRc4 = &pCursr->pBe->rc4; while( go ){ iKey = 0; @@ -653,7 +758,9 @@ int sqliteDbbeNew(DbbeCursor *pCursr){ iKey = (iKey<<8) + rc4byte(pRc4); } if( iKey==0 ) continue; - go = sqliteDbbeLookup(pCursr->pFile, sizeof(iKey), &iKey, 0, 0)!=0; + key.p = (char*)&iKey; + key.n = 4; + go = ArrayFindElement(&pCursr->pTble->data, key)!=0; } return iKey; } @@ -662,43 +769,33 @@ int sqliteDbbeNew(DbbeCursor *pCursr){ ** Write an entry into the table. Overwrite any prior entry with the ** same key. */ -int sqliteDbbePut( - DbbeCursor *pCursr, /* Write to this cursor */ - int nKey, /* Size of the key */ - char *pKey, /* The key */ - int nData, /* Size of the data */ - char *pData /* The data */ -){ - int rc; - datum **ppRec, *pRec; - if( pCursr->pFile==0 ) return SQLITE_ERROR; - ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 1); - if( ppRec==0 ) return SQLITE_NOMEM; - pRec = *ppRec; - sqliteFree(pRec->dptr); - pRec->dptr = sqliteMalloc( nData ); - if( pRec->dptr==0 ) return SQLITE_NOMEM; - memcpy(pRec->dptr, pData, nData); - pRec->dsize = nData; +static int +sqliteMemPut(DbbeCursor *pCursr, int nKey,char *pKey, int nData, char *pData){ + Datum data, key; + data.n = nData; + data.p = sqliteMalloc( data.n ); + memcpy(data.p, pData, data.n); + key.n = nKey; + key.p = pKey; + data = ArrayInsert(&pCursr->pTble->data, key, data); + if( data.p ){ + sqliteFree(data.p); + } return SQLITE_OK; } /* ** Remove an entry from a table, if the entry exists. */ -int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){ - datum **ppRec, *pRec; - ppRec = sqliteDbbeLookcup(pCursr->pFile, nKey, pKey, 0, 0); - if( ppRec ){ - pRec = *ppRec; - *ppRec = pRec->pNext; - if( pCursr->pRec==pRec ){ - pCursr->pRec = 0; - pCursr->h = -1; - } - sqliteFree(pRec->dptr); - sqliteFree(pRec); - pCursr->pFile->nRec--; +static int sqliteMemDelete(DbbeCursor *pCursr, int nKey, char *pKey){ + Datum key, data; + key.n = nKey; + key.p = pKey; + data.p = 0; + data.n = 0; + data = ArrayInsert(&pCursr->pTble->data, key, data); + if( data.p ){ + sqliteFree(data.p); } return SQLITE_OK; } @@ -710,12 +807,13 @@ int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){ ** and then immediately unlinking the file. That works great ** under Unix, but fails when we try to port to Windows. */ -int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){ - char *zFile; /* Full name of the temporary file */ +static int sqliteMemOpenTempFile(Dbbe *pDbbe, FILE **ppTble){ + char *zName; /* Full name of the temporary file */ char zBuf[50]; /* Base name of the temporary file */ int i; /* Loop counter */ int limit; /* Prevent an infinite loop */ int rc = SQLITE_OK; /* Value returned by this function */ + Dbbex *pBe = (Dbbex*)pDbbe; for(i=0; inTemp; i++){ if( pBe->apTemp[i]==0 ) break; @@ -726,33 +824,34 @@ int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){ pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) ); } if( pBe->apTemp==0 ){ - *ppFile = 0; + *ppTble = 0; return SQLITE_NOMEM; } limit = 4; - zFile = 0; + zName = 0; do{ - randomName(&pBe->rc4, zBuf, "/_temp_file_"); - sqliteFree(zFile); - zFile = 0; - sqliteSetString(&zFile, pBe->zDir, zBuf, 0); - }while( access(zFile,0)==0 && limit-- >= 0 ); - *ppFile = pBe->apTemp[i] = fopen(zFile, "w+"); + randomName(&pBe->rc4, zBuf, "/tmp/_temp_file_"); + sqliteFree(zName); + zName = 0; + sqliteSetString(&zName, zBuf, 0); + }while( access(zName,0)==0 && limit-- >= 0 ); + *ppTble = pBe->apTemp[i] = fopen(zName, "w+"); if( pBe->apTemp[i]==0 ){ rc = SQLITE_ERROR; - sqliteFree(zFile); + sqliteFree(zName); pBe->azTemp[i] = 0; }else{ - pBe->azTemp[i] = zFile; + pBe->azTemp[i] = zName; } return rc; } /* -** Close a temporary file opened using sqliteDbbeOpenTempFile() +** Close a temporary file opened using sqliteMemOpenTempFile() */ -void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){ +static void sqliteMemCloseTempFile(Dbbe *pDbbe, FILE *f){ int i; + Dbbex *pBe = (Dbbex*)pDbbe; for(i=0; inTemp; i++){ if( pBe->apTemp[i]==f ){ unlink(pBe->azTemp[i]); @@ -764,3 +863,53 @@ void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){ } fclose(f); } + + +/* +** This routine opens a new database. For the GDBM driver +** implemented here, the database name is the name of the directory +** containing all the files of the database. +** +** If successful, a pointer to the Dbbe structure is returned. +** If there are errors, an appropriate error message is left +** in *pzErrMsg and NULL is returned. +*/ +Dbbe *sqliteMemOpen( + const char *zName, /* The name of the database */ + int writeFlag, /* True if we will be writing to the database */ + int createFlag, /* True to create database if it doesn't exist */ + char **pzErrMsg /* Write error messages (if any) here */ +){ + Dbbex *pNew; + long now; + + pNew = sqliteMalloc( sizeof(*pNew) ); + if( pNew==0 ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + return 0; + } + ArrayInit(&pNew->tables); + pNew->dbbe.Close = sqliteMemClose; + pNew->dbbe.OpenCursor = sqliteMemOpenCursor; + pNew->dbbe.DropTable = sqliteMemDropTable; + pNew->dbbe.ReorganizeTable = sqliteMemReorganizeTable; + pNew->dbbe.CloseCursor = sqliteMemCloseCursor; + pNew->dbbe.Fetch = sqliteMemFetch; + pNew->dbbe.Test = sqliteMemTest; + pNew->dbbe.CopyKey = sqliteMemCopyKey; + pNew->dbbe.CopyData = sqliteMemCopyData; + pNew->dbbe.ReadKey = sqliteMemReadKey; + pNew->dbbe.ReadData = sqliteMemReadData; + pNew->dbbe.KeyLength = sqliteMemKeyLength; + pNew->dbbe.DataLength = sqliteMemDataLength; + pNew->dbbe.NextKey = sqliteMemNextKey; + pNew->dbbe.Rewind = sqliteMemRewind; + pNew->dbbe.New = sqliteMemNew; + pNew->dbbe.Put = sqliteMemPut; + pNew->dbbe.Delete = sqliteMemDelete; + pNew->dbbe.OpenTempFile = sqliteMemOpenTempFile; + pNew->dbbe.CloseTempFile = sqliteMemCloseTempFile; + time(&now); + rc4init(&pNew->rc4, (char*)&now, sizeof(now)); + return &pNew->dbbe; +} diff --git a/test/all.test b/test/all.test index 52f5b93ad7..3f4fc53b6f 100644 --- a/test/all.test +++ b/test/all.test @@ -22,7 +22,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: all.test,v 1.2 2000/06/02 14:27:23 drh Exp $ +# $Id: all.test,v 1.3 2000/10/19 14:10:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -36,6 +36,12 @@ if {[file exists ./sqlite_test_count]} { } for {set Counter 0} {$Counter<$COUNT} {incr Counter} { + set dbprefix memory: + foreach testfile [lsort -dictionary [glob $testdir/*.test]] { + if {[file tail $testfile]=="all.test"} continue + source $testfile + } + set dbprefix gdbm: foreach testfile [lsort -dictionary [glob $testdir/*.test]] { if {[file tail $testfile]=="all.test"} continue source $testfile diff --git a/test/index.test b/test/index.test index 0f8b495fc8..79fe7d9b87 100644 --- a/test/index.test +++ b/test/index.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the CREATE INDEX statement. # -# $Id: index.test,v 1.7 2000/08/02 13:47:42 drh Exp $ +# $Id: index.test,v 1.8 2000/10/19 14:10:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -39,12 +39,14 @@ do_test index-1.1b { execsql {SELECT name, sql, tbl_name, type FROM sqlite_master WHERE name='index1'} } {index1 {CREATE INDEX index1 ON test1(f1)} test1 index} +skipif memory: do_test index-1.1c { db close sqlite db testdb execsql {SELECT name, sql, tbl_name, type FROM sqlite_master WHERE name='index1'} } {index1 {CREATE INDEX index1 ON test1(f1)} test1 index} +skipif memory: do_test index-1.1d { db close sqlite db testdb @@ -106,6 +108,7 @@ set r {} for {set i 1} {$i<100} {incr i} { lappend r testdb/index$i.tbl } +skipif memory: do_test index-3.2 { execsql {INSERT INTO test1 VALUES(1,2,3,4,5)} lsort -dictionary [glob testdb/index*.tbl] @@ -223,6 +226,10 @@ do_test index-7.1 { for {set i 1} {$i<20} {incr i} { execsql "INSERT INTO test1 VALUES($i,[expr {int(pow(2,$i))}])" } + execsql {SELECT count(*) FROM test1} +} {19} +skipif memory: +do_test index-7.1b { lsort -dictionary [glob testdb/test1*.tbl] } {testdb/test1.tbl testdb/test1__primary_key.tbl} do_test index-7.2 { diff --git a/test/lock.test b/test/lock.test index 5ca16b0096..7b1c9a7375 100644 --- a/test/lock.test +++ b/test/lock.test @@ -23,7 +23,9 @@ # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock.test,v 1.3 2000/10/16 22:06:43 drh Exp $ +# $Id: lock.test,v 1.4 2000/10/19 14:10:09 drh Exp $ + +if {$dbprefix=="gdbm:"} { set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -99,3 +101,5 @@ catch {exec kill -HUP $::lock_pid} catch {exec kill -9 $::lock_pid} finish_test + +} diff --git a/test/select2.test b/test/select2.test index 7437cbe60d..ab88f71d5a 100644 --- a/test/select2.test +++ b/test/select2.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select2.test,v 1.8 2000/07/29 13:07:00 drh Exp $ +# $Id: select2.test,v 1.9 2000/10/19 14:10:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -104,9 +104,16 @@ do_test select2-3.2c { execsql {SELECT f1 FROM tbl2 WHERE f2=1000} } {500} do_test select2-3.2d { + execsql {SELECT fcnt() FROM tbl2 WHERE 1000=f2} +} {2} +do_test select2-3.2e { + execsql {SELECT fcnt() FROM tbl2 WHERE f2=1000} +} {2} +testif gdbm: +do_test select2-3.2f { set t1 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE 1000=f2}} 1] 0] set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2=1000}} 1] 0] - expr {$t1*0.8<$t2 && $t2*0.8<$t1} + expr {$t1*0.7<$t2 && $t2*0.7<$t1} } {1} # Make sure queries run faster with an index than without @@ -117,5 +124,8 @@ do_test select2-3.3 { set t2 [lindex [time {execsql {SELECT f1 FROM tbl2 WHERE f2==2000}} 1] 0] expr {$t1*10 < $t2} } {1} +do_test select2-3.4 { + expr {[execsql {SELECT fcnt() FROM tbl2 WHERE f2==2000}]>10} +} {1} finish_test diff --git a/test/table.test b/test/table.test index fbfd15acbc..17d7fc1b13 100644 --- a/test/table.test +++ b/test/table.test @@ -23,7 +23,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.6 2000/08/02 13:47:43 drh Exp $ +# $Id: table.test,v 1.7 2000/10/19 14:10:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -49,6 +49,9 @@ do_test table-1.1 { # do_test table-1.2 { execsql {INSERT INTO test1 VALUES('hi', 'y''all')} +} {} +testif gdbm: +do_test table-1.2b { lsort [glob -nocomplain testdb/*.tbl] } {testdb/sqlite_master.tbl testdb/test1.tbl} @@ -61,6 +64,7 @@ do_test table-1.3 { # Close and reopen the database. Verify that everything is # still the same. # +skipif memory: do_test table-1.4 { db close sqlite db testdb @@ -76,6 +80,7 @@ do_test table-1.5 { # Verify that the file associated with the database is gone. # +testif gdbm: do_test table-1.5 { lsort [glob -nocomplain testdb/*.tbl] } {testdb/sqlite_master.tbl} @@ -83,6 +88,7 @@ do_test table-1.5 { # Close and reopen the database. Verify that the table is # still gone. # +skipif memory: do_test table-1.6 { db close sqlite db testdb @@ -121,6 +127,7 @@ do_test table-2.1b { set v [catch {execsql {CREATE TABLE sqlite_master(two text)}} msg] lappend v $msg } {1 {table sqlite_master already exists}} +skipif memory: do_test table-2.1c { db close sqlite db testdb @@ -138,6 +145,7 @@ do_test table-2.2a { set v [catch {execsql {CREATE TABLE test3(two text)}} msg] lappend v $msg } {1 {there is already an index named test3}} +skipif memory: do_test table-2.2b { db close sqlite db testdb @@ -198,6 +206,7 @@ do_test table-3.4 { set v [catch {execsql {CREATE TABLE bIg(xyz foo)}} msg] lappend v $msg } {1 {table bIg already exists}} +skipif memory: do_test table-3.5 { db close sqlite db testdb @@ -226,6 +235,7 @@ do_test table-4.1 { } execsql {SELECT name FROM sqlite_master WHERE type!='meta' ORDER BY name} } $r +skipif memory: do_test table-4.1b { db close sqlite db testdb @@ -287,6 +297,7 @@ do_test table-5.4 { # Create a table with a goofy name # +testif gdbm: do_test table-6.1 { execsql {CREATE TABLE 'Spaces In This Name!'(x int)} execsql {INSERT INTO 'spaces in this name!' VALUES(1)} diff --git a/test/tester.tcl b/test/tester.tcl index 390368d80f..6a85c21418 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -23,13 +23,27 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.6 2000/09/21 13:01:37 drh Exp $ +# $Id: tester.tcl,v 1.7 2000/10/19 14:10:09 drh Exp $ # Create a test database # -file delete -force testdb -file mkdir testdb -sqlite db testdb +if {![info exists dbprefix]} { + if {[info exists env(SQLITE_PREFIX)]} { + set dbprefix $env(SQLITE_PREFIX): + } else { + set dbprefix "gdbm:" + } +} +switch $dbprefix { + gdbm: { + file delete -force testdb + file mkdir testdb + } + memory: { + # do nothing + } +} +sqlite db ${dbprefix}testdb # Abort early if this script has been run before. # @@ -39,12 +53,17 @@ if {[info exists nTest]} return # set nErr 0 set nTest 0 +set skip_test 0 # Invoke the do_test procedure to run a single test # proc do_test {name cmd expected} { - global argv nErr nTest - if {[llength $argv]==0} { + global argv nErr nTest skip_test + if {$skip_test} { + set skip_test 0 + return + } + if {[llength $argv]==0} { set go 1 } else { set go 0 @@ -57,7 +76,7 @@ proc do_test {name cmd expected} { } if {!$go} return incr nTest - puts -nonewline $name... + puts -nonewline $::dbprefix$name... flush stdout if {[catch {uplevel #0 "$cmd;\n"} result]} { puts "\nError: $result" @@ -70,6 +89,29 @@ proc do_test {name cmd expected} { } } +# Skip a test based on the dbprefix +# +proc skipif {args} { + foreach a $args { + if {$::dbprefix==$a} { + set ::skip_test 1 + return + } + } +} + +# Run the next test only if the dbprefix is among the listed arguments +# +proc testif {args} { + foreach a $args { + if {$::dbprefix==$a} { + set ::skip_test 0 + return + } + } + set ::skip_test 1 +} + # Run this routine last # proc finish_test {} { diff --git a/test/vacuum.test b/test/vacuum.test index 565994e31e..802a70021e 100644 --- a/test/vacuum.test +++ b/test/vacuum.test @@ -23,7 +23,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the VACUUM statement. # -# $Id: vacuum.test,v 1.1 2000/06/03 18:06:54 drh Exp $ +# $Id: vacuum.test,v 1.2 2000/10/19 14:10:10 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -53,6 +53,7 @@ execsql {INSERT INTO test1 VALUES(2)} execsql {INSERT INTO test1 VALUES(3)} execsql {INSERT INTO test2 VALUES(4)} +testif gdbm: do_test vacuum-1.3 { set b1 [file mtime testdb/test1.tbl] set b2 [file mtime testdb/test2.tbl] @@ -64,6 +65,7 @@ do_test vacuum-1.3 { set a3 [file mtime testdb/index1.tbl] expr {$a1>$b1 && $a2==$b2 && $a3==$b3} } {1} +testif gdbm: do_test vacuum-1.4 { set b1 [file mtime testdb/test1.tbl] set b2 [file mtime testdb/test2.tbl] diff --git a/www/changes.tcl b/www/changes.tcl index 87147a18d7..23a91cc6db 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -17,6 +17,11 @@ proc chng {date desc} { puts "

    $desc

" } +chng {2000 Oct 19 (1.0.14)} { +
  • Added a "memory:" backend driver that stores its database in an + in-memory hash table.
  • +} + chng {2000 Oct 18 (1.0.13)} {
  • Break out the GDBM driver into a separate file in anticipation to added new drivers.