From bb0b679c02a1d92c331984ce924ecddc1694c928 Mon Sep 17 00:00:00 2001
From: drh
Date: Thu, 19 Oct 2000 01:49:02 +0000
Subject: [PATCH] Break out DBBE drivers into separate files. (CVS 157)
FossilOrigin-Name: 979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c
---
Makefile.in | 6 +-
VERSION | 2 +-
manifest | 25 +-
manifest.uuid | 2 +-
src/dbbe.c | 706 +------------------------------------------
src/dbbe.h | 143 ++++-----
src/dbbegdbm.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++
src/main.c | 4 +-
src/vdbe.c | 89 +++---
www/changes.tcl | 7 +
10 files changed, 942 insertions(+), 818 deletions(-)
create mode 100644 src/dbbegdbm.c
diff --git a/Makefile.in b/Makefile.in
index d0fe73c39e..4556180a0a 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 delete.o expr.o insert.o \
+LIBOBJ = build.o dbbe.o dbbegdbm.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
@@ -56,6 +56,7 @@ LIBOBJ = build.o dbbe.o delete.o expr.o insert.o \
SRC = \
$(TOP)/src/build.c \
$(TOP)/src/dbbe.c \
+ $(TOP)/src/dbbegdbm.c \
$(TOP)/src/dbbe.h \
$(TOP)/src/dbbemem.c \
$(TOP)/src/delete.c \
@@ -118,6 +119,9 @@ build.o: $(TOP)/src/build.c $(HDR)
dbbe.o: $(TOP)/src/dbbe.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbe.c
+dbbegdbm.o: $(TOP)/src/dbbegdbm.c $(HDR)
+ $(TCC) $(GDBM_FLAGS) -c $(TOP)/src/dbbegdbm.c
+
main.o: $(TOP)/src/main.c $(HDR)
$(TCC) $(GDBM_FLAGS) -c $(TOP)/src/main.c
diff --git a/VERSION b/VERSION
index bb83058ed5..2ac9634d32 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.12
+1.0.13
diff --git a/manifest b/manifest
index 3ccd05d93f..87a3221998 100644
--- a/manifest
+++ b/manifest
@@ -1,20 +1,21 @@
-C Version\s1.0.12\s(CVS\s491)
-D 2000-10-17T01:35:00
+C Break\sout\sDBBE\sdrivers\sinto\sseparate\sfiles.\s(CVS\s157)
+D 2000-10-19T01:49:02
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
-F Makefile.in faecea9b6419cec25030b4818c9b3f7f4163b3c1
+F Makefile.in e52c865acc544f539820b2c3efad5af6e6a0c533
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
-F VERSION 0a8a772d5d713cb4d3d7b6ddedf1335436f90263
+F VERSION 8029dd7a9837bd0f6a598758411f7b68ecc8d433
F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F src/build.c e2ceba852dc45ca899e68a042b29c3daab011575
-F src/dbbe.c 226daaf8c095ceb4aff48cad188dad90643f9867
-F src/dbbe.h 6337132f904e72ecb28b07390021c241397e4cbf
+F src/dbbe.c 5f8f9aa18a17e728e604dc48bc435111ce7d4f73
+F src/dbbe.h d175a04b35ea75078274e059dcbcbf7c1262d42a
+F src/dbbegdbm.c 4ac7222afff0cf91014803f8791740b6da825a2b
F src/dbbemem.c eb3d79be7105bd80069815ee499c8e8682876378
F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065
F src/expr.c e8e350d7baa33bd9ed8701c159eaba5e912e0adb
F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958
-F src/main.c 4774731549159ba33c031cfaf6e4c78630c80d96
+F src/main.c 6686df1f9e88fb72c3b3fc660d4595382555fb5c
F src/parse.y 5d199034de5d29ebedb42c1c51f34db4df40cbe5
F src/printf.c 1efb6b3e7f28a93be57132de3f8f400d2ac1460e
F src/select.c c1de8ac34131324fa05664b06b0ae1ee9c02905d
@@ -27,7 +28,7 @@ F src/tclsqlite.c 44b08b47612a668caaf7c4ec32133b69d73ff78e
F src/tokenize.c b0f5c12598105ec924c0733a916485f920168720
F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc
F src/util.c 811e0ad47f842c16555aaf361b26dab7221c1a6c
-F src/vdbe.c 7acc17367da350259107dcb1dac5590e8747b890
+F src/vdbe.c a876c75429903acb9167b741b0513ef0198f6001
F src/vdbe.h 140cdec3c56f70483e169f8ae657bd90f9fd6e98
F src/where.c 3dfad2ffd0aa994d5eceac88852f7189c8d1d3c8
F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7
@@ -65,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 4f3d27bdd02f253e38907c55768871dba3677ddf
+F www/changes.tcl 08e23dd0438b5b5ef3a67dbf57e065186343c9be
F www/crosscompile.tcl bee79c34f6c3f162ec1c6f5294e79f73651d27ee
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
@@ -75,7 +76,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl ae101d5f7c07dcc59770e2a84aae09025fab2dad
F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520
-P 17fcd3b01568e95224425b982fb77abd8b12191a
-R 9d72075e97e2decee6bef55f36d23c73
+P 7330218a912ea788b0c83fa7c7350fd6df88b18a
+R f2349cd3f015fde9fcbd6e3d9e05118d
U drh
-Z dc6f764623dcec0a5a01e239a81f28c8
+Z f79070030a7939f47c5a891f83eee50c
diff --git a/manifest.uuid b/manifest.uuid
index 1166b8ec7f..fe7e38bada 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-7330218a912ea788b0c83fa7c7350fd6df88b18a
\ No newline at end of file
+979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c
\ No newline at end of file
diff --git a/src/dbbe.c b/src/dbbe.c
index b9271ad678..9a3f4b3254 100644
--- a/src/dbbe.c
+++ b/src/dbbe.c
@@ -30,124 +30,17 @@
** relatively simple to convert to a different database such
** as NDBM, SDBM, or BerkeleyDB.
**
-** $Id: dbbe.c,v 1.19 2000/08/17 09:50:00 drh Exp $
+** $Id: dbbe.c,v 1.20 2000/10/19 01:49:02 drh Exp $
*/
#include "sqliteInt.h"
-#include
-#include
-#include
-#include
-#include
/*
-** Information about each open disk file is an instance of this
-** structure. There will only be one such structure for each
-** disk file. If the VDBE opens the same file 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 file */
- GDBM_FILE dbf; /* The file itself */
- int nRef; /* Number of references */
- int delOnClose; /* Delete when closing */
- int writeable; /* Opened for writing */
- BeFile *pNext, *pPrev; /* Next and previous on list of open files */
-};
-
-/*
-** 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 complete database is an instance of the following structure.
-*/
-struct Dbbe {
- char *zDir; /* The directory containing the database */
- int write; /* True for write permission */
- BeFile *pOpen; /* List of open files */
- 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 */
-};
-
-/*
-** 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 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
-** 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 key; /* Most recently used key */
- datum data; /* Most recent data */
- int needRewind; /* Next key should be the first */
- int readPending; /* The fetch hasn't actually been done yet */
-};
-
-/*
-** Initialize the RC4 PRNG. "seed" is a pointer to some random
-** data used to initialize the PRNG.
-*/
-static void rc4init(struct rc4 *p, char *seed, int seedlen){
- int i;
- char k[256];
- p->j = 0;
- p->i = 0;
- for(i=0; i<256; i++){
- p->s[i] = i;
- k[i] = seed[i%seedlen];
- }
- for(i=0; i<256; i++){
- int t;
- p->j = (p->j + p->s[i] + k[i]) & 0xff;
- t = p->s[p->j];
- p->s[p->j] = p->s[i];
- p->s[i] = t;
- }
-}
-
-/*
-** Get a single 8-bit random value from the RC4 PRNG.
-*/
-static int rc4byte(struct rc4 *p){
- int t;
- p->i = (p->i + 1) & 0xff;
- p->j = (p->j + p->s[p->i]) & 0xff;
- t = p->s[p->i];
- p->s[p->i] = p->s[p->j];
- p->s[p->j] = t;
- t = p->s[p->i] + p->s[p->j];
- return t & 0xff;
-}
-
-/*
-** The "mkdir()" function only takes one argument under Windows.
-*/
-#if OS_WIN
-# define mkdir(A,B) mkdir(A)
-#endif
-
-/*
-** 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.
+** This routine opens a new database. It looks at the first
+** few characters of the database name to try to determine what
+** kind of database to open. If the first characters are "gdbm:",
+** then it uses the GDBM driver. If the first few characters are
+** "memory:" then it uses the in-memory driver. If there is no
+** match, the default to the GDBM driver.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
@@ -159,582 +52,15 @@ Dbbe *sqliteDbbeOpen(
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
- Dbbe *pNew;
- struct stat statbuf;
- char *zMaster;
-
- if( !writeFlag ) createFlag = 0;
- if( stat(zName, &statbuf)!=0 ){
- if( createFlag ) mkdir(zName, 0750);
- if( stat(zName, &statbuf)!=0 ){
- sqliteSetString(pzErrMsg, createFlag ?
- "can't find or create directory \"" : "can't find directory \"",
- zName, "\"", 0);
- return 0;
- }
+ extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
+ if( strncmp(zName, "gdbm:", 5)==0 ){
+ return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
}
- if( !S_ISDIR(statbuf.st_mode) ){
- sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
- return 0;
+#if 0
+ if( strncmp(zName, "memory:", 7)==0 ){
+ extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
+ return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
}
- if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
- sqliteSetString(pzErrMsg, "access permission denied", 0);
- return 0;
- }
- zMaster = 0;
- sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
- if( stat(zMaster, &statbuf)==0
- && access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
- sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
- sqliteFree(zMaster);
- return 0;
- }
- sqliteFree(zMaster);
- pNew = sqliteMalloc(sizeof(Dbbe) + strlen(zName) + 1);
- if( pNew==0 ){
- sqliteSetString(pzErrMsg, "out of memory", 0);
- return 0;
- }
- pNew->zDir = (char*)&pNew[1];
- strcpy(pNew->zDir, zName);
- pNew->write = writeFlag;
- pNew->pOpen = 0;
- time(&statbuf.st_ctime);
- rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
- return pNew;
-}
-
-/*
-** Completely shutdown the given database. Close all files. Free all memory.
-*/
-void sqliteDbbeClose(Dbbe *pBe){
- BeFile *pFile, *pNext;
- int i;
- for(pFile=pBe->pOpen; pFile; pFile=pNext){
- pNext = pFile->pNext;
- gdbm_close(pFile->dbf);
- memset(pFile, 0, sizeof(*pFile));
- sqliteFree(pFile);
- }
- for(i=0; inTemp; i++){
- if( pBe->apTemp[i]!=0 ){
- unlink(pBe->azTemp[i]);
- fclose(pBe->apTemp[i]);
- sqliteFree(pBe->azTemp[i]);
- pBe->apTemp[i] = 0;
- pBe->azTemp[i] = 0;
- break;
- }
- }
- sqliteFree(pBe->azTemp);
- sqliteFree(pBe->apTemp);
- memset(pBe, 0, sizeof(*pBe));
- sqliteFree(pBe);
-}
-
-/*
-** Translate the name of an SQL table (or index) into the name
-** of a file that holds the key/data pairs for that table or
-** index. Space to hold the filename is obtained from
-** sqliteMalloc() and must be freed by the calling function.
-*/
-static char *sqliteFileOfTable(Dbbe *pBe, const char *zTable){
- char *zFile = 0;
- int i;
- sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
- if( zFile==0 ) return 0;
- for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
- int c = zFile[i];
- if( isupper(c) ){
- zFile[i] = tolower(c);
- }else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
- zFile[i] = '+';
- }
- }
- return zFile;
-}
-
-/*
-** Generate a random filename with the given prefix. The new filename
-** is written into zBuf[]. The calling function must insure that
-** zBuf[] is big enough to hold the prefix plus 20 or so extra
-** characters.
-**
-** Very random names are chosen so that the chance of a
-** collision with an existing filename is very very small.
-*/
-static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
- int i, j;
- static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
- strcpy(zBuf, zPrefix);
- j = strlen(zBuf);
- for(i=0; i<15; i++){
- int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
- zBuf[j++] = zRandomChars[c];
- }
- zBuf[j] = 0;
-}
-
-/*
-** Open a new table cursor. Write a pointer to the corresponding
-** DbbeCursor structure into *ppCursr. Return an integer success
-** code:
-**
-** SQLITE_OK It worked!
-**
-** SQLITE_NOMEM sqliteMalloc() failed
-**
-** SQLITE_PERM Attempt to access a file for which file
-** access permission is denied
-**
-** SQLITE_BUSY Another thread or process is already using
-** the corresponding file and has that file locked.
-**
-** SQLITE_READONLY The current thread already has this file open
-** readonly but you are trying to open for writing.
-** (This can happen if a SELECT callback tries to
-** do an UPDATE or DELETE.)
-**
-** If zTable is 0 or "", then a temporary database file is created and
-** 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 */
- 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 */
- int rc = SQLITE_OK; /* Return value */
- int rw_mask; /* Permissions mask for opening a table */
- int mode; /* Mode for opening a table */
-
- *ppCursr = 0;
- pCursr = sqliteMalloc( sizeof(*pCursr) );
- if( pCursr==0 ) return SQLITE_NOMEM;
- if( zTable ){
- zFile = sqliteFileOfTable(pBe, zTable);
- for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
- if( strcmp(pFile->zName,zFile)==0 ) break;
- }
- }else{
- pFile = 0;
- zFile = 0;
- }
- if( pFile==0 ){
- if( writeable ){
- rw_mask = GDBM_WRCREAT | GDBM_FAST;
- mode = 0640;
- }else{
- rw_mask = GDBM_READER;
- mode = 0640;
- }
- pFile = sqliteMalloc( sizeof(*pFile) );
- if( pFile==0 ){
- sqliteFree(zFile);
- return SQLITE_NOMEM;
- }
- if( zFile ){
- if( !writeable || pBe->write ){
- pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
- }else{
- pFile->dbf = 0;
- }
- }else{
- int limit;
- struct rc4 *pRc4;
- char zRandom[50];
- pRc4 = &pBe->rc4;
- zFile = 0;
- limit = 5;
- do {
- randomName(&pBe->rc4, zRandom, "_temp_table_");
- sqliteFree(zFile);
- zFile = sqliteFileOfTable(pBe, zRandom);
- pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
- }while( pFile->dbf==0 && limit-- >= 0);
- pFile->delOnClose = 1;
- }
- pFile->writeable = writeable;
- pFile->zName = zFile;
- pFile->nRef = 1;
- pFile->pPrev = 0;
- if( pBe->pOpen ){
- pBe->pOpen->pPrev = pFile;
- }
- pFile->pNext = pBe->pOpen;
- pBe->pOpen = pFile;
- if( pFile->dbf==0 ){
- if( !writeable && access(zFile,0) ){
- /* Trying to read a non-existant file. This is OK. All the
- ** reads will return empty, which is what we want. */
- rc = SQLITE_OK;
- }else if( pBe->write==0 ){
- rc = SQLITE_READONLY;
- }else if( access(zFile,W_OK|R_OK) ){
- rc = SQLITE_PERM;
- }else{
- rc = SQLITE_BUSY;
- }
- }
- }else{
- sqliteFree(zFile);
- pFile->nRef++;
- if( writeable && !pFile->writeable ){
- rc = SQLITE_READONLY;
- }
- }
- pCursr->pBe = pBe;
- pCursr->pFile = pFile;
- pCursr->readPending = 0;
- pCursr->needRewind = 1;
- if( rc!=SQLITE_OK ){
- sqliteDbbeCloseCursor(pCursr);
- *ppCursr = 0;
- }else{
- *ppCursr = pCursr;
- }
- return rc;
-}
-
-/*
-** 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){
- char *zFile; /* Name of the table file */
-
- zFile = sqliteFileOfTable(pBe, zTable);
- unlink(zFile);
- sqliteFree(zFile);
-}
-
-/*
-** Reorganize a table to reduce search times and disk usage.
-*/
-int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){
- DbbeCursor *pCrsr;
- int rc;
-
- rc = sqliteDbbeOpenCursor(pBe, zTable, 1, &pCrsr);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
- gdbm_reorganize(pCrsr->pFile->dbf);
- }
- if( pCrsr ){
- sqliteDbbeCloseCursor(pCrsr);
- }
- 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->dbf!=NULL ){
- gdbm_sync(pFile->dbf);
- }
- if( pFile->nRef<=0 ){
- if( pFile->dbf!=NULL ){
- gdbm_close(pFile->dbf);
- }
- if( pFile->pPrev ){
- pFile->pPrev->pNext = pFile->pNext;
- }else{
- pBe->pOpen = pFile->pNext;
- }
- if( pFile->pNext ){
- pFile->pNext->pPrev = pFile->pPrev;
- }
- if( pFile->delOnClose ){
- unlink(pFile->zName);
- }
- sqliteFree(pFile->zName);
- memset(pFile, 0, sizeof(*pFile));
- sqliteFree(pFile);
- }
- if( pCursr->key.dptr ) free(pCursr->key.dptr);
- if( pCursr->data.dptr ) free(pCursr->data.dptr);
- memset(pCursr, 0, sizeof(*pCursr));
- sqliteFree(pCursr);
-}
-
-/*
-** Clear the given datum
-*/
-static void datumClear(datum *p){
- if( p->dptr ) free(p->dptr);
- p->dptr = 0;
- p->dsize = 0;
-}
-
-/*
-** 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 key;
- key.dsize = nKey;
- key.dptr = pKey;
- datumClear(&pCursr->key);
- datumClear(&pCursr->data);
- if( pCursr->pFile && pCursr->pFile->dbf ){
- pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
- }
- return pCursr->data.dptr!=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){
- datum key;
- int result = 0;
- key.dsize = nKey;
- key.dptr = pKey;
- if( pCursr->pFile && pCursr->pFile->dbf ){
- result = gdbm_exists(pCursr->pFile->dbf, key);
- }
- return result;
-}
-
-/*
-** 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){
- int n;
- if( offset>=pCursr->key.dsize ) return 0;
- if( offset+size>pCursr->key.dsize ){
- n = pCursr->key.dsize - offset;
- }else{
- n = size;
- }
- memcpy(zBuf, &pCursr->key.dptr[offset], n);
- return n;
-}
-int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
- int n;
- if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
- pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
- pCursr->readPending = 0;
- }
- if( offset>=pCursr->data.dsize ) return 0;
- if( offset+size>pCursr->data.dsize ){
- n = pCursr->data.dsize - offset;
- }else{
- n = size;
- }
- memcpy(zBuf, &pCursr->data.dptr[offset], n);
- return n;
-}
-
-/*
-** Return a pointer to bytes from the key or data. The data returned
-** is ephemeral.
-*/
-char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){
- if( offset<0 || offset>=pCursr->key.dsize ) return "";
- return &pCursr->key.dptr[offset];
-}
-char *sqliteDbbeReadData(DbbeCursor *pCursr, int offset){
- if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
- pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
- pCursr->readPending = 0;
- }
- if( offset<0 || offset>=pCursr->data.dsize ) return "";
- return &pCursr->data.dptr[offset];
-}
-
-/*
-** Return the total number of bytes in either data or key.
-*/
-int sqliteDbbeKeyLength(DbbeCursor *pCursr){
- return pCursr->key.dsize;
-}
-int sqliteDbbeDataLength(DbbeCursor *pCursr){
- if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
- pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
- pCursr->readPending = 0;
- }
- return pCursr->data.dsize;
-}
-
-/*
-** Make is so that the next call to sqliteNextKey() finds the first
-** key of the table.
-*/
-int sqliteDbbeRewind(DbbeCursor *pCursr){
- pCursr->needRewind = 1;
- return SQLITE_OK;
-}
-
-/*
-** Read the next key from the table. Return 1 on success. Return
-** 0 if there are no more keys.
-*/
-int sqliteDbbeNextKey(DbbeCursor *pCursr){
- datum nextkey;
- int rc;
- if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
- pCursr->readPending = 0;
- return 0;
- }
- if( pCursr->needRewind ){
- nextkey = gdbm_firstkey(pCursr->pFile->dbf);
- pCursr->needRewind = 0;
- }else{
- nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
- }
- datumClear(&pCursr->key);
- datumClear(&pCursr->data);
- pCursr->key = nextkey;
- if( pCursr->key.dptr ){
- pCursr->readPending = 1;
- rc = 1;
- }else{
- pCursr->needRewind = 1;
- pCursr->readPending = 0;
- rc = 0;
- }
- return rc;
-}
-
-/*
-** Get a new integer key.
-*/
-int sqliteDbbeNew(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;
- for(i=0; i<4; i++){
- iKey = (iKey<<8) + rc4byte(pRc4);
- }
- if( iKey==0 ) continue;
- key.dptr = (char*)&iKey;
- key.dsize = 4;
- go = gdbm_exists(pCursr->pFile->dbf, key);
- }
- return iKey;
-}
-
-/*
-** Write an entry into the table. Overwrite any prior entry with the
-** same key.
-*/
-int sqliteDbbePut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
- datum data, key;
- int rc;
- if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
- data.dsize = nData;
- data.dptr = pData;
- key.dsize = nKey;
- key.dptr = pKey;
- rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
- if( rc ) rc = SQLITE_ERROR;
- datumClear(&pCursr->key);
- datumClear(&pCursr->data);
- return rc;
-}
-
-/*
-** Remove an entry from a table, if the entry exists.
-*/
-int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){
- datum key;
- int rc;
- datumClear(&pCursr->key);
- datumClear(&pCursr->data);
- if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
- key.dsize = nKey;
- key.dptr = pKey;
- rc = gdbm_delete(pCursr->pFile->dbf, key);
- if( rc ) rc = SQLITE_ERROR;
- return rc;
-}
-
-/*
-** Open a temporary file. The file should be deleted when closed.
-**
-** Note that we can't use the old Unix trick of opening the file
-** 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 */
- 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 */
-
- for(i=0; inTemp; i++){
- if( pBe->apTemp[i]==0 ) break;
- }
- if( i>=pBe->nTemp ){
- pBe->nTemp++;
- pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
- pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
- }
- if( pBe->apTemp==0 ){
- *ppFile = 0;
- return SQLITE_NOMEM;
- }
- limit = 4;
- zFile = 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+");
- if( pBe->apTemp[i]==0 ){
- rc = SQLITE_ERROR;
- sqliteFree(zFile);
- pBe->azTemp[i] = 0;
- }else{
- pBe->azTemp[i] = zFile;
- }
- return rc;
-}
-
-/*
-** Close a temporary file opened using sqliteDbbeOpenTempFile()
-*/
-void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){
- int i;
- for(i=0; inTemp; i++){
- if( pBe->apTemp[i]==f ){
- unlink(pBe->azTemp[i]);
- sqliteFree(pBe->azTemp[i]);
- pBe->apTemp[i] = 0;
- pBe->azTemp[i] = 0;
- break;
- }
- }
- fclose(f);
+#endif
+ return sqliteGdbmOpen(zName, writeFlag, createFlag, pzErrMsg);
}
diff --git a/src/dbbe.h b/src/dbbe.h
index 329cef3c5c..7c54a77b64 100644
--- a/src/dbbe.h
+++ b/src/dbbe.h
@@ -28,7 +28,7 @@
** This library was originally designed to support the following
** backends: GDBM, NDBM, SDBM, Berkeley DB.
**
-** $Id: dbbe.h,v 1.7 2000/08/17 09:50:00 drh Exp $
+** $Id: dbbe.h,v 1.8 2000/10/19 01:49:02 drh Exp $
*/
#ifndef _SQLITE_DBBE_H_
#define _SQLITE_DBBE_H_
@@ -54,90 +54,99 @@
typedef struct Dbbe Dbbe;
typedef struct DbbeCursor DbbeCursor;
-/*
-** The 18 interface routines.
-*/
-/* Open a complete database */
+/*
+** Open a complete database.
+**
+** If the database name begins with "gdbm:" the GDBM driver is used.
+** If the name begins with "memory:" the in-memory driver is used.
+** The default driver is GDBM.
+*/
Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
-/* Close the whole database. */
-void sqliteDbbeClose(Dbbe*);
-
-/* Open a cursor into particular file of a previously opened database.
-** Create the file if it doesn't already exist and writeable!=0. zName
-** is the base name of the file to be opened. This routine will add
-** an appropriate path and extension to the filename to locate the
-** actual file.
-**
-** If zName is 0 or "", then a temporary file is created that
-** will be deleted when closed.
+/*
+** This is the structure returned by sqliteDbbeOpen(). It contains pointers
+** to all access routines for the database backend.
*/
-int sqliteDbbeOpenCursor(Dbbe*, const char *zName, int writeable, DbbeCursor**);
+struct Dbbe {
+ /* Close the whole database. */
+ void (*Close)(Dbbe*);
-/* Delete a table from the database */
-void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
+ /* Open a cursor into particular file of a previously opened database.
+ ** Create the file if it doesn't already exist and writeable!=0. zName
+ ** is the base name of the file to be opened. This routine will add
+ ** an appropriate path and extension to the filename to locate the
+ ** actual file.
+ **
+ ** If zName is 0 or "", then a temporary file is created that
+ ** will be deleted when closed.
+ */
+ int (*OpenCursor)(Dbbe*, const char *zName, int writeable, DbbeCursor**);
-/* Reorganize a table to speed access or reduce its disk usage */
-int sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
+ /* Delete a table from the database */
+ void (*DropTable)(Dbbe*, const char *zTableName);
-/* Close a cursor */
-void sqliteDbbeCloseCursor(DbbeCursor*);
+ /* Reorganize a table to speed access or reduce its disk usage */
+ int (*ReorganizeTable)(Dbbe*, const char *zTableName);
-/* Fetch an entry from a table with the given key. Return 1 if
-** successful and 0 if no such entry exists.
-*/
-int sqliteDbbeFetch(DbbeCursor*, int nKey, char *pKey);
+ /* Close a cursor */
+ void (*CloseCursor)(DbbeCursor*);
-/* Return 1 if the given key is already in the table. Return 0
-** if it is not.
-*/
-int sqliteDbbeTest(DbbeCursor*, int nKey, char *pKey);
+ /* Fetch an entry from a table with the given key. Return 1 if
+ ** successful and 0 if no such entry exists.
+ */
+ int (*Fetch)(DbbeCursor*, int nKey, char *pKey);
-/* Retrieve the key or data used for the last fetch. Only size
-** bytes are read beginning with the offset-th byte. The return
-** value is the actual number of bytes read.
-*/
-int sqliteDbbeCopyKey(DbbeCursor*, int offset, int size, char *zBuf);
-int sqliteDbbeCopyData(DbbeCursor*, int offset, int size, char *zBuf);
+ /* Return 1 if the given key is already in the table. Return 0
+ ** if it is not.
+ */
+ int (*Test)(DbbeCursor*, int nKey, char *pKey);
-/* Retrieve the key or data. The result is ephemeral. In other words,
-** the result is stored in a buffer that might be overwritten on the next
-** call to any DBBE routine. If the results are needed for longer than
-** that, you must make a copy.
-*/
-char *sqliteDbbeReadKey(DbbeCursor*, int offset);
-char *sqliteDbbeReadData(DbbeCursor*, int offset);
+ /* Retrieve the key or data used for the last fetch. Only size
+ ** bytes are read beginning with the offset-th byte. The return
+ ** value is the actual number of bytes read.
+ */
+ int (*CopyKey)(DbbeCursor*, int offset, int size, char *zBuf);
+ int (*CopyData)(DbbeCursor*, int offset, int size, char *zBuf);
-/* Return the length of the most recently fetched key or data. */
-int sqliteDbbeKeyLength(DbbeCursor*);
-int sqliteDbbeDataLength(DbbeCursor*);
+ /* Retrieve the key or data. The result is ephemeral. In other words,
+ ** the result is stored in a buffer that might be overwritten on the next
+ ** call to any DBBE routine. If the results are needed for longer than
+ ** that, you must make a copy.
+ */
+ char *(*ReadKey)(DbbeCursor*, int offset);
+ char *(*ReadData)(DbbeCursor*, int offset);
-/* Retrieve the next entry in the table. The first key is retrieved
-** the first time this routine is called, or after a call to
-** sqliteDbbeRewind(). The return value is 1 if there is another
-** entry, or 0 if there are no more entries. */
-int sqliteDbbeNextKey(DbbeCursor*);
+ /* Return the length of the most recently fetched key or data. */
+ int (*KeyLength)(DbbeCursor*);
+ int (*DataLength)(DbbeCursor*);
-/* Make it so that the next call to sqliteDbbeNextKey() returns
-** the first entry of the table. */
-int sqliteDbbeRewind(DbbeCursor*);
+ /* Retrieve the next entry in the table. The first key is retrieved
+ ** the first time this routine is called, or after a call to
+ ** Dbbe.Rewind(). The return value is 1 if there is another
+ ** entry, or 0 if there are no more entries. */
+ int (*NextKey)(DbbeCursor*);
-/* Get a new integer key for this table. */
-int sqliteDbbeNew(DbbeCursor*);
+ /* Make it so that the next call to Dbbe.NextKey() returns
+ ** the first entry of the table. */
+ int (*Rewind)(DbbeCursor*);
-/* Write an entry into a table. If another entry already exists with
-** the same key, the old entry is discarded first.
-*/
-int sqliteDbbePut(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
+ /* Get a new integer key for this table. */
+ int (*New)(DbbeCursor*);
-/* Remove an entry from the table */
-int sqliteDbbeDelete(DbbeCursor*, int nKey, char *pKey);
+ /* Write an entry into a table. If another entry already exists with
+ ** the same key, the old entry is discarded first.
+ */
+ int (*Put)(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
-/* Open a file suitable for temporary storage */
-int sqliteDbbeOpenTempFile(Dbbe*, FILE**);
+ /* Remove an entry from the table */
+ int (*Delete)(DbbeCursor*, int nKey, char *pKey);
-/* Close a temporary file */
-void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
+ /* Open a file suitable for temporary storage */
+ int (*OpenTempFile)(Dbbe*, FILE**);
+
+ /* Close a temporary file */
+ void (*CloseTempFile)(Dbbe *, FILE *);
+};
#endif /* defined(_SQLITE_DBBE_H_) */
diff --git a/src/dbbegdbm.c b/src/dbbegdbm.c
new file mode 100644
index 0000000000..1e2702028f
--- /dev/null
+++ b/src/dbbegdbm.c
@@ -0,0 +1,776 @@
+/*
+** 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
+** License as published by the Free Software Foundation; either
+** version 2 of the License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+** General Public License for more details.
+**
+** You should have received a copy of the GNU General Public
+** License along with this library; if not, write to the
+** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*************************************************************************
+** This file contains code to implement the database backend (DBBE)
+** for sqlite. The database backend is the interface between
+** sqlite and the code that does the actually reading and writing
+** of information to the disk.
+**
+** This file uses GDBM as the database backend. It should be
+** relatively simple to convert to a different database such
+** as NDBM, SDBM, or BerkeleyDB.
+**
+** $Id: dbbegdbm.c,v 1.1 2000/10/19 01:49:02 drh Exp $
+*/
+#include "sqliteInt.h"
+#include
+#include
+#include
+#include
+#include
+
+/*
+** Information about each open disk file is an instance of this
+** structure. There will only be one such structure for each
+** disk file. If the VDBE opens the same file 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 file */
+ GDBM_FILE dbf; /* The file itself */
+ int nRef; /* Number of references */
+ int delOnClose; /* Delete when closing */
+ int writeable; /* Opened for writing */
+ BeFile *pNext, *pPrev; /* Next and previous on list of open files */
+};
+
+/*
+** 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.
+*/
+typedef struct Dbbex Dbbex;
+struct Dbbex {
+ Dbbe dbbe; /* The base class */
+ char *zDir; /* The directory containing the database */
+ int write; /* True for write permission */
+ BeFile *pOpen; /* List of open files */
+ 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 */
+};
+
+/*
+** 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 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
+** associated with the same disk file.
+*/
+struct DbbeCursor {
+ Dbbex *pBe; /* The database of which this record is a part */
+ BeFile *pFile; /* The database file for this table */
+ datum key; /* Most recently used key */
+ datum data; /* Most recent data */
+ int needRewind; /* Next key should be the first */
+ int readPending; /* The fetch hasn't actually been done yet */
+};
+
+/*
+** Initialize the RC4 PRNG. "seed" is a pointer to some random
+** data used to initialize the PRNG.
+*/
+static void rc4init(struct rc4 *p, char *seed, int seedlen){
+ int i;
+ char k[256];
+ p->j = 0;
+ p->i = 0;
+ for(i=0; i<256; i++){
+ p->s[i] = i;
+ k[i] = seed[i%seedlen];
+ }
+ for(i=0; i<256; i++){
+ int t;
+ p->j = (p->j + p->s[i] + k[i]) & 0xff;
+ t = p->s[p->j];
+ p->s[p->j] = p->s[i];
+ p->s[i] = t;
+ }
+}
+
+/*
+** Get a single 8-bit random value from the RC4 PRNG.
+*/
+static int rc4byte(struct rc4 *p){
+ int t;
+ p->i = (p->i + 1) & 0xff;
+ p->j = (p->j + p->s[p->i]) & 0xff;
+ t = p->s[p->i];
+ p->s[p->i] = p->s[p->j];
+ p->s[p->j] = t;
+ t = p->s[p->i] + p->s[p->j];
+ return t & 0xff;
+}
+
+/*
+** The "mkdir()" function only takes one argument under Windows.
+*/
+#if OS_WIN
+# define mkdir(A,B) mkdir(A)
+#endif
+
+/*
+** Forward declaration
+*/
+static void sqliteGdbmCloseCursor(DbbeCursor *pCursr);
+
+/*
+** Completely shutdown the given database. Close all files. Free all memory.
+*/
+static void sqliteGdbmClose(Dbbe *pDbbe){
+ Dbbex *pBe = (Dbbex*)pDbbe;
+ BeFile *pFile, *pNext;
+ int i;
+ for(pFile=pBe->pOpen; pFile; pFile=pNext){
+ pNext = pFile->pNext;
+ gdbm_close(pFile->dbf);
+ memset(pFile, 0, sizeof(*pFile));
+ sqliteFree(pFile);
+ }
+ for(i=0; inTemp; i++){
+ if( pBe->apTemp[i]!=0 ){
+ unlink(pBe->azTemp[i]);
+ fclose(pBe->apTemp[i]);
+ sqliteFree(pBe->azTemp[i]);
+ pBe->apTemp[i] = 0;
+ pBe->azTemp[i] = 0;
+ break;
+ }
+ }
+ sqliteFree(pBe->azTemp);
+ sqliteFree(pBe->apTemp);
+ memset(pBe, 0, sizeof(*pBe));
+ sqliteFree(pBe);
+}
+
+/*
+** Translate the name of an SQL table (or index) into the name
+** of a file that holds the key/data pairs for that table or
+** index. Space to hold the filename is obtained from
+** sqliteMalloc() and must be freed by the calling function.
+*/
+static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){
+ char *zFile = 0;
+ int i;
+ sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
+ if( zFile==0 ) return 0;
+ for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
+ int c = zFile[i];
+ if( isupper(c) ){
+ zFile[i] = tolower(c);
+ }else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
+ zFile[i] = '+';
+ }
+ }
+ return zFile;
+}
+
+/*
+** Generate a random filename with the given prefix. The new filename
+** is written into zBuf[]. The calling function must insure that
+** zBuf[] is big enough to hold the prefix plus 20 or so extra
+** characters.
+**
+** Very random names are chosen so that the chance of a
+** collision with an existing filename is very very small.
+*/
+static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
+ int i, j;
+ static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ strcpy(zBuf, zPrefix);
+ j = strlen(zBuf);
+ for(i=0; i<15; i++){
+ int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
+ zBuf[j++] = zRandomChars[c];
+ }
+ zBuf[j] = 0;
+}
+
+/*
+** Open a new table cursor. Write a pointer to the corresponding
+** DbbeCursor structure into *ppCursr. Return an integer success
+** code:
+**
+** SQLITE_OK It worked!
+**
+** SQLITE_NOMEM sqliteMalloc() failed
+**
+** SQLITE_PERM Attempt to access a file for which file
+** access permission is denied
+**
+** SQLITE_BUSY Another thread or process is already using
+** the corresponding file and has that file locked.
+**
+** SQLITE_READONLY The current thread already has this file open
+** readonly but you are trying to open for writing.
+** (This can happen if a SELECT callback tries to
+** do an UPDATE or DELETE.)
+**
+** If zTable is 0 or "", then a temporary database file is created and
+** a cursor to that temporary file is opened. The temporary file
+** will be deleted from the disk when it is closed.
+*/
+static int sqliteGdbmOpenCursor(
+ 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 */
+ 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 = sqliteFileOfTable(pBe, zTable);
+ for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
+ if( strcmp(pFile->zName,zFile)==0 ) break;
+ }
+ }else{
+ pFile = 0;
+ zFile = 0;
+ }
+ if( pFile==0 ){
+ if( writeable ){
+ rw_mask = GDBM_WRCREAT | GDBM_FAST;
+ mode = 0640;
+ }else{
+ rw_mask = GDBM_READER;
+ mode = 0640;
+ }
+ pFile = sqliteMalloc( sizeof(*pFile) );
+ if( pFile==0 ){
+ sqliteFree(zFile);
+ return SQLITE_NOMEM;
+ }
+ if( zFile ){
+ if( !writeable || pBe->write ){
+ pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
+ }else{
+ pFile->dbf = 0;
+ }
+ }else{
+ int limit;
+ struct rc4 *pRc4;
+ char zRandom[50];
+ pRc4 = &pBe->rc4;
+ zFile = 0;
+ limit = 5;
+ do {
+ randomName(&pBe->rc4, zRandom, "_temp_table_");
+ sqliteFree(zFile);
+ zFile = sqliteFileOfTable(pBe, zRandom);
+ pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
+ }while( pFile->dbf==0 && limit-- >= 0);
+ pFile->delOnClose = 1;
+ }
+ pFile->writeable = writeable;
+ pFile->zName = zFile;
+ pFile->nRef = 1;
+ pFile->pPrev = 0;
+ if( pBe->pOpen ){
+ pBe->pOpen->pPrev = pFile;
+ }
+ pFile->pNext = pBe->pOpen;
+ pBe->pOpen = pFile;
+ if( pFile->dbf==0 ){
+ if( !writeable && access(zFile,0) ){
+ /* Trying to read a non-existant file. This is OK. All the
+ ** reads will return empty, which is what we want. */
+ rc = SQLITE_OK;
+ }else if( pBe->write==0 ){
+ rc = SQLITE_READONLY;
+ }else if( access(zFile,W_OK|R_OK) ){
+ rc = SQLITE_PERM;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+ }
+ }else{
+ sqliteFree(zFile);
+ pFile->nRef++;
+ if( writeable && !pFile->writeable ){
+ rc = SQLITE_READONLY;
+ }
+ }
+ pCursr->pBe = pBe;
+ pCursr->pFile = pFile;
+ pCursr->readPending = 0;
+ pCursr->needRewind = 1;
+ if( rc!=SQLITE_OK ){
+ sqliteGdbmCloseCursor(pCursr);
+ *ppCursr = 0;
+ }else{
+ *ppCursr = pCursr;
+ }
+ return rc;
+}
+
+/*
+** Drop a table from the database. The file on the disk that corresponds
+** to this table is deleted.
+*/
+static void sqliteGdbmDropTable(Dbbe *pBe, const char *zTable){
+ char *zFile; /* Name of the table file */
+
+ zFile = sqliteFileOfTable((Dbbex*)pBe, zTable);
+ unlink(zFile);
+ sqliteFree(zFile);
+}
+
+/*
+** Close a cursor previously opened by sqliteGdbmOpenCursor().
+**
+** 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.
+*/
+static void sqliteGdbmCloseCursor(DbbeCursor *pCursr){
+ BeFile *pFile;
+ Dbbex *pBe;
+ if( pCursr==0 ) return;
+ pFile = pCursr->pFile;
+ pBe = pCursr->pBe;
+ pFile->nRef--;
+ if( pFile->dbf!=NULL ){
+ gdbm_sync(pFile->dbf);
+ }
+ if( pFile->nRef<=0 ){
+ if( pFile->dbf!=NULL ){
+ gdbm_close(pFile->dbf);
+ }
+ if( pFile->pPrev ){
+ pFile->pPrev->pNext = pFile->pNext;
+ }else{
+ pBe->pOpen = pFile->pNext;
+ }
+ if( pFile->pNext ){
+ pFile->pNext->pPrev = pFile->pPrev;
+ }
+ if( pFile->delOnClose ){
+ unlink(pFile->zName);
+ }
+ sqliteFree(pFile->zName);
+ memset(pFile, 0, sizeof(*pFile));
+ sqliteFree(pFile);
+ }
+ if( pCursr->key.dptr ) free(pCursr->key.dptr);
+ if( pCursr->data.dptr ) free(pCursr->data.dptr);
+ memset(pCursr, 0, sizeof(*pCursr));
+ sqliteFree(pCursr);
+}
+
+/*
+** Reorganize a table to reduce search times and disk usage.
+*/
+static int sqliteGdbmReorganizeTable(Dbbe *pBe, const char *zTable){
+ DbbeCursor *pCrsr;
+ int rc;
+
+ rc = sqliteGdbmOpenCursor(pBe, zTable, 1, &pCrsr);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
+ gdbm_reorganize(pCrsr->pFile->dbf);
+ }
+ if( pCrsr ){
+ sqliteGdbmCloseCursor(pCrsr);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Clear the given datum
+*/
+static void datumClear(datum *p){
+ if( p->dptr ) free(p->dptr);
+ p->dptr = 0;
+ p->dsize = 0;
+}
+
+/*
+** Fetch a single record from an open cursor. Return 1 on success
+** and 0 on failure.
+*/
+static int sqliteGdbmFetch(DbbeCursor *pCursr, int nKey, char *pKey){
+ datum key;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ datumClear(&pCursr->key);
+ datumClear(&pCursr->data);
+ if( pCursr->pFile && pCursr->pFile->dbf ){
+ pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
+ }
+ return pCursr->data.dptr!=0;
+}
+
+/*
+** Return 1 if the given key is already in the table. Return 0
+** if it is not.
+*/
+static int sqliteGdbmTest(DbbeCursor *pCursr, int nKey, char *pKey){
+ datum key;
+ int result = 0;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ if( pCursr->pFile && pCursr->pFile->dbf ){
+ result = gdbm_exists(pCursr->pFile->dbf, key);
+ }
+ return result;
+}
+
+/*
+** Copy bytes from the current key or data into a buffer supplied by
+** the calling function. Return the number of bytes copied.
+*/
+static
+int sqliteGdbmCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
+ int n;
+ if( offset>=pCursr->key.dsize ) return 0;
+ if( offset+size>pCursr->key.dsize ){
+ n = pCursr->key.dsize - offset;
+ }else{
+ n = size;
+ }
+ memcpy(zBuf, &pCursr->key.dptr[offset], n);
+ return n;
+}
+static
+int sqliteGdbmCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
+ int n;
+ if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
+ pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
+ pCursr->readPending = 0;
+ }
+ if( offset>=pCursr->data.dsize ) return 0;
+ if( offset+size>pCursr->data.dsize ){
+ n = pCursr->data.dsize - offset;
+ }else{
+ n = size;
+ }
+ memcpy(zBuf, &pCursr->data.dptr[offset], n);
+ return n;
+}
+
+/*
+** Return a pointer to bytes from the key or data. The data returned
+** is ephemeral.
+*/
+static char *sqliteGdbmReadKey(DbbeCursor *pCursr, int offset){
+ if( offset<0 || offset>=pCursr->key.dsize ) return "";
+ return &pCursr->key.dptr[offset];
+}
+static char *sqliteGdbmReadData(DbbeCursor *pCursr, int offset){
+ if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
+ pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
+ pCursr->readPending = 0;
+ }
+ if( offset<0 || offset>=pCursr->data.dsize ) return "";
+ return &pCursr->data.dptr[offset];
+}
+
+/*
+** Return the total number of bytes in either data or key.
+*/
+static int sqliteGdbmKeyLength(DbbeCursor *pCursr){
+ return pCursr->key.dsize;
+}
+static int sqliteGdbmDataLength(DbbeCursor *pCursr){
+ if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
+ pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
+ pCursr->readPending = 0;
+ }
+ return pCursr->data.dsize;
+}
+
+/*
+** Make is so that the next call to sqliteNextKey() finds the first
+** key of the table.
+*/
+static int sqliteGdbmRewind(DbbeCursor *pCursr){
+ pCursr->needRewind = 1;
+ return SQLITE_OK;
+}
+
+/*
+** Read the next key from the table. Return 1 on success. Return
+** 0 if there are no more keys.
+*/
+static int sqliteGdbmNextKey(DbbeCursor *pCursr){
+ datum nextkey;
+ int rc;
+ if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
+ pCursr->readPending = 0;
+ return 0;
+ }
+ if( pCursr->needRewind ){
+ nextkey = gdbm_firstkey(pCursr->pFile->dbf);
+ pCursr->needRewind = 0;
+ }else{
+ nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
+ }
+ datumClear(&pCursr->key);
+ datumClear(&pCursr->data);
+ pCursr->key = nextkey;
+ if( pCursr->key.dptr ){
+ pCursr->readPending = 1;
+ rc = 1;
+ }else{
+ pCursr->needRewind = 1;
+ pCursr->readPending = 0;
+ rc = 0;
+ }
+ return rc;
+}
+
+/*
+** Get a new integer key.
+*/
+static int sqliteGdbmNew(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;
+ for(i=0; i<4; i++){
+ iKey = (iKey<<8) + rc4byte(pRc4);
+ }
+ if( iKey==0 ) continue;
+ key.dptr = (char*)&iKey;
+ key.dsize = 4;
+ go = gdbm_exists(pCursr->pFile->dbf, key);
+ }
+ return iKey;
+}
+
+/*
+** Write an entry into the table. Overwrite any prior entry with the
+** same key.
+*/
+static int
+sqliteGdbmPut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
+ datum data, key;
+ int rc;
+ if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
+ data.dsize = nData;
+ data.dptr = pData;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
+ if( rc ) rc = SQLITE_ERROR;
+ datumClear(&pCursr->key);
+ datumClear(&pCursr->data);
+ return rc;
+}
+
+/*
+** Remove an entry from a table, if the entry exists.
+*/
+static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
+ datum key;
+ int rc;
+ datumClear(&pCursr->key);
+ datumClear(&pCursr->data);
+ if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
+ key.dsize = nKey;
+ key.dptr = pKey;
+ rc = gdbm_delete(pCursr->pFile->dbf, key);
+ if( rc ) rc = SQLITE_ERROR;
+ return rc;
+}
+
+/*
+** Open a temporary file. The file should be deleted when closed.
+**
+** Note that we can't use the old Unix trick of opening the file
+** and then immediately unlinking the file. That works great
+** under Unix, but fails when we try to port to Windows.
+*/
+static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
+ char *zFile; /* 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;
+ }
+ if( i>=pBe->nTemp ){
+ pBe->nTemp++;
+ pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
+ pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
+ }
+ if( pBe->apTemp==0 ){
+ *ppFile = 0;
+ return SQLITE_NOMEM;
+ }
+ limit = 4;
+ zFile = 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+");
+ if( pBe->apTemp[i]==0 ){
+ rc = SQLITE_ERROR;
+ sqliteFree(zFile);
+ pBe->azTemp[i] = 0;
+ }else{
+ pBe->azTemp[i] = zFile;
+ }
+ return rc;
+}
+
+/*
+** Close a temporary file opened using sqliteGdbmOpenTempFile()
+*/
+static void sqliteGdbmCloseTempFile(Dbbe *pDbbe, FILE *f){
+ int i;
+ Dbbex *pBe = (Dbbex*)pDbbe;
+ for(i=0; inTemp; i++){
+ if( pBe->apTemp[i]==f ){
+ unlink(pBe->azTemp[i]);
+ sqliteFree(pBe->azTemp[i]);
+ pBe->apTemp[i] = 0;
+ pBe->azTemp[i] = 0;
+ break;
+ }
+ }
+ 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 *sqliteGdbmOpen(
+ 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;
+ struct stat statbuf;
+ char *zMaster;
+
+ if( !writeFlag ) createFlag = 0;
+ if( stat(zName, &statbuf)!=0 ){
+ if( createFlag ) mkdir(zName, 0750);
+ if( stat(zName, &statbuf)!=0 ){
+ sqliteSetString(pzErrMsg, createFlag ?
+ "can't find or create directory \"" : "can't find directory \"",
+ zName, "\"", 0);
+ return 0;
+ }
+ }
+ if( !S_ISDIR(statbuf.st_mode) ){
+ sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
+ return 0;
+ }
+ if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
+ sqliteSetString(pzErrMsg, "access permission denied", 0);
+ return 0;
+ }
+ zMaster = 0;
+ sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
+ if( stat(zMaster, &statbuf)==0
+ && access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
+ sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
+ sqliteFree(zMaster);
+ return 0;
+ }
+ sqliteFree(zMaster);
+ pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1);
+ if( pNew==0 ){
+ sqliteSetString(pzErrMsg, "out of memory", 0);
+ return 0;
+ }
+ pNew->dbbe.Close = sqliteGdbmClose;
+ pNew->dbbe.OpenCursor = sqliteGdbmOpenCursor;
+ pNew->dbbe.DropTable = sqliteGdbmDropTable;
+ pNew->dbbe.ReorganizeTable = sqliteGdbmReorganizeTable;
+ pNew->dbbe.CloseCursor = sqliteGdbmCloseCursor;
+ pNew->dbbe.Fetch = sqliteGdbmFetch;
+ pNew->dbbe.Test = sqliteGdbmTest;
+ pNew->dbbe.CopyKey = sqliteGdbmCopyKey;
+ pNew->dbbe.CopyData = sqliteGdbmCopyData;
+ pNew->dbbe.ReadKey = sqliteGdbmReadKey;
+ pNew->dbbe.ReadData = sqliteGdbmReadData;
+ pNew->dbbe.KeyLength = sqliteGdbmKeyLength;
+ pNew->dbbe.DataLength = sqliteGdbmDataLength;
+ pNew->dbbe.NextKey = sqliteGdbmNextKey;
+ pNew->dbbe.Rewind = sqliteGdbmRewind;
+ pNew->dbbe.New = sqliteGdbmNew;
+ pNew->dbbe.Put = sqliteGdbmPut;
+ pNew->dbbe.Delete = sqliteGdbmDelete;
+ pNew->dbbe.OpenTempFile = sqliteGdbmOpenTempFile;
+ pNew->dbbe.CloseTempFile = sqliteGdbmCloseTempFile;
+ pNew->zDir = (char*)&pNew[1];
+ strcpy(pNew->zDir, zName);
+ pNew->write = writeFlag;
+ pNew->pOpen = 0;
+ time(&statbuf.st_ctime);
+ rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
+ return &pNew->dbbe;
+}
diff --git a/src/main.c b/src/main.c
index b953dae0f7..ca43c35fce 100644
--- a/src/main.c
+++ b/src/main.c
@@ -26,7 +26,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.20 2000/10/16 22:06:42 drh Exp $
+** $Id: main.c,v 1.21 2000/10/19 01:49:02 drh Exp $
*/
#include "sqliteInt.h"
@@ -239,7 +239,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
*/
void sqlite_close(sqlite *db){
int i;
- sqliteDbbeClose(db->pBe);
+ db->pBe->Close(db->pBe);
for(i=0; iapTblHash[i];
db->apTblHash[i] = 0;
diff --git a/src/vdbe.c b/src/vdbe.c
index c7b9cb9d46..da27f4b47f 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -41,7 +41,7 @@
** But other routines are also provided to help in building up
** a program instruction by instruction.
**
-** $Id: vdbe.c,v 1.43 2000/10/16 22:06:43 drh Exp $
+** $Id: vdbe.c,v 1.44 2000/10/19 01:49:03 drh Exp $
*/
#include "sqliteInt.h"
#include
@@ -679,7 +679,7 @@ static void Cleanup(Vdbe *p){
p->azColName = 0;
for(i=0; inCursor; i++){
if( p->aCsr[i].pCursor ){
- sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
+ p->pBe->CloseCursor(p->aCsr[i].pCursor);
p->aCsr[i].pCursor = 0;
}
}
@@ -696,7 +696,7 @@ static void Cleanup(Vdbe *p){
p->nMem = 0;
for(i=0; inList; i++){
if( p->apList[i] ){
- sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ p->pBe->CloseTempFile(p->pBe, p->apList[i]);
p->apList[i] = 0;
}
}
@@ -924,6 +924,7 @@ int sqliteVdbeExec(
int pc; /* The program counter */
Op *pOp; /* Current operation */
int rc; /* Value to return */
+ Dbbe *pBe = p->pBe; /* The backend driver */
char zBuf[100]; /* Space to sprintf() and integer */
p->tos = -1;
@@ -1762,10 +1763,10 @@ int sqliteVdbeExec(
for(j=p->nCursor; j<=i; j++) p->aCsr[j].pCursor = 0;
p->nCursor = i+1;
}else if( p->aCsr[i].pCursor ){
- sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
+ pBe->CloseCursor(p->aCsr[i].pCursor);
}
do {
- rc = sqliteDbbeOpenCursor(p->pBe,pOp->p3,pOp->p2,&p->aCsr[i].pCursor);
+ rc = pBe->OpenCursor(pBe,pOp->p3,pOp->p2,&p->aCsr[i].pCursor);
switch( rc ){
case SQLITE_BUSY: {
if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){
@@ -1806,7 +1807,7 @@ int sqliteVdbeExec(
case OP_Close: {
int i = pOp->p1;
if( i>=0 && inCursor && p->aCsr[i].pCursor ){
- sqliteDbbeCloseCursor(p->aCsr[i].pCursor);
+ pBe->CloseCursor(p->aCsr[i].pCursor);
p->aCsr[i].pCursor = 0;
}
break;
@@ -1824,11 +1825,11 @@ int sqliteVdbeExec(
if( tos<0 ) goto not_enough_stack;
if( i>=0 && inCursor && p->aCsr[i].pCursor ){
if( p->aStack[tos].flags & STK_Int ){
- sqliteDbbeFetch(p->aCsr[i].pCursor, sizeof(int),
+ pBe->Fetch(p->aCsr[i].pCursor, sizeof(int),
(char*)&p->aStack[tos].i);
}else{
if( Stringify(p, tos) ) goto no_mem;
- sqliteDbbeFetch(p->aCsr[i].pCursor, p->aStack[tos].n,
+ pBe->Fetch(p->aCsr[i].pCursor, p->aStack[tos].n,
p->zStack[tos]);
}
p->nFetch++;
@@ -1890,11 +1891,11 @@ int sqliteVdbeExec(
if( tos<0 ) goto not_enough_stack;
if( i>=0 && inCursor && p->aCsr[i].pCursor ){
if( p->aStack[tos].flags & STK_Int ){
- alreadyExists = sqliteDbbeTest(p->aCsr[i].pCursor, sizeof(int),
+ alreadyExists = pBe->Test(p->aCsr[i].pCursor, sizeof(int),
(char*)&p->aStack[tos].i);
}else{
if( Stringify(p, tos) ) goto no_mem;
- alreadyExists = sqliteDbbeTest(p->aCsr[i].pCursor,p->aStack[tos].n,
+ alreadyExists = pBe->Test(p->aCsr[i].pCursor,p->aStack[tos].n,
p->zStack[tos]);
}
}
@@ -1920,7 +1921,7 @@ int sqliteVdbeExec(
if( i<0 || i>=p->nCursor || p->aCsr[i].pCursor==0 ){
v = 0;
}else{
- v = sqliteDbbeNew(p->aCsr[i].pCursor);
+ v = pBe->New(p->aCsr[i].pCursor);
}
NeedStack(p, p->tos+1);
p->tos++;
@@ -1953,7 +1954,7 @@ int sqliteVdbeExec(
nKey = sizeof(int);
zKey = (char*)&p->aStack[nos].i;
}
- sqliteDbbePut(p->aCsr[i].pCursor, nKey, zKey,
+ pBe->Put(p->aCsr[i].pCursor, nKey, zKey,
p->aStack[tos].n, p->zStack[tos]);
}
PopStack(p, 2);
@@ -1980,7 +1981,7 @@ int sqliteVdbeExec(
nKey = p->aStack[tos].n;
zKey = p->zStack[tos];
}
- sqliteDbbeDelete(p->aCsr[i].pCursor, nKey, zKey);
+ pBe->Delete(p->aCsr[i].pCursor, nKey, zKey);
}
PopStack(p, 1);
break;
@@ -2034,29 +2035,29 @@ int sqliteVdbeExec(
if( NeedStack(p, tos) ) goto no_mem;
if( i>=0 && inCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
if( p->aCsr[i].keyAsData ){
- amt = sqliteDbbeKeyLength(pCrsr);
+ amt = pBe->KeyLength(pCrsr);
if( amt<=sizeof(int)*(p2+1) ){
p->aStack[tos].flags = STK_Null;
break;
}
- pAddr = (int*)sqliteDbbeReadKey(pCrsr, sizeof(int)*p2);
+ pAddr = (int*)pBe->ReadKey(pCrsr, sizeof(int)*p2);
if( *pAddr==0 ){
p->aStack[tos].flags = STK_Null;
break;
}
- z = sqliteDbbeReadKey(pCrsr, *pAddr);
+ z = pBe->ReadKey(pCrsr, *pAddr);
}else{
- amt = sqliteDbbeDataLength(pCrsr);
+ amt = pBe->DataLength(pCrsr);
if( amt<=sizeof(int)*(p2+1) ){
p->aStack[tos].flags = STK_Null;
break;
}
- pAddr = (int*)sqliteDbbeReadData(pCrsr, sizeof(int)*p2);
+ pAddr = (int*)pBe->ReadData(pCrsr, sizeof(int)*p2);
if( *pAddr==0 ){
p->aStack[tos].flags = STK_Null;
break;
}
- z = sqliteDbbeReadData(pCrsr, *pAddr);
+ z = pBe->ReadData(pCrsr, *pAddr);
}
p->zStack[tos] = z;
p->aStack[tos].n = strlen(z) + 1;
@@ -2079,11 +2080,11 @@ int sqliteVdbeExec(
if( NeedStack(p, p->tos) ) goto no_mem;
if( i>=0 && inCursor && (pCrsr = p->aCsr[i].pCursor)!=0 ){
- char *z = sqliteDbbeReadKey(pCrsr, 0);
+ char *z = pBe->ReadKey(pCrsr, 0);
if( p->aCsr[i].keyAsData ){
p->zStack[tos] = z;
p->aStack[tos].flags = STK_Str;
- p->aStack[tos].n = sqliteDbbeKeyLength(pCrsr);
+ p->aStack[tos].n = pBe->KeyLength(pCrsr);
}else{
memcpy(&p->aStack[tos].i, z, sizeof(int));
p->aStack[tos].flags = STK_Int;
@@ -2100,7 +2101,7 @@ int sqliteVdbeExec(
case OP_Rewind: {
int i = pOp->p1;
if( i>=0 && inCursor && p->aCsr[i].pCursor!=0 ){
- sqliteDbbeRewind(p->aCsr[i].pCursor);
+ pBe->Rewind(p->aCsr[i].pCursor);
}
break;
}
@@ -2113,7 +2114,7 @@ int sqliteVdbeExec(
case OP_Next: {
int i = pOp->p1;
if( i>=0 && inCursor && p->aCsr[i].pCursor!=0 ){
- if( sqliteDbbeNextKey(p->aCsr[i].pCursor)==0 ){
+ if( pBe->NextKey(p->aCsr[i].pCursor)==0 ){
pc = pOp->p2 - 1;
}else{
p->nFetch++;
@@ -2163,8 +2164,8 @@ int sqliteVdbeExec(
int *aIdx;
int nIdx;
int j, k;
- nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
- aIdx = (int*)sqliteDbbeReadData(pCrsr, 0);
+ nIdx = pBe->DataLength(pCrsr)/sizeof(int);
+ aIdx = (int*)pBe->ReadData(pCrsr, 0);
if( nIdx>1 ){
k = *(aIdx++);
if( k>nIdx-1 ) k = nIdx-1;
@@ -2209,10 +2210,10 @@ int sqliteVdbeExec(
Integerify(p, nos);
newVal = p->aStack[nos].i;
if( Stringify(p, tos) ) goto no_mem;
- r = sqliteDbbeFetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
+ r = pBe->Fetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
if( r==0 ){
/* Create a new record for this index */
- sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
+ pBe->Put(pCrsr, p->aStack[tos].n, p->zStack[tos],
sizeof(int), (char*)&newVal);
}else{
/* Extend the existing record */
@@ -2220,32 +2221,32 @@ int sqliteVdbeExec(
int *aIdx;
int k;
- nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
+ nIdx = pBe->DataLength(pCrsr)/sizeof(int);
if( nIdx==1 ){
aIdx = sqliteMalloc( sizeof(int)*4 );
if( aIdx==0 ) goto no_mem;
aIdx[0] = 2;
- sqliteDbbeCopyData(pCrsr, 0, sizeof(int), (char*)&aIdx[1]);
+ pBe->CopyData(pCrsr, 0, sizeof(int), (char*)&aIdx[1]);
aIdx[2] = newVal;
- sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
+ pBe->Put(pCrsr, p->aStack[tos].n, p->zStack[tos],
sizeof(int)*4, (char*)aIdx);
sqliteFree(aIdx);
}else{
- aIdx = (int*)sqliteDbbeReadData(pCrsr, 0);
+ aIdx = (int*)pBe->ReadData(pCrsr, 0);
k = aIdx[0];
if( kaStack[tos].n, p->zStack[tos],
+ pBe->Put(pCrsr, p->aStack[tos].n, p->zStack[tos],
sizeof(int)*nIdx, (char*)aIdx);
}else{
nIdx *= 2;
aIdx = sqliteMalloc( sizeof(int)*nIdx );
if( aIdx==0 ) goto no_mem;
- sqliteDbbeCopyData(pCrsr, 0, sizeof(int)*(k+1), (char*)aIdx);
+ pBe->CopyData(pCrsr, 0, sizeof(int)*(k+1), (char*)aIdx);
aIdx[k+1] = newVal;
aIdx[0]++;
- sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
+ pBe->Put(pCrsr, p->aStack[tos].n, p->zStack[tos],
sizeof(int)*nIdx, (char*)aIdx);
sqliteFree(aIdx);
}
@@ -2284,12 +2285,12 @@ int sqliteVdbeExec(
Integerify(p, nos);
oldVal = p->aStack[nos].i;
if( Stringify(p, tos) ) goto no_mem;
- r = sqliteDbbeFetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
+ r = pBe->Fetch(pCrsr, p->aStack[tos].n, p->zStack[tos]);
if( r==0 ) break;
- nIdx = sqliteDbbeDataLength(pCrsr)/sizeof(int);
- aIdx = (int*)sqliteDbbeReadData(pCrsr, 0);
+ nIdx = pBe->DataLength(pCrsr)/sizeof(int);
+ aIdx = (int*)pBe->ReadData(pCrsr, 0);
if( (nIdx==1 && aIdx[0]==oldVal) || (aIdx[0]==1 && aIdx[1]==oldVal) ){
- sqliteDbbeDelete(pCrsr, p->aStack[tos].n, p->zStack[tos]);
+ pBe->Delete(pCrsr, p->aStack[tos].n, p->zStack[tos]);
}else{
k = aIdx[0];
for(j=1; j<=k && aIdx[j]!=oldVal; j++){}
@@ -2300,7 +2301,7 @@ int sqliteVdbeExec(
if( aIdx[0]*3 + 1 < nIdx ){
nIdx /= 2;
}
- sqliteDbbePut(pCrsr, p->aStack[tos].n, p->zStack[tos],
+ pBe->Put(pCrsr, p->aStack[tos].n, p->zStack[tos],
sizeof(int)*nIdx, (char*)aIdx);
}
}
@@ -2315,7 +2316,7 @@ int sqliteVdbeExec(
** from the disk.
*/
case OP_Destroy: {
- sqliteDbbeDropTable(p->pBe, pOp->p3);
+ pBe->DropTable(pBe, pOp->p3);
break;
}
@@ -2324,7 +2325,7 @@ int sqliteVdbeExec(
** Compress, optimize, and tidy up the GDBM file named by P3.
*/
case OP_Reorganize: {
- sqliteDbbeReorganizeTable(p->pBe, pOp->p3);
+ pBe->ReorganizeTable(pBe, pOp->p3);
break;
}
@@ -2346,9 +2347,9 @@ int sqliteVdbeExec(
for(j=p->nList; j<=i; j++) p->apList[j] = 0;
p->nList = i+1;
}else if( p->apList[i] ){
- sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ pBe->CloseTempFile(pBe, p->apList[i]);
}
- rc = sqliteDbbeOpenTempFile(p->pBe, &p->apList[i]);
+ rc = pBe->OpenTempFile(pBe, &p->apList[i]);
if( rc!=SQLITE_OK ){
sqliteSetString(pzErrMsg, "unable to open a temporary file", 0);
}
@@ -2418,7 +2419,7 @@ int sqliteVdbeExec(
int i = pOp->p1;
if( i<0 ) goto bad_instruction;
if( inList && p->apList[i]!=0 ){
- sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+ pBe->CloseTempFile(pBe, p->apList[i]);
p->apList[i] = 0;
}
break;
diff --git a/www/changes.tcl b/www/changes.tcl
index 3945d96b1c..87147a18d7 100644
--- a/www/changes.tcl
+++ b/www/changes.tcl
@@ -17,6 +17,13 @@ proc chng {date desc} {
puts "
"
}
+chng {2000 Oct 18 (1.0.13)} {
+Break out the GDBM driver into a separate file in anticipation
+ to added new drivers.
+Allow the name of a database to be prefixed by the driver type.
+ For now, the only driver type is "gdbm:".
+}
+
chng {2000 Oct 16 (1.0.12)} {
Fixed an off-by-one error that was causing a coredump in
the '%q' format directive of the new