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