]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Break out DBBE drivers into separate files. (CVS 157)
authordrh <drh@noemail.net>
Thu, 19 Oct 2000 01:49:02 +0000 (01:49 +0000)
committerdrh <drh@noemail.net>
Thu, 19 Oct 2000 01:49:02 +0000 (01:49 +0000)
FossilOrigin-Name: 979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c

Makefile.in
VERSION
manifest
manifest.uuid
src/dbbe.c
src/dbbe.h
src/dbbegdbm.c [new file with mode: 0644]
src/main.c
src/vdbe.c
www/changes.tcl

index d0fe73c39ecc978e6b34247f53551195a05a3ba8..4556180a0a0911060f78816cc0ef94734ceb3707 100644 (file)
@@ -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 bb83058ed548aaf2dfaaee63a55c900f1e4a8a9f..2ac9634d32a9bc0a6fd85a02b73541f10672520c 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.12
+1.0.13
index 3ccd05d93f5a4e1f54621871e4897cace4c0c225..87a322199863d4620d53d5d6b7d19b2002e05101 100644 (file)
--- 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
index 1166b8ec7f88dfe81b1c057e85031c3c215d2016..fe7e38badad9638ab28a77dafbd8c1344c4cbcc7 100644 (file)
@@ -1 +1 @@
-7330218a912ea788b0c83fa7c7350fd6df88b18a
\ No newline at end of file
+979ef5d5d64b8e38cc15fef0d2d507ca2fe6842c
\ No newline at end of file
index b9271ad678b23b178b75b00dde9fe10d50d6a721..9a3f4b3254e93c2f9f3ed5b39cb252802bb90c7b 100644 (file)
 ** 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 <gdbm.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <time.h>
 
 /*
-** 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;
-    }
-  }
-  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;
+  extern Dbbe *sqliteGdbmOpen(const char*,int,int,char**);
+  if( strncmp(zName, "gdbm:", 5)==0 ){
+    return sqliteGdbmOpen(&zName[5], writeFlag, createFlag, pzErrMsg);
   }
-  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;
+#if 0
+  if( strncmp(zName, "memory:", 7)==0 ){
+    extern Dbbe *sqliteMemOpen(const char*,int,int,char**);
+    return sqliteMemOpen(&zName[7], writeFlag, createFlag, pzErrMsg);
   }
-  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; i<pBe->nTemp; 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; i<pBe->nTemp; 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; i<pBe->nTemp; 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);
 }
index 329cef3c5c9a422e64eaede69fb1607afc5f3e70..7c54a77b648c943c57b654911515091168ed4e67 100644 (file)
@@ -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_
 typedef struct Dbbe Dbbe;
 typedef struct DbbeCursor DbbeCursor;
 
-/*
-** The 18 interface routines.
-*/
-
-/* Open a complete database */
-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.
+/*
+** Open a complete database.
 **
-** If zName is 0 or "", then a temporary file is created that
-** will be deleted when closed.
-*/
-int sqliteDbbeOpenCursor(Dbbe*, const char *zName, int writeable, DbbeCursor**);
-
-/* Delete a table from the database */
-void sqliteDbbeDropTable(Dbbe*, const char *zTableName);
-
-/* Reorganize a table to speed access or reduce its disk usage */
-int sqliteDbbeReorganizeTable(Dbbe*, const char *zTableName);
-
-/* Close a cursor */
-void sqliteDbbeCloseCursor(DbbeCursor*);
-
-/* 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);
-
-/* Return 1 if the given key is already in the table.  Return 0
-** if it is not.
+** 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.
 */
-int sqliteDbbeTest(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);
-
-/* 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);
-
-/* Return the length of the most recently fetched key or data. */
-int sqliteDbbeKeyLength(DbbeCursor*);
-int sqliteDbbeDataLength(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
-** sqliteDbbeRewind().  The return value is 1 if there is another
-** entry, or 0 if there are no more entries. */
-int sqliteDbbeNextKey(DbbeCursor*);
-
-/* Make it so that the next call to sqliteDbbeNextKey() returns
-** the first entry of the table. */
-int sqliteDbbeRewind(DbbeCursor*);
-
-/* Get a new integer key for this table. */
-int sqliteDbbeNew(DbbeCursor*);
+Dbbe *sqliteDbbeOpen(const char *zName, int write, int create, char **pzErr);
 
-/* Write an entry into a table.  If another entry already exists with
-** the same key, the old entry is discarded first.
+/*
+** This is the structure returned by sqliteDbbeOpen().  It contains pointers
+** to all access routines for the database backend.
 */
-int sqliteDbbePut(DbbeCursor*, int nKey, char *pKey, int nData, char *pData);
-
-/* Remove an entry from the table */
-int sqliteDbbeDelete(DbbeCursor*, int nKey, char *pKey);
-
-/* Open a file suitable for temporary storage */
-int sqliteDbbeOpenTempFile(Dbbe*, FILE**);
-
-/* Close a temporary file */
-void sqliteDbbeCloseTempFile(Dbbe *, FILE *);
+struct Dbbe {
+  /* Close the whole database. */
+  void (*Close)(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.
+  */
+  int (*OpenCursor)(Dbbe*, const char *zName, int writeable, DbbeCursor**);
+
+  /* Delete a table from the database */
+  void (*DropTable)(Dbbe*, const char *zTableName);
+
+  /* Reorganize a table to speed access or reduce its disk usage */
+  int (*ReorganizeTable)(Dbbe*, const char *zTableName);
+
+  /* Close a cursor */
+  void (*CloseCursor)(DbbeCursor*);
+
+  /* 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);
+
+  /* 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 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);
+
+  /* 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);
+
+  /* Return the length of the most recently fetched key or data. */
+  int (*KeyLength)(DbbeCursor*);
+  int (*DataLength)(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*);
+
+  /* Make it so that the next call to Dbbe.NextKey() returns
+  ** the first entry of the table. */
+  int (*Rewind)(DbbeCursor*);
+
+  /* Get a new integer key for this table. */
+  int (*New)(DbbeCursor*);
+
+  /* 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);
+
+  /* Remove an entry from the table */
+  int (*Delete)(DbbeCursor*, int nKey, char *pKey);
+
+  /* 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 (file)
index 0000000..1e27020
--- /dev/null
@@ -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 <gdbm.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+
+/*
+** 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; i<pBe->nTemp; 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; i<pBe->nTemp; 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; i<pBe->nTemp; 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;
+}
index b953dae0f7d22c72d1775cbf09c0ccb02b15afef..ca43c35fce9461926e2edb015c81114239577d66 100644 (file)
@@ -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; i<N_HASH; i++){
     Table *pNext, *pList = db->apTblHash[i];
     db->apTblHash[i] = 0;
index c7b9cb9d46c5a37868f6eaf537f19bef66b293d7..da27f4b47fbed0f5c9336193efa4230780f0a207 100644 (file)
@@ -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 <unistd.h>
@@ -679,7 +679,7 @@ static void Cleanup(Vdbe *p){
   p->azColName = 0;
   for(i=0; i<p->nCursor; 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; i<p->nList; 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 && i<p->nCursor && 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 && i<p->nCursor && 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 && i<p->nCursor && 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 && i<p->nCursor && (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 && i<p->nCursor && (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 && i<p->nCursor && 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 && i<p->nCursor && 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( k<nIdx-1 ){
                 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);
               }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( i<p->nList && p->apList[i]!=0 ){
-          sqliteDbbeCloseTempFile(p->pBe, p->apList[i]);
+          pBe->CloseTempFile(pBe, p->apList[i]);
           p->apList[i] = 0;
         }
         break;
index 3945d96b1ca63f8eac2a8d45ade458c619c4b283..87147a18d7b6ceb48d77e7a8af03cc166ac8b0b4 100644 (file)
@@ -17,6 +17,13 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chng {2000 Oct 18 (1.0.13)} {
+<li>Break out the GDBM driver into a separate file in anticipation
+    to added new drivers.</li>
+<li>Allow the name of a database to be prefixed by the driver type.
+    For now, the only driver type is "gdbm:".<li>
+}
+
 chng {2000 Oct 16 (1.0.12)} {
 <li>Fixed an off-by-one error that was causing a coredump in 
     the '%q' format directive of the new