]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Perpare to fork SQLite2.0 develop into a separate tree (CVS 183)
authordrh <drh@noemail.net>
Sun, 11 Feb 2001 16:56:24 +0000 (16:56 +0000)
committerdrh <drh@noemail.net>
Sun, 11 Feb 2001 16:56:24 +0000 (16:56 +0000)
FossilOrigin-Name: 6adb6078871114ba19ab601bb94d43ff9e03e43f

13 files changed:
VERSION
manifest
manifest.uuid
src/ex/README [new file with mode: 0644]
src/ex/db.c [moved from src/db.c with 99% similarity]
src/ex/db.h [moved from src/db.h with 97% similarity]
src/ex/dbbebdb1.c [new file with mode: 0644]
src/ex/dbbemird.c [new file with mode: 0644]
src/ex/pg.c [moved from src/pg.c with 99% similarity]
src/ex/pg.h [moved from src/pg.h with 96% similarity]
src/main.c
src/tokenize.c
www/changes.tcl

diff --git a/VERSION b/VERSION
index 9084fa2f716a7117829f3f32a5f4cef400e02903..c2320f5be488cd920b9e5fe6a600873b869a8fe1 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0
+1.0.20
index bd9711067d0f746b3d4b101dd2e9ef57c0352894..5694e61bb72d318877b3922f0625094caadc21b7 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,28 +1,31 @@
-C hi\s(CVS\s1716)
-D 2001-02-06T14:10:27
+C Perpare\sto\sfork\sSQLite2.0\sdevelop\sinto\sa\sseparate\stree\s(CVS\s183)
+D 2001-02-11T16:56:24
 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
 F Makefile.in 7efa81e2985b45ba73db27d55b70cc927f5abfd7
 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
-F VERSION 05e17b646a817240c206186f94f8f4c70974d5dc
+F VERSION 153302ac968751d918e44c3f26774dcfe50ddc0a
 F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
 F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
 F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
 F doc/report1.txt ad0a41513479f1be0355d1f3f074e66779ff2282
 F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
 F src/build.c 7aa5879bf58ea6bbff22c26c59d1130021fa6ca4
-F src/db.c fff070e77423bbf98f5b138f23c605006a61066d
-F src/db.h 6c8b8b6777f7c55b37cb851e9fe4bc3f379920c0
 F src/dbbe.c 162d29b09ac379f160892c5795efc14099dcc8eb
 F src/dbbe.h 0435a36906a839cce062608f51bd9d3e79878fec
 F src/dbbegdbm.c 5bfcb1b4ee47a98c5eae83041e9716cd3233fd0e
 F src/dbbemem.c 215e107830ddf0d5a565bb7c20dd7200a925ef75
 F src/delete.c b83dc815f83220a82df13f1f1f479187d305fe6a
+F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
+F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
+F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e
+F src/ex/dbbebdb1.c 61ed414307f829478614def33de640bbe5b2f770
+F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a
+F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f
+F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd
 F src/expr.c 49bc261fdc4f4fb91c74cd668a9a952c00e85931
 F src/insert.c 4bc1cab84f7805d560a1417734a532843e30b762
-F src/main.c 92fcd6d967ceee1f96a5b9543779eef6e9b56913
+F src/main.c 5afe29c425b875acede20f609485866eb5b276f6
 F src/parse.y 25ee4d8efccc4b247c32fe4ab194e3dd8fd5a4ee
-F src/pg.c 2981173b2a752ef3578168e7af1a32ff22b70db6
-F src/pg.h a95c4803a1aae99449aa2c0a1af0c8d863a3f340
 F src/printf.c af0dc65c293427272e1949c7807b1d88f10004fd
 F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
 F src/select.c 0cadab95c8011ddbffe804de94f12f3c0e317863
@@ -33,7 +36,7 @@ F src/sqliteInt.h fd513fa6b7ac94919f85ebfa183aaa194284ce16
 F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
 F src/tclsqlite.c 2a925e1835c348f07dd17c87d95ae9a577833407
 F src/test.file 55ca6286e3e4f4fba5d0448333fa99fc5a404a73
-F src/tokenize.c 6843f1d7a5d2ee08ceb10bdecfcc8684131ffcf7
+F src/tokenize.c c7ad428f38e56342eb2025320480b5ae9ece1b90
 F src/update.c 9692fbac8e95fdbc5318d39db576aa6c57b9c8ab
 F src/util.c 0298100e6427a4b644f767ede12276fa7170fbb6
 F src/vdbe.c 6e613f25b0fe1c81b097f46a8fe68c68c39a6abf
@@ -75,7 +78,7 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
 F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
 F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
 F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
-F www/changes.tcl cb276a239c98524731e2780c70deb01b2e7e4bcc
+F www/changes.tcl 2bd34627e9dc459f53d7e11630d92660be974b10
 F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
 F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
 F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
@@ -85,7 +88,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
 F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
 F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
 F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
-P 29305c3c9eb805a1f5beb82ad50cba1d3bbd3465
-R b22c18ed2ce590a98ae62ee327d6d335
+P 5128135c7e12f763722981ad39ecb7835db49142
+R ab8718eb81b6e7a57fbb613590282f19
 U drh
-Z 85d522fbac6369cd564c9bf8a4621677
+Z 4c71efe3f5c2467e23cd148f67512b61
index 0c42c90875951b70e0ca337a110a08523385011f..2f3646a6e50301fad40ce24408ce4937fd50a5ba 100644 (file)
@@ -1 +1 @@
-5128135c7e12f763722981ad39ecb7835db49142
\ No newline at end of file
+6adb6078871114ba19ab601bb94d43ff9e03e43f
\ No newline at end of file
diff --git a/src/ex/README b/src/ex/README
new file mode 100644 (file)
index 0000000..29f7142
--- /dev/null
@@ -0,0 +1,9 @@
+This directory is intended to hold "experimental" files.
+
+The code in this directory does not necessary work.  It may
+or may not be added to future SQLite releases.  We just need
+a place to put ideas and works-in-progress and so this 
+directory was created.
+
+If you are interested in the production versions of SQLite,
+you can safely ignore this directory.
similarity index 99%
rename from src/db.c
rename to src/ex/db.c
index 3a9c6687bb2445bda0a101417301581404d3c1c2..358ddc9d3e0ebdc1c49bda994b4d15fd6c4cdcf5 100644 (file)
--- a/src/db.c
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: db.c,v 1.6 2001/01/31 13:28:08 drh Exp $
+** $Id: db.c,v 1.1 2001/02/11 16:56:24 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pg.h"
similarity index 97%
rename from src/db.h
rename to src/ex/db.h
index cf7d6853de2cc046e13b6af677e17ae38c31a44b..67a090640992f917011a69ba9d27f0d7beb399bf 100644 (file)
--- a/src/db.h
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: db.h,v 1.5 2001/01/31 13:28:09 drh Exp $
+** $Id: db.h,v 1.1 2001/02/11 16:56:24 drh Exp $
 */
 
 typedef struct Db Db;
diff --git a/src/ex/dbbebdb1.c b/src/ex/dbbebdb1.c
new file mode 100644 (file)
index 0000000..f1d3d9d
--- /dev/null
@@ -0,0 +1,618 @@
+/*
+** 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 Berkeley Database version 1.85 as the database backend.
+**
+** $Id: dbbebdb1.c,v 1.1 2001/02/11 16:56:24 drh Exp $
+*/
+#ifdef USE_BDB2
+
+#include "sqliteInt.h"
+#include <sys/types.h>
+#include <limits.h>
+#include <db.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 */
+  DB dbf;                 /* The file itself */
+  int nRef;               /* Number of references */
+  int delOnClose;         /* Delete when closing */
+  int writeable;          /* Opened for writing */
+  DbbeCursor *pCursor; /* Which of several DbbeCursors has the file cursor */
+  BeFile *pNext, *pPrev;  /* Next and previous on list of open files */
+};
+
+/*
+** The following structure contains all information used by BDB2
+** database driver.  This is a subclass of the Dbbe structure.
+*/
+typedef struct Dbbex Dbbex;
+struct Dbbex {
+  Dbbe dbbe;         /* The base class */
+  int write;         /* True for write permission */
+  BeFile *pOpen;     /* List of open files */
+  char *zDir;        /* Directory hold the database */
+};
+
+/*
+** 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 */
+  DBT key;           /* Most recently used key */
+  DBT data;          /* Most recent data */
+  int needRewind;    /* Next key should be the first */
+  int readPending;   /* The fetch hasn't actually been done yet */
+};
+
+/*
+** The "mkdir()" function only takes one argument under Windows.
+*/
+#if OS_WIN
+# define mkdir(A,B) mkdir(A)
+#endif
+
+/*
+** Forward declaration
+*/
+static void sqliteBdb1CloseCursor(DbbeCursor *pCursr);
+
+/*
+** Completely shutdown the given database.  Close all files.  Free all memory.
+*/
+static void sqliteBdb1Close(Dbbe *pDbbe){
+  Dbbex *pBe = (Dbbex*)pDbbe;
+  BeFile *pFile, *pNext;
+  for(pFile=pBe->pOpen; pFile; pFile=pNext){
+    pNext = pFile->pNext;
+    (*pFile->dbf)(pFile->dbf);
+    memset(pFile, 0, sizeof(*pFile));   
+    sqliteFree(pFile);
+  }
+  sqliteDbbeCloseAllTempFiles(pDbbe);
+  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){
+  return sqliteDbbeNameToFile(pBe->zDir, zTable, ".tbl");
+}
+
+/*
+** 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 sqliteBdb1OpenCursor(
+  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 */
+  int intKeyOnly,         /* True if only integer keys are used */
+  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 open_flags;         /* Flags passed to dbopen() */
+  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 ){
+      open_flags = O_RDWR|O_CREAT
+    }else{
+      open_flags = O_RDONLY;
+    }
+    pFile = sqliteMalloc( sizeof(*pFile) );
+    if( pFile==0 ){
+      sqliteFree(zFile);
+      return SQLITE_NOMEM;
+    }
+    if( zFile ){
+      if( !writeable || pBe->write ){
+        pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 0);
+      }else{
+        pFile->dbf = 0;
+      }
+    }else{
+      int limit;
+      char zRandom[50];
+      zFile = 0;
+      limit = 5;
+      do {
+        sqliteRandomName(zRandom, "_temp_table_");
+        sqliteFree(zFile);
+        zFile = sqliteFileOfTable(pBe, zRandom);
+        pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 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->pCursor = 0;
+    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 ){
+    sqliteBdb1CloseCursor(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 sqliteBdb1DropTable(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 sqliteBdb1OpenCursor().
+**
+** 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 sqliteBdb1CloseCursor(DbbeCursor *pCursr){
+  BeFile *pFile;
+  Dbbex *pBe;
+  if( pCursr==0 ) return;
+  pFile = pCursr->pFile;
+  pBe = pCursr->pBe;
+  if( pFile->pCursor==pCursr ){
+    pFile->pCursor = 0;
+  }
+  pFile->nRef--;
+  if( pFile->dbf!=NULL ){
+    (*pFile->dbf->sync)(pFile->dbf, 0);
+  }
+  if( pFile->nRef<=0 ){
+    if( pFile->dbf!=NULL ){
+      (*pFile->dbf->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 sqliteBdb1ReorganizeTable(Dbbe *pBe, const char *zTable){
+  /* No-op */
+  return SQLITE_OK;
+}
+
+/*
+** Clear the given datum
+*/
+static void datumClear(datum *p){
+  if( p->dptr ) free(p->dptr); ########
+  p->data = 0;
+  p->size = 0;
+}
+
+/*
+** Fetch a single record from an open cursor.  Return 1 on success
+** and 0 on failure.
+*/
+static int sqliteBdb1Fetch(DbbeCursor *pCursr, int nKey, char *pKey){
+  DBT key;
+  key.size = nKey;
+  key.data = 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 sqliteBdb1Test(DbbeCursor *pCursr, int nKey, char *pKey){
+  DBT 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 sqliteBdb1CopyKey(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 sqliteBdb1CopyData(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 *sqliteBdb1ReadKey(DbbeCursor *pCursr, int offset){
+  if( offset<0 || offset>=pCursr->key.dsize ) return "";
+  return &pCursr->key.dptr[offset];
+}
+static char *sqliteBdb1ReadData(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 sqliteBdb1KeyLength(DbbeCursor *pCursr){
+  return pCursr->key.dsize;
+}
+static int sqliteBdb1DataLength(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 sqliteBdb1Rewind(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 sqliteBdb1NextKey(DbbeCursor *pCursr){
+  DBT 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 sqliteBdb1New(DbbeCursor *pCursr){
+  int iKey;
+  DBT key;
+  int go = 1;
+
+  if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
+  while( go ){
+    iKey = sqliteRandomInteger();
+    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 sqliteBdb1Put(
+  DbbeCursor *pCursr,  /* Write to the database associated with this cursor */
+  int nKey,            /* Number of bytes in the key */
+  char *pKey,          /* The data for the key */
+  int nData,           /* Number of bytes of data */
+  char *pData          /* The data */
+){
+  DBT 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 sqliteBdb1Delete(DbbeCursor *pCursr, int nKey, char *pKey){
+  DBT 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 is located in the same directory
+** as the rest of the database.
+*/
+static int sqliteBdb1OpenTempFile(Dbbe *pDbbe, FILE **ppFile){
+  Dbbex *pBe = (Dbbex*)pDbbe;
+  return sqliteDbbeOpenTempFile(pBe->zDir, pDbbe, ppFile);
+}
+
+/*
+** This variable contains pointers to all of the access methods
+** used to implement the GDBM backend.
+*/
+static struct DbbeMethods gdbmMethods = {
+  /* n         Close */   sqliteBdb1Close,
+  /*      OpenCursor */   sqliteBdb1OpenCursor,
+  /*       DropTable */   sqliteBdb1DropTable,
+  /* ReorganizeTable */   sqliteBdb1ReorganizeTable,
+  /*     CloseCursor */   sqliteBdb1CloseCursor,
+  /*           Fetch */   sqliteBdb1Fetch,
+  /*            Test */   sqliteBdb1Test,
+  /*         CopyKey */   sqliteBdb1CopyKey,
+  /*        CopyData */   sqliteBdb1CopyData,
+  /*         ReadKey */   sqliteBdb1ReadKey,
+  /*        ReadData */   sqliteBdb1ReadData,
+  /*       KeyLength */   sqliteBdb1KeyLength,
+  /*      DataLength */   sqliteBdb1DataLength,
+  /*         NextKey */   sqliteBdb1NextKey,
+  /*          Rewind */   sqliteBdb1Rewind,
+  /*             New */   sqliteBdb1New,
+  /*             Put */   sqliteBdb1Put,
+  /*          Delete */   sqliteBdb1Delete,
+  /*    OpenTempFile */   sqliteBdb1OpenTempFile,
+  /*   CloseTempFile */   sqliteDbbeCloseTempFile
+};
+
+
+/*
+** 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 *sqliteBdb1Open(
+  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.x = &gdbmMethods;
+  pNew->zDir = (char*)&pNew[1];
+  strcpy(pNew->zDir, zName);
+  pNew->write = writeFlag;
+  pNew->pOpen = 0;
+  return &pNew->dbbe;
+}
diff --git a/src/ex/dbbemird.c b/src/ex/dbbemird.c
new file mode 100644 (file)
index 0000000..7ca9759
--- /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: dbbemird.c,v 1.1 2001/02/11 16:56:24 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;
+}
similarity index 99%
rename from src/pg.c
rename to src/ex/pg.c
index f622a80d77d6d5e988d87be799ade400d18d183b..5a826c81456e28a0acf6a87b70690a191723ea5d 100644 (file)
--- a/src/pg.c
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: pg.c,v 1.4 2001/01/25 01:45:41 drh Exp $
+** $Id: pg.c,v 1.1 2001/02/11 16:56:24 drh Exp $
 */
 #include <assert.h>
 #include <sys/types.h>
similarity index 96%
rename from src/pg.h
rename to src/ex/pg.h
index cdf8a276245a43f18f1cad7a33fffb7f99107aa9..f0fd50acf841b4a6c1701734c78ceac2d1a56871 100644 (file)
--- a/src/pg.h
@@ -21,7 +21,7 @@
 **   http://www.hwaci.com/drh/
 **
 *************************************************************************
-** $Id: pg.h,v 1.3 2001/01/21 00:58:09 drh Exp $
+** $Id: pg.h,v 1.1 2001/02/11 16:56:24 drh Exp $
 */
 
 typedef struct Pgr Pgr;
index 47f85d82104c380034b8fc27d5813ba54e736515..4205b70bed021e7bf2ea03fb096b5722512ea3dd 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.24 2001/01/15 22:51:11 drh Exp $
+** $Id: main.c,v 1.25 2001/02/11 16:56:24 drh Exp $
 */
 #include "sqliteInt.h"
 #include <unistd.h>
@@ -124,7 +124,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
   ** database scheme.
   */
   static VdbeOp initProg[] = {
-    { OP_OpenTbl,    0, 0,  MASTER_NAME},
+    { OP_OpenTbl,  0, 0,  MASTER_NAME},
     { OP_Next,     0, 9,  0},           /* 1 */
     { OP_Field,    0, 0,  0},
     { OP_String,   0, 0,  "meta"},
index fddad500ea4cc06d9f8551da0c67de65087b9b97..3942fc402fd0b95200f255d562cd0ee43964cd6e 100644 (file)
@@ -27,7 +27,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.16 2000/12/10 18:23:51 drh Exp $
+** $Id: tokenize.c,v 1.17 2001/02/11 16:56:24 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -308,7 +308,7 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
   static FILE *trace = 0;
   extern void *sqliteParserAlloc(void*(*)(int));
   extern void sqliteParserFree(void*, void(*)(void*));
-  extern int sqliteParser(void*, int, ...);
+  extern int sqliteParser(void*, int, Token, Parse*);
   extern void sqliteParserTrace(FILE*, char *);
 
   pParse->db->flags &= ~SQLITE_Interrupt;
index 939175664677acd8a42d5a978a4cf7220bfe91f3..657728738d615cf7b7abb845b0e07dae21f188bb 100644 (file)
@@ -17,6 +17,17 @@ proc chng {date desc} {
   puts "<DD><P><UL>$desc</UL></P></DD>"
 }
 
+chng {2001 Feb 11 (1.0.20)} {
+<li>Merge development changes into the main trunk.  Future work toward
+    using a BTree file structure will use a separate CVS source tree.  This
+    CVS tree will continue to support the GDBM version of SQLite only.</li>
+}
+
+chng {2001 Feb 6 (1.0.19)} {
+<li>Fix a strange (but valid) C declaration that was causing problems
+    for QNX.  No logical changes.</li>
+}
+
 chng {2001 Jan 4 (1.0.18)} {
 <li>Print the offending SQL statement when an error occurs.</li>
 <li>Do not require commas between constraints in CREATE TABLE statements.</li>