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

    $desc

" } +chng {2000 Oct 18 (1.0.13)} { +
  • Break out the GDBM driver into a separate file in anticipation + to added new drivers.
  • +
  • Allow the name of a database to be prefixed by the driver type. + For now, the only driver type is "gdbm:".
  • +} + chng {2000 Oct 16 (1.0.12)} {
  • Fixed an off-by-one error that was causing a coredump in the '%q' format directive of the new