From 767c2001c6c3bd610a0b6fee392e46e04b27a3a4 Mon Sep 17 00:00:00 2001
From: drh
Date: Thu, 19 Oct 2000 14:10:08 +0000
Subject: [PATCH] Added the "memory:" driver (CVS 158)
FossilOrigin-Name: 54d60c68dc83410e911b828a680772541c44e9df
---
Makefile.in | 6 +-
VERSION | 2 +-
manifest | 34 +-
manifest.uuid | 2 +-
src/dbbe.c | 4 +-
src/dbbemem.c | 967 ++++++++++++++++++++++++++--------------------
test/all.test | 8 +-
test/index.test | 9 +-
test/lock.test | 6 +-
test/select2.test | 14 +-
test/table.test | 13 +-
test/tester.tcl | 56 ++-
test/vacuum.test | 4 +-
www/changes.tcl | 5 +
14 files changed, 684 insertions(+), 446 deletions(-)
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 "
"
}
+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.