]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Added the "memory:" driver (CVS 158)
authordrh <drh@noemail.net>
Thu, 19 Oct 2000 14:10:08 +0000 (14:10 +0000)
committerdrh <drh@noemail.net>
Thu, 19 Oct 2000 14:10:08 +0000 (14:10 +0000)
FossilOrigin-Name: 54d60c68dc83410e911b828a680772541c44e9df

14 files changed:
Makefile.in
VERSION
manifest
manifest.uuid
src/dbbe.c
src/dbbemem.c
test/all.test
test/index.test
test/lock.test
test/select2.test
test/table.test
test/tester.tcl
test/vacuum.test
www/changes.tcl

index 4556180a0a0911060f78816cc0ef94734ceb3707..f186f8ee847a2a864a48fa6b8e37399fbde5f4b7 100644 (file)
@@ -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 2ac9634d32a9bc0a6fd85a02b73541f10672520c..5b09c67c2066cc1cf4b56a66f4f9875115b9f4ad 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.13
+1.0.14
index f60825a3335151ce962fa87f2f38ac61ec6e29d5..b0c85bad25c50b8d8fdd8ea3a65fa0009748a86a 100644 (file)
--- 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
index 5002f682971f8c2d8e3d6aa6539acdc85067cdd4..fb47449c914edb20e143d15b6d2dd0f64f0de8e6 100644 (file)
@@ -1 +1 @@
-b9c84fa57912d7d777b4ebefc12627d80a2fc378
\ No newline at end of file
+54d60c68dc83410e911b828a680772541c44e9df
\ No newline at end of file
index 9a3f4b3254e93c2f9f3ed5b39cb252802bb90c7b..43948c1e110d2cc5dd0e4dd1327aeb8abcb01f35 100644 (file)
@@ -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);
 }
index 439230767d34101e52a4ed0305a68c3210debdbb..8ce8b1d75e9d3c1ca5532d6b1295a8664116f22e 100644 (file)
@@ -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
 ** 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 <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
 #include <time.h>
 
+
+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;
+};
+
 /*
-** The following structure holds the current state of the RC4 algorithm.
-** We use RC4 as a random number generator.  Each call to RC4 gives
-** a random 8-bit number.
+** 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.
 **
-** Nothing in this file or anywhere else in SQLite does any kind of
-** encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
-** number generator) not as an encryption device.
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
 */
-struct rc4 {
-  int i, j;
-  int s[256];
+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;
+  }
+}
+
 /*
-** Key or data is stored as an instance of the following
+** Generate a hash from an N-byte key
 */
-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 */
+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;
 }
 
 /*
@@ -65,48 +331,56 @@ typedef struct datum {
 ** 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 */
+** 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 complete database is an instance of the following structure.
+** The following structure holds the current state of the RC4 algorithm.
+** We use RC4 as a random number generator.  Each call to RC4 gives
+** a random 8-bit number.
+**
+** Nothing in this file or anywhere else in SQLite does any kind of
+** encryption.  The RC4 algorithm is being used as a PRNG (pseudo-random
+** number generator) not as an encryption device.
+*/
+struct rc4 {
+  int i, j;
+  int s[256];
+};
+
+/*
+** The following structure contains all information used by GDBM
+** database driver.  This is a subclass of the Dbbe 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; i<pFile->nHash; 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; i<pBe->nTemp; 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.
-*/
-static int sqliteStrHash(const char *z){
-  int h = 0;
-  while( *z ){
-    h = (h<<3) ^ h ^ *(z++);
-  }
-  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;
+** 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 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);
+    }
   }
-  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;
-    }
-    pFile->zName = zFile;
-    pFile->nRef = 1;
-    pFile->pPrev = 0;
-    if( pBe->pOpen ){
-      pBe->pOpen->pPrev = pFile;
+      pTble->zName = 0;
+      pTble->delOnClose = 1;
     }
-    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
-*/
-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;
-      }
-    }
-  }
-}
-
-/*
-** Drop a table from the database.  The file that corresponds
+** Drop a table from the database.  The file on the disk that corresponds
 ** to this table is deleted.
 */
-void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){
-  File *pFile;
-
-  pFile = sqliteDbbeFindFile(pBe, zTable);
-  if( pFile ){
-    sqliteDbbeUnlinkFile(pFile);
-    sqliteDbbeFreeFile(pFile);
-  }
-}
-
-/*
-** Reorganize a table to reduce search times and disk usage.
-*/
-int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){
-  return SQLITE_OK;
+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);
 }
 
 /*
-** Close a cursor previously opened by sqliteDbbeOpenCursor().
+** 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 BeFile.nref field of the
+** closed.  This routine decrements the MTable.nref field of the
 ** underlying file and closes the file when nref reaches 0.
 */
-void sqliteDbbeCloseCursor(DbbeCursor *pCursr){
-  BeFile *pFile;
-  Dbbe *pBe;
+static void sqliteMemCloseCursor(DbbeCursor *pCursr){
+  MTable *pTble;
+  Dbbex *pBe;
   if( pCursr==0 ) return;
-  pFile = pCursr->pFile;
+  pTble = pCursr->pTble;
   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);
-    }
+  if( pTble->delOnClose ){
+    deleteMTable(pTble);
   }
-  memset(pCursr, 0, sizeof(*pCursr));
   sqliteFree(pCursr);
 }
 
 /*
-** Compute a hash on binary data
+** Reorganize a table to reduce search times and disk usage.
 */
-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; i<pFile->nHash; 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];
+static int sqliteMemReorganizeTable(Dbbe *pBe, const char *zTable){
+  /* Do nothing */
+  return SQLITE_OK;
 }
 
 /*
 ** 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; h<pFile->nHash && 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; i<pBe->nTemp; 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; i<pBe->nTemp; 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;
+}
index 52f5b93ad7c563a7e5923eb2160690b55f3a3eb0..3f4fc53b6f58783b6f3e8e6ebfdd791ba1b9ce1d 100644 (file)
@@ -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
index 0f8b495fc8144c6f2832ae202226ebcc032d65d9..79fe7d9b879f524fb80d652121e13f636640ef9e 100644 (file)
@@ -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 {
index 5ca16b0096671254bad17807f881a7721ab2be13..7b1c9a7375aefdcb9ddee9356a11e27776373e68 100644 (file)
@@ -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
+
+}
index 7437cbe60d0ee7536efa568d11745861e24e8948..ab88f71d5a9c7e7e062e902684dcd8ec6f79561a 100644 (file)
@@ -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
index fbfd15acbcdbb34ffbb5485b18c6f9235e24617c..17d7fc1b13bb5127a3462e60a8303bcfe1026a9d 100644 (file)
@@ -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)}
index 390368d80f66d01ddc920a7cc6bf55e45d9571b9..6a85c21418ad08cf2162a6ae84c3a59c21e66bc2 100644 (file)
 # 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 {} {
index 565994e31e4f5724eef9b729c05e1c1ef229e2a0..802a70021e749b2c6ab5163b5c6136a4b2c00834 100644 (file)
@@ -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]
index 87147a18d7b6ceb48d77e7a8af03cc166ac8b0b4..23a91cc6db1b4f850e7be16e50907d2a04d2aff4 100644 (file)
@@ -17,6 +17,11 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chng {2000 Oct 19 (1.0.14)} {
+<li>Added a "memory:" backend driver that stores its database in an
+    in-memory hash table.</li>
+}
+
 chng {2000 Oct 18 (1.0.13)} {
 <li>Break out the GDBM driver into a separate file in anticipation
     to added new drivers.</li>