Perpare to fork SQLite2.0 develop into a separate tree (CVS 183)

FossilOrigin-Name: 6adb6078871114ba19ab601bb94d43ff9e03e43f
This commit is contained in:
drh 2001-02-11 16:56:24 +00:00
parent 2e7051b3ec
commit 338ea13c0d
13 changed files with 1440 additions and 23 deletions

View File

@ -1 +1 @@
1.1.0
1.0.20

View File

@ -1,28 +1,31 @@
C hi\s(CVS\s1716)
D 2001-02-06T14:10:27
C Perpare\sto\sfork\sSQLite2.0\sdevelop\sinto\sa\sseparate\stree\s(CVS\s183)
D 2001-02-11T16:56:24
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 7efa81e2985b45ba73db27d55b70cc927f5abfd7
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
F VERSION 05e17b646a817240c206186f94f8f4c70974d5dc
F VERSION 153302ac968751d918e44c3f26774dcfe50ddc0a
F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x
F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff
F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47
F doc/report1.txt ad0a41513479f1be0355d1f3f074e66779ff2282
F src/TODO 38a68a489e56e9fd4a96263e0ff9404a47368ad4
F src/build.c 7aa5879bf58ea6bbff22c26c59d1130021fa6ca4
F src/db.c fff070e77423bbf98f5b138f23c605006a61066d
F src/db.h 6c8b8b6777f7c55b37cb851e9fe4bc3f379920c0
F src/dbbe.c 162d29b09ac379f160892c5795efc14099dcc8eb
F src/dbbe.h 0435a36906a839cce062608f51bd9d3e79878fec
F src/dbbegdbm.c 5bfcb1b4ee47a98c5eae83041e9716cd3233fd0e
F src/dbbemem.c 215e107830ddf0d5a565bb7c20dd7200a925ef75
F src/delete.c b83dc815f83220a82df13f1f1f479187d305fe6a
F src/ex/README b745b00acce2d892f60c40111dacdfc48e0c1c7a
F src/ex/db.c f1419ae6c93e40b5ac6e39fe7efd95d868e6f9d7
F src/ex/db.h 3f2933ee20c147fe494835786e4c6f3a562def4e
F src/ex/dbbebdb1.c 61ed414307f829478614def33de640bbe5b2f770
F src/ex/dbbemird.c b00aef85656fa0a101dac2c32e12922ad106715a
F src/ex/pg.c 2bbf6a94f37226d06337868b6bf4d7affc60197f
F src/ex/pg.h 23a4ac807b0546ec2bb6239ec8bd3e06926572cd
F src/expr.c 49bc261fdc4f4fb91c74cd668a9a952c00e85931
F src/insert.c 4bc1cab84f7805d560a1417734a532843e30b762
F src/main.c 92fcd6d967ceee1f96a5b9543779eef6e9b56913
F src/main.c 5afe29c425b875acede20f609485866eb5b276f6
F src/parse.y 25ee4d8efccc4b247c32fe4ab194e3dd8fd5a4ee
F src/pg.c 2981173b2a752ef3578168e7af1a32ff22b70db6
F src/pg.h a95c4803a1aae99449aa2c0a1af0c8d863a3f340
F src/printf.c af0dc65c293427272e1949c7807b1d88f10004fd
F src/random.c b36c3f57dc80c8f354e6bfbf39cf1e1de021d54a
F src/select.c 0cadab95c8011ddbffe804de94f12f3c0e317863
@ -33,7 +36,7 @@ F src/sqliteInt.h fd513fa6b7ac94919f85ebfa183aaa194284ce16
F src/table.c 5be76051a8ed6f6bfa641f4adc52529efa34fbf9
F src/tclsqlite.c 2a925e1835c348f07dd17c87d95ae9a577833407
F src/test.file 55ca6286e3e4f4fba5d0448333fa99fc5a404a73
F src/tokenize.c 6843f1d7a5d2ee08ceb10bdecfcc8684131ffcf7
F src/tokenize.c c7ad428f38e56342eb2025320480b5ae9ece1b90
F src/update.c 9692fbac8e95fdbc5318d39db576aa6c57b9c8ab
F src/util.c 0298100e6427a4b644f767ede12276fa7170fbb6
F src/vdbe.c 6e613f25b0fe1c81b097f46a8fe68c68c39a6abf
@ -75,7 +78,7 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8
F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6
F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be
F www/c_interface.tcl 11be2d5826eb7d6efd629751d3b483c1ed78ba14
F www/changes.tcl cb276a239c98524731e2780c70deb01b2e7e4bcc
F www/changes.tcl 2bd34627e9dc459f53d7e11630d92660be974b10
F www/crosscompile.tcl c99efacb3aefaa550c6e80d91b240f55eb9fd33e
F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9
F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0
@ -85,7 +88,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P 29305c3c9eb805a1f5beb82ad50cba1d3bbd3465
R b22c18ed2ce590a98ae62ee327d6d335
P 5128135c7e12f763722981ad39ecb7835db49142
R ab8718eb81b6e7a57fbb613590282f19
U drh
Z 85d522fbac6369cd564c9bf8a4621677
Z 4c71efe3f5c2467e23cd148f67512b61

View File

@ -1 +1 @@
5128135c7e12f763722981ad39ecb7835db49142
6adb6078871114ba19ab601bb94d43ff9e03e43f

9
src/ex/README Normal file
View File

@ -0,0 +1,9 @@
This directory is intended to hold "experimental" files.
The code in this directory does not necessary work. It may
or may not be added to future SQLite releases. We just need
a place to put ideas and works-in-progress and so this
directory was created.
If you are interested in the production versions of SQLite,
you can safely ignore this directory.

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/
**
*************************************************************************
** $Id: db.c,v 1.6 2001/01/31 13:28:08 drh Exp $
** $Id: db.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#include "sqliteInt.h"
#include "pg.h"

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/
**
*************************************************************************
** $Id: db.h,v 1.5 2001/01/31 13:28:09 drh Exp $
** $Id: db.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Db Db;

618
src/ex/dbbebdb1.c Normal file
View File

@ -0,0 +1,618 @@
/*
** Copyright (c) 2000 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*************************************************************************
** This file contains code to implement the database backend (DBBE)
** for sqlite. The database backend is the interface between
** sqlite and the code that does the actually reading and writing
** of information to the disk.
**
** This file uses Berkeley Database version 1.85 as the database backend.
**
** $Id: dbbebdb1.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#ifdef USE_BDB2
#include "sqliteInt.h"
#include <sys/types.h>
#include <limits.h>
#include <db.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
/*
** Information about each open disk file is an instance of this
** structure. There will only be one such structure for each
** disk file. If the VDBE opens the same file twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single BeFile structure with an
** nRef of 2.
*/
typedef struct BeFile BeFile;
struct BeFile {
char *zName; /* Name of the file */
DB dbf; /* The file itself */
int nRef; /* Number of references */
int delOnClose; /* Delete when closing */
int writeable; /* Opened for writing */
DbbeCursor *pCursor; /* Which of several DbbeCursors has the file cursor */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The following structure contains all information used by BDB2
** database driver. This is a subclass of the Dbbe structure.
*/
typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
char *zDir; /* Directory hold the database */
};
/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single BeFile structure for each disk file, but
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
** a cursor pointing to a particular part of the open BeFile. The
** BeFile.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
Dbbex *pBe; /* The database of which this record is a part */
BeFile *pFile; /* The database file for this table */
DBT key; /* Most recently used key */
DBT data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** The "mkdir()" function only takes one argument under Windows.
*/
#if OS_WIN
# define mkdir(A,B) mkdir(A)
#endif
/*
** Forward declaration
*/
static void sqliteBdb1CloseCursor(DbbeCursor *pCursr);
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
static void sqliteBdb1Close(Dbbe *pDbbe){
Dbbex *pBe = (Dbbex*)pDbbe;
BeFile *pFile, *pNext;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
(*pFile->dbf)(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
sqliteDbbeCloseAllTempFiles(pDbbe);
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Translate the name of an SQL table (or index) into the name
** of a file that holds the key/data pairs for that table or
** index. Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){
return sqliteDbbeNameToFile(pBe->zDir, zTable, ".tbl");
}
/*
** Open a new table cursor. Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr. Return an integer success
** code:
**
** SQLITE_OK It worked!
**
** SQLITE_NOMEM sqliteMalloc() failed
**
** SQLITE_PERM Attempt to access a file for which file
** access permission is denied
**
** SQLITE_BUSY Another thread or process is already using
** the corresponding file and has that file locked.
**
** SQLITE_READONLY The current thread already has this file open
** readonly but you are trying to open for writing.
** (This can happen if a SELECT callback tries to
** do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened. The temporary file
** will be deleted from the disk when it is closed.
*/
static int sqliteBdb1OpenCursor(
Dbbe *pDbbe, /* The database the table belongs to */
const char *zTable, /* The SQL name of the file to be opened */
int writeable, /* True to open for writing */
int intKeyOnly, /* True if only integer keys are used */
DbbeCursor **ppCursr /* Write the resulting table pointer here */
){
char *zFile; /* Name of the table file */
DbbeCursor *pCursr; /* The new table cursor */
BeFile *pFile; /* The underlying data file for this table */
int rc = SQLITE_OK; /* Return value */
int open_flags; /* Flags passed to dbopen() */
Dbbex *pBe = (Dbbex*)pDbbe;
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
if( zTable ){
zFile = sqliteFileOfTable(pBe, zTable);
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
if( strcmp(pFile->zName,zFile)==0 ) break;
}
}else{
pFile = 0;
zFile = 0;
}
if( pFile==0 ){
if( writeable ){
open_flags = O_RDWR|O_CREAT
}else{
open_flags = O_RDONLY;
}
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return SQLITE_NOMEM;
}
if( zFile ){
if( !writeable || pBe->write ){
pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 0);
}else{
pFile->dbf = 0;
}
}else{
int limit;
char zRandom[50];
zFile = 0;
limit = 5;
do {
sqliteRandomName(zRandom, "_temp_table_");
sqliteFree(zFile);
zFile = sqliteFileOfTable(pBe, zRandom);
pFile->dbf = dbopen(zFile, open_flags, DB_HASH, 0);
}while( pFile->dbf==0 && limit-- >= 0);
pFile->delOnClose = 1;
}
pFile->writeable = writeable;
pFile->zName = zFile;
pFile->nRef = 1;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
}
pFile->pCursor = 0;
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
if( pFile->dbf==0 ){
if( !writeable && access(zFile,0) ){
/* Trying to read a non-existant file. This is OK. All the
** reads will return empty, which is what we want. */
rc = SQLITE_OK;
}else if( pBe->write==0 ){
rc = SQLITE_READONLY;
}else if( access(zFile,W_OK|R_OK) ){
rc = SQLITE_PERM;
}else{
rc = SQLITE_BUSY;
}
}
}else{
sqliteFree(zFile);
pFile->nRef++;
if( writeable && !pFile->writeable ){
rc = SQLITE_READONLY;
}
}
pCursr->pBe = pBe;
pCursr->pFile = pFile;
pCursr->readPending = 0;
pCursr->needRewind = 1;
if( rc!=SQLITE_OK ){
sqliteBdb1CloseCursor(pCursr);
*ppCursr = 0;
}else{
*ppCursr = pCursr;
}
return rc;
}
/*
** Drop a table from the database. The file on the disk that corresponds
** to this table is deleted.
*/
static void sqliteBdb1DropTable(Dbbe *pBe, const char *zTable){
char *zFile; /* Name of the table file */
zFile = sqliteFileOfTable((Dbbex*)pBe, zTable);
unlink(zFile);
sqliteFree(zFile);
}
/*
** Close a cursor previously opened by sqliteBdb1OpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed. This routine decrements the BeFile.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
static void sqliteBdb1CloseCursor(DbbeCursor *pCursr){
BeFile *pFile;
Dbbex *pBe;
if( pCursr==0 ) return;
pFile = pCursr->pFile;
pBe = pCursr->pBe;
if( pFile->pCursor==pCursr ){
pFile->pCursor = 0;
}
pFile->nRef--;
if( pFile->dbf!=NULL ){
(*pFile->dbf->sync)(pFile->dbf, 0);
}
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
(*pFile->dbf->close)(pFile->dbf);
}
if( pFile->pPrev ){
pFile->pPrev->pNext = pFile->pNext;
}else{
pBe->pOpen = pFile->pNext;
}
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
if( pFile->delOnClose ){
unlink(pFile->zName);
}
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
if( pCursr->key.dptr ) free(pCursr->key.dptr); ######
if( pCursr->data.dptr ) free(pCursr->data.dptr); ######
memset(pCursr, 0, sizeof(*pCursr));
sqliteFree(pCursr);
}
/*
** Reorganize a table to reduce search times and disk usage.
*/
static int sqliteBdb1ReorganizeTable(Dbbe *pBe, const char *zTable){
/* No-op */
return SQLITE_OK;
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr); ########
p->data = 0;
p->size = 0;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
static int sqliteBdb1Fetch(DbbeCursor *pCursr, int nKey, char *pKey){
DBT key;
key.size = nKey;
key.data = pKey;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
}
return pCursr->data.dptr!=0;
}
/*
** Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
static int sqliteBdb1Test(DbbeCursor *pCursr, int nKey, char *pKey){
DBT key;
int result = 0;
key.dsize = nKey;
key.dptr = pKey;
if( pCursr->pFile && pCursr->pFile->dbf ){
result = gdbm_exists(pCursr->pFile->dbf, key);
}
return result;
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
static
int sqliteBdb1CopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( offset>=pCursr->key.dsize ) return 0;
if( offset+size>pCursr->key.dsize ){
n = pCursr->key.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->key.dptr[offset], n);
return n;
}
static
int sqliteBdb1CopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset>=pCursr->data.dsize ) return 0;
if( offset+size>pCursr->data.dsize ){
n = pCursr->data.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->data.dptr[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
static char *sqliteBdb1ReadKey(DbbeCursor *pCursr, int offset){
if( offset<0 || offset>=pCursr->key.dsize ) return "";
return &pCursr->key.dptr[offset];
}
static char *sqliteBdb1ReadData(DbbeCursor *pCursr, int offset){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset<0 || offset>=pCursr->data.dsize ) return "";
return &pCursr->data.dptr[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
static int sqliteBdb1KeyLength(DbbeCursor *pCursr){
return pCursr->key.dsize;
}
static int sqliteBdb1DataLength(DbbeCursor *pCursr){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
return pCursr->data.dsize;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
static int sqliteBdb1Rewind(DbbeCursor *pCursr){
pCursr->needRewind = 1;
return SQLITE_OK;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
static int sqliteBdb1NextKey(DbbeCursor *pCursr){
DBT nextkey;
int rc;
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
pCursr->readPending = 0;
return 0;
}
if( pCursr->needRewind ){
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
pCursr->needRewind = 0;
}else{
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
}
datumClear(&pCursr->key);
datumClear(&pCursr->data);
pCursr->key = nextkey;
if( pCursr->key.dptr ){
pCursr->readPending = 1;
rc = 1;
}else{
pCursr->needRewind = 1;
pCursr->readPending = 0;
rc = 0;
}
return rc;
}
/*
** Get a new integer key.
*/
static int sqliteBdb1New(DbbeCursor *pCursr){
int iKey;
DBT key;
int go = 1;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
while( go ){
iKey = sqliteRandomInteger();
if( iKey==0 ) continue;
key.dptr = (char*)&iKey;
key.dsize = 4;
go = gdbm_exists(pCursr->pFile->dbf, key);
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
static int sqliteBdb1Put(
DbbeCursor *pCursr, /* Write to the database associated with this cursor */
int nKey, /* Number of bytes in the key */
char *pKey, /* The data for the key */
int nData, /* Number of bytes of data */
char *pData /* The data */
){
DBT data, key;
int rc;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
data.dsize = nData;
data.dptr = pData;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
if( rc ) rc = SQLITE_ERROR;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
return rc;
}
/*
** Remove an entry from a table, if the entry exists.
*/
static int sqliteBdb1Delete(DbbeCursor *pCursr, int nKey, char *pKey){
DBT key;
int rc;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_delete(pCursr->pFile->dbf, key);
if( rc ) rc = SQLITE_ERROR;
return rc;
}
/*
** Open a temporary file. The file is located in the same directory
** as the rest of the database.
*/
static int sqliteBdb1OpenTempFile(Dbbe *pDbbe, FILE **ppFile){
Dbbex *pBe = (Dbbex*)pDbbe;
return sqliteDbbeOpenTempFile(pBe->zDir, pDbbe, ppFile);
}
/*
** This variable contains pointers to all of the access methods
** used to implement the GDBM backend.
*/
static struct DbbeMethods gdbmMethods = {
/* n Close */ sqliteBdb1Close,
/* OpenCursor */ sqliteBdb1OpenCursor,
/* DropTable */ sqliteBdb1DropTable,
/* ReorganizeTable */ sqliteBdb1ReorganizeTable,
/* CloseCursor */ sqliteBdb1CloseCursor,
/* Fetch */ sqliteBdb1Fetch,
/* Test */ sqliteBdb1Test,
/* CopyKey */ sqliteBdb1CopyKey,
/* CopyData */ sqliteBdb1CopyData,
/* ReadKey */ sqliteBdb1ReadKey,
/* ReadData */ sqliteBdb1ReadData,
/* KeyLength */ sqliteBdb1KeyLength,
/* DataLength */ sqliteBdb1DataLength,
/* NextKey */ sqliteBdb1NextKey,
/* Rewind */ sqliteBdb1Rewind,
/* New */ sqliteBdb1New,
/* Put */ sqliteBdb1Put,
/* Delete */ sqliteBdb1Delete,
/* OpenTempFile */ sqliteBdb1OpenTempFile,
/* CloseTempFile */ sqliteDbbeCloseTempFile
};
/*
** This routine opens a new database. For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteBdb1Open(
const char *zName, /* The name of the database */
int writeFlag, /* True if we will be writing to the database */
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbex *pNew;
struct stat statbuf;
char *zMaster;
if( !writeFlag ) createFlag = 0;
if( stat(zName, &statbuf)!=0 ){
if( createFlag ) mkdir(zName, 0750);
if( stat(zName, &statbuf)!=0 ){
sqliteSetString(pzErrMsg, createFlag ?
"can't find or create directory \"" : "can't find directory \"",
zName, "\"", 0);
return 0;
}
}
if( !S_ISDIR(statbuf.st_mode) ){
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
return 0;
}
if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
sqliteSetString(pzErrMsg, "access permission denied", 0);
return 0;
}
zMaster = 0;
sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
if( stat(zMaster, &statbuf)==0
&& access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
sqliteFree(zMaster);
return 0;
}
sqliteFree(zMaster);
pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1);
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
pNew->dbbe.x = &gdbmMethods;
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
return &pNew->dbbe;
}

776
src/ex/dbbemird.c Normal file
View 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: dbbemird.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#include "sqliteInt.h"
#include <gdbm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
/*
** Information about each open disk file is an instance of this
** structure. There will only be one such structure for each
** disk file. If the VDBE opens the same file twice (as will happen
** for a self-join, for example) then two DbbeCursor structures are
** created but there is only a single BeFile structure with an
** nRef of 2.
*/
typedef struct BeFile BeFile;
struct BeFile {
char *zName; /* Name of the file */
GDBM_FILE dbf; /* The file itself */
int nRef; /* Number of references */
int delOnClose; /* Delete when closing */
int writeable; /* Opened for writing */
BeFile *pNext, *pPrev; /* Next and previous on list of open files */
};
/*
** The following structure holds the current state of the RC4 algorithm.
** We use RC4 as a random number generator. Each call to RC4 gives
** a random 8-bit number.
**
** Nothing in this file or anywhere else in SQLite does any kind of
** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
** number generator) not as an encryption device.
*/
struct rc4 {
int i, j;
int s[256];
};
/*
** The following structure contains all information used by GDBM
** database driver. This is a subclass of the Dbbe structure.
*/
typedef struct Dbbex Dbbex;
struct Dbbex {
Dbbe dbbe; /* The base class */
char *zDir; /* The directory containing the database */
int write; /* True for write permission */
BeFile *pOpen; /* List of open files */
int nTemp; /* Number of temporary files created */
FILE **apTemp; /* Space to hold temporary file pointers */
char **azTemp; /* Names of the temporary files */
struct rc4 rc4; /* The random number generator */
};
/*
** An cursor into a database file is an instance of the following structure.
** There can only be a single BeFile structure for each disk file, but
** there can be multiple DbbeCursor structures. Each DbbeCursor represents
** a cursor pointing to a particular part of the open BeFile. The
** BeFile.nRef field hold a count of the number of DbbeCursor structures
** associated with the same disk file.
*/
struct DbbeCursor {
Dbbex *pBe; /* The database of which this record is a part */
BeFile *pFile; /* The database file for this table */
datum key; /* Most recently used key */
datum data; /* Most recent data */
int needRewind; /* Next key should be the first */
int readPending; /* The fetch hasn't actually been done yet */
};
/*
** Initialize the RC4 PRNG. "seed" is a pointer to some random
** data used to initialize the PRNG.
*/
static void rc4init(struct rc4 *p, char *seed, int seedlen){
int i;
char k[256];
p->j = 0;
p->i = 0;
for(i=0; i<256; i++){
p->s[i] = i;
k[i] = seed[i%seedlen];
}
for(i=0; i<256; i++){
int t;
p->j = (p->j + p->s[i] + k[i]) & 0xff;
t = p->s[p->j];
p->s[p->j] = p->s[i];
p->s[i] = t;
}
}
/*
** Get a single 8-bit random value from the RC4 PRNG.
*/
static int rc4byte(struct rc4 *p){
int t;
p->i = (p->i + 1) & 0xff;
p->j = (p->j + p->s[p->i]) & 0xff;
t = p->s[p->i];
p->s[p->i] = p->s[p->j];
p->s[p->j] = t;
t = p->s[p->i] + p->s[p->j];
return t & 0xff;
}
/*
** The "mkdir()" function only takes one argument under Windows.
*/
#if OS_WIN
# define mkdir(A,B) mkdir(A)
#endif
/*
** Forward declaration
*/
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr);
/*
** Completely shutdown the given database. Close all files. Free all memory.
*/
static void sqliteGdbmClose(Dbbe *pDbbe){
Dbbex *pBe = (Dbbex*)pDbbe;
BeFile *pFile, *pNext;
int i;
for(pFile=pBe->pOpen; pFile; pFile=pNext){
pNext = pFile->pNext;
gdbm_close(pFile->dbf);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]!=0 ){
unlink(pBe->azTemp[i]);
fclose(pBe->apTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
sqliteFree(pBe->azTemp);
sqliteFree(pBe->apTemp);
memset(pBe, 0, sizeof(*pBe));
sqliteFree(pBe);
}
/*
** Translate the name of an SQL table (or index) into the name
** of a file that holds the key/data pairs for that table or
** index. Space to hold the filename is obtained from
** sqliteMalloc() and must be freed by the calling function.
*/
static char *sqliteFileOfTable(Dbbex *pBe, const char *zTable){
char *zFile = 0;
int i;
sqliteSetString(&zFile, pBe->zDir, "/", zTable, ".tbl", 0);
if( zFile==0 ) return 0;
for(i=strlen(pBe->zDir)+1; zFile[i]; i++){
int c = zFile[i];
if( isupper(c) ){
zFile[i] = tolower(c);
}else if( !isalnum(c) && c!='-' && c!='_' && c!='.' ){
zFile[i] = '+';
}
}
return zFile;
}
/*
** Generate a random filename with the given prefix. The new filename
** is written into zBuf[]. The calling function must insure that
** zBuf[] is big enough to hold the prefix plus 20 or so extra
** characters.
**
** Very random names are chosen so that the chance of a
** collision with an existing filename is very very small.
*/
static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){
int i, j;
static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
strcpy(zBuf, zPrefix);
j = strlen(zBuf);
for(i=0; i<15; i++){
int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1);
zBuf[j++] = zRandomChars[c];
}
zBuf[j] = 0;
}
/*
** Open a new table cursor. Write a pointer to the corresponding
** DbbeCursor structure into *ppCursr. Return an integer success
** code:
**
** SQLITE_OK It worked!
**
** SQLITE_NOMEM sqliteMalloc() failed
**
** SQLITE_PERM Attempt to access a file for which file
** access permission is denied
**
** SQLITE_BUSY Another thread or process is already using
** the corresponding file and has that file locked.
**
** SQLITE_READONLY The current thread already has this file open
** readonly but you are trying to open for writing.
** (This can happen if a SELECT callback tries to
** do an UPDATE or DELETE.)
**
** If zTable is 0 or "", then a temporary database file is created and
** a cursor to that temporary file is opened. The temporary file
** will be deleted from the disk when it is closed.
*/
static int sqliteGdbmOpenCursor(
Dbbe *pDbbe, /* The database the table belongs to */
const char *zTable, /* The SQL name of the file to be opened */
int writeable, /* True to open for writing */
DbbeCursor **ppCursr /* Write the resulting table pointer here */
){
char *zFile; /* Name of the table file */
DbbeCursor *pCursr; /* The new table cursor */
BeFile *pFile; /* The underlying data file for this table */
int rc = SQLITE_OK; /* Return value */
int rw_mask; /* Permissions mask for opening a table */
int mode; /* Mode for opening a table */
Dbbex *pBe = (Dbbex*)pDbbe;
*ppCursr = 0;
pCursr = sqliteMalloc( sizeof(*pCursr) );
if( pCursr==0 ) return SQLITE_NOMEM;
if( zTable ){
zFile = sqliteFileOfTable(pBe, zTable);
for(pFile=pBe->pOpen; pFile; pFile=pFile->pNext){
if( strcmp(pFile->zName,zFile)==0 ) break;
}
}else{
pFile = 0;
zFile = 0;
}
if( pFile==0 ){
if( writeable ){
rw_mask = GDBM_WRCREAT | GDBM_FAST;
mode = 0640;
}else{
rw_mask = GDBM_READER;
mode = 0640;
}
pFile = sqliteMalloc( sizeof(*pFile) );
if( pFile==0 ){
sqliteFree(zFile);
return SQLITE_NOMEM;
}
if( zFile ){
if( !writeable || pBe->write ){
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}else{
pFile->dbf = 0;
}
}else{
int limit;
struct rc4 *pRc4;
char zRandom[50];
pRc4 = &pBe->rc4;
zFile = 0;
limit = 5;
do {
randomName(&pBe->rc4, zRandom, "_temp_table_");
sqliteFree(zFile);
zFile = sqliteFileOfTable(pBe, zRandom);
pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0);
}while( pFile->dbf==0 && limit-- >= 0);
pFile->delOnClose = 1;
}
pFile->writeable = writeable;
pFile->zName = zFile;
pFile->nRef = 1;
pFile->pPrev = 0;
if( pBe->pOpen ){
pBe->pOpen->pPrev = pFile;
}
pFile->pNext = pBe->pOpen;
pBe->pOpen = pFile;
if( pFile->dbf==0 ){
if( !writeable && access(zFile,0) ){
/* Trying to read a non-existant file. This is OK. All the
** reads will return empty, which is what we want. */
rc = SQLITE_OK;
}else if( pBe->write==0 ){
rc = SQLITE_READONLY;
}else if( access(zFile,W_OK|R_OK) ){
rc = SQLITE_PERM;
}else{
rc = SQLITE_BUSY;
}
}
}else{
sqliteFree(zFile);
pFile->nRef++;
if( writeable && !pFile->writeable ){
rc = SQLITE_READONLY;
}
}
pCursr->pBe = pBe;
pCursr->pFile = pFile;
pCursr->readPending = 0;
pCursr->needRewind = 1;
if( rc!=SQLITE_OK ){
sqliteGdbmCloseCursor(pCursr);
*ppCursr = 0;
}else{
*ppCursr = pCursr;
}
return rc;
}
/*
** Drop a table from the database. The file on the disk that corresponds
** to this table is deleted.
*/
static void sqliteGdbmDropTable(Dbbe *pBe, const char *zTable){
char *zFile; /* Name of the table file */
zFile = sqliteFileOfTable((Dbbex*)pBe, zTable);
unlink(zFile);
sqliteFree(zFile);
}
/*
** Close a cursor previously opened by sqliteGdbmOpenCursor().
**
** There can be multiple cursors pointing to the same open file.
** The underlying file is not closed until all cursors have been
** closed. This routine decrements the BeFile.nref field of the
** underlying file and closes the file when nref reaches 0.
*/
static void sqliteGdbmCloseCursor(DbbeCursor *pCursr){
BeFile *pFile;
Dbbex *pBe;
if( pCursr==0 ) return;
pFile = pCursr->pFile;
pBe = pCursr->pBe;
pFile->nRef--;
if( pFile->dbf!=NULL ){
gdbm_sync(pFile->dbf);
}
if( pFile->nRef<=0 ){
if( pFile->dbf!=NULL ){
gdbm_close(pFile->dbf);
}
if( pFile->pPrev ){
pFile->pPrev->pNext = pFile->pNext;
}else{
pBe->pOpen = pFile->pNext;
}
if( pFile->pNext ){
pFile->pNext->pPrev = pFile->pPrev;
}
if( pFile->delOnClose ){
unlink(pFile->zName);
}
sqliteFree(pFile->zName);
memset(pFile, 0, sizeof(*pFile));
sqliteFree(pFile);
}
if( pCursr->key.dptr ) free(pCursr->key.dptr);
if( pCursr->data.dptr ) free(pCursr->data.dptr);
memset(pCursr, 0, sizeof(*pCursr));
sqliteFree(pCursr);
}
/*
** Reorganize a table to reduce search times and disk usage.
*/
static int sqliteGdbmReorganizeTable(Dbbe *pBe, const char *zTable){
DbbeCursor *pCrsr;
int rc;
rc = sqliteGdbmOpenCursor(pBe, zTable, 1, &pCrsr);
if( rc!=SQLITE_OK ){
return rc;
}
if( pCrsr && pCrsr->pFile && pCrsr->pFile->dbf ){
gdbm_reorganize(pCrsr->pFile->dbf);
}
if( pCrsr ){
sqliteGdbmCloseCursor(pCrsr);
}
return SQLITE_OK;
}
/*
** Clear the given datum
*/
static void datumClear(datum *p){
if( p->dptr ) free(p->dptr);
p->dptr = 0;
p->dsize = 0;
}
/*
** Fetch a single record from an open cursor. Return 1 on success
** and 0 on failure.
*/
static int sqliteGdbmFetch(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
key.dsize = nKey;
key.dptr = pKey;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, key);
}
return pCursr->data.dptr!=0;
}
/*
** Return 1 if the given key is already in the table. Return 0
** if it is not.
*/
static int sqliteGdbmTest(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int result = 0;
key.dsize = nKey;
key.dptr = pKey;
if( pCursr->pFile && pCursr->pFile->dbf ){
result = gdbm_exists(pCursr->pFile->dbf, key);
}
return result;
}
/*
** Copy bytes from the current key or data into a buffer supplied by
** the calling function. Return the number of bytes copied.
*/
static
int sqliteGdbmCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( offset>=pCursr->key.dsize ) return 0;
if( offset+size>pCursr->key.dsize ){
n = pCursr->key.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->key.dptr[offset], n);
return n;
}
static
int sqliteGdbmCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){
int n;
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset>=pCursr->data.dsize ) return 0;
if( offset+size>pCursr->data.dsize ){
n = pCursr->data.dsize - offset;
}else{
n = size;
}
memcpy(zBuf, &pCursr->data.dptr[offset], n);
return n;
}
/*
** Return a pointer to bytes from the key or data. The data returned
** is ephemeral.
*/
static char *sqliteGdbmReadKey(DbbeCursor *pCursr, int offset){
if( offset<0 || offset>=pCursr->key.dsize ) return "";
return &pCursr->key.dptr[offset];
}
static char *sqliteGdbmReadData(DbbeCursor *pCursr, int offset){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
if( offset<0 || offset>=pCursr->data.dsize ) return "";
return &pCursr->data.dptr[offset];
}
/*
** Return the total number of bytes in either data or key.
*/
static int sqliteGdbmKeyLength(DbbeCursor *pCursr){
return pCursr->key.dsize;
}
static int sqliteGdbmDataLength(DbbeCursor *pCursr){
if( pCursr->readPending && pCursr->pFile && pCursr->pFile->dbf ){
pCursr->data = gdbm_fetch(pCursr->pFile->dbf, pCursr->key);
pCursr->readPending = 0;
}
return pCursr->data.dsize;
}
/*
** Make is so that the next call to sqliteNextKey() finds the first
** key of the table.
*/
static int sqliteGdbmRewind(DbbeCursor *pCursr){
pCursr->needRewind = 1;
return SQLITE_OK;
}
/*
** Read the next key from the table. Return 1 on success. Return
** 0 if there are no more keys.
*/
static int sqliteGdbmNextKey(DbbeCursor *pCursr){
datum nextkey;
int rc;
if( pCursr==0 || pCursr->pFile==0 || pCursr->pFile->dbf==0 ){
pCursr->readPending = 0;
return 0;
}
if( pCursr->needRewind ){
nextkey = gdbm_firstkey(pCursr->pFile->dbf);
pCursr->needRewind = 0;
}else{
nextkey = gdbm_nextkey(pCursr->pFile->dbf, pCursr->key);
}
datumClear(&pCursr->key);
datumClear(&pCursr->data);
pCursr->key = nextkey;
if( pCursr->key.dptr ){
pCursr->readPending = 1;
rc = 1;
}else{
pCursr->needRewind = 1;
pCursr->readPending = 0;
rc = 0;
}
return rc;
}
/*
** Get a new integer key.
*/
static int sqliteGdbmNew(DbbeCursor *pCursr){
int iKey;
datum key;
int go = 1;
int i;
struct rc4 *pRc4;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1;
pRc4 = &pCursr->pBe->rc4;
while( go ){
iKey = 0;
for(i=0; i<4; i++){
iKey = (iKey<<8) + rc4byte(pRc4);
}
if( iKey==0 ) continue;
key.dptr = (char*)&iKey;
key.dsize = 4;
go = gdbm_exists(pCursr->pFile->dbf, key);
}
return iKey;
}
/*
** Write an entry into the table. Overwrite any prior entry with the
** same key.
*/
static int
sqliteGdbmPut(DbbeCursor *pCursr, int nKey,char *pKey,int nData,char *pData){
datum data, key;
int rc;
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
data.dsize = nData;
data.dptr = pData;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_store(pCursr->pFile->dbf, key, data, GDBM_REPLACE);
if( rc ) rc = SQLITE_ERROR;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
return rc;
}
/*
** Remove an entry from a table, if the entry exists.
*/
static int sqliteGdbmDelete(DbbeCursor *pCursr, int nKey, char *pKey){
datum key;
int rc;
datumClear(&pCursr->key);
datumClear(&pCursr->data);
if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return SQLITE_ERROR;
key.dsize = nKey;
key.dptr = pKey;
rc = gdbm_delete(pCursr->pFile->dbf, key);
if( rc ) rc = SQLITE_ERROR;
return rc;
}
/*
** Open a temporary file. The file should be deleted when closed.
**
** Note that we can't use the old Unix trick of opening the file
** and then immediately unlinking the file. That works great
** under Unix, but fails when we try to port to Windows.
*/
static int sqliteGdbmOpenTempFile(Dbbe *pDbbe, FILE **ppFile){
char *zFile; /* Full name of the temporary file */
char zBuf[50]; /* Base name of the temporary file */
int i; /* Loop counter */
int limit; /* Prevent an infinite loop */
int rc = SQLITE_OK; /* Value returned by this function */
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==0 ) break;
}
if( i>=pBe->nTemp ){
pBe->nTemp++;
pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) );
pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) );
}
if( pBe->apTemp==0 ){
*ppFile = 0;
return SQLITE_NOMEM;
}
limit = 4;
zFile = 0;
do{
randomName(&pBe->rc4, zBuf, "/_temp_file_");
sqliteFree(zFile);
zFile = 0;
sqliteSetString(&zFile, pBe->zDir, zBuf, 0);
}while( access(zFile,0)==0 && limit-- >= 0 );
*ppFile = pBe->apTemp[i] = fopen(zFile, "w+");
if( pBe->apTemp[i]==0 ){
rc = SQLITE_ERROR;
sqliteFree(zFile);
pBe->azTemp[i] = 0;
}else{
pBe->azTemp[i] = zFile;
}
return rc;
}
/*
** Close a temporary file opened using sqliteGdbmOpenTempFile()
*/
static void sqliteGdbmCloseTempFile(Dbbe *pDbbe, FILE *f){
int i;
Dbbex *pBe = (Dbbex*)pDbbe;
for(i=0; i<pBe->nTemp; i++){
if( pBe->apTemp[i]==f ){
unlink(pBe->azTemp[i]);
sqliteFree(pBe->azTemp[i]);
pBe->apTemp[i] = 0;
pBe->azTemp[i] = 0;
break;
}
}
fclose(f);
}
/*
** This routine opens a new database. For the GDBM driver
** implemented here, the database name is the name of the directory
** containing all the files of the database.
**
** If successful, a pointer to the Dbbe structure is returned.
** If there are errors, an appropriate error message is left
** in *pzErrMsg and NULL is returned.
*/
Dbbe *sqliteGdbmOpen(
const char *zName, /* The name of the database */
int writeFlag, /* True if we will be writing to the database */
int createFlag, /* True to create database if it doesn't exist */
char **pzErrMsg /* Write error messages (if any) here */
){
Dbbex *pNew;
struct stat statbuf;
char *zMaster;
if( !writeFlag ) createFlag = 0;
if( stat(zName, &statbuf)!=0 ){
if( createFlag ) mkdir(zName, 0750);
if( stat(zName, &statbuf)!=0 ){
sqliteSetString(pzErrMsg, createFlag ?
"can't find or create directory \"" : "can't find directory \"",
zName, "\"", 0);
return 0;
}
}
if( !S_ISDIR(statbuf.st_mode) ){
sqliteSetString(pzErrMsg, "not a directory: \"", zName, "\"", 0);
return 0;
}
if( access(zName, writeFlag ? (X_OK|W_OK|R_OK) : (X_OK|R_OK)) ){
sqliteSetString(pzErrMsg, "access permission denied", 0);
return 0;
}
zMaster = 0;
sqliteSetString(&zMaster, zName, "/" MASTER_NAME ".tbl", 0);
if( stat(zMaster, &statbuf)==0
&& access(zMaster, writeFlag ? (W_OK|R_OK) : R_OK)!=0 ){
sqliteSetString(pzErrMsg, "access permission denied for ", zMaster, 0);
sqliteFree(zMaster);
return 0;
}
sqliteFree(zMaster);
pNew = sqliteMalloc(sizeof(Dbbex) + strlen(zName) + 1);
if( pNew==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 0;
}
pNew->dbbe.Close = sqliteGdbmClose;
pNew->dbbe.OpenCursor = sqliteGdbmOpenCursor;
pNew->dbbe.DropTable = sqliteGdbmDropTable;
pNew->dbbe.ReorganizeTable = sqliteGdbmReorganizeTable;
pNew->dbbe.CloseCursor = sqliteGdbmCloseCursor;
pNew->dbbe.Fetch = sqliteGdbmFetch;
pNew->dbbe.Test = sqliteGdbmTest;
pNew->dbbe.CopyKey = sqliteGdbmCopyKey;
pNew->dbbe.CopyData = sqliteGdbmCopyData;
pNew->dbbe.ReadKey = sqliteGdbmReadKey;
pNew->dbbe.ReadData = sqliteGdbmReadData;
pNew->dbbe.KeyLength = sqliteGdbmKeyLength;
pNew->dbbe.DataLength = sqliteGdbmDataLength;
pNew->dbbe.NextKey = sqliteGdbmNextKey;
pNew->dbbe.Rewind = sqliteGdbmRewind;
pNew->dbbe.New = sqliteGdbmNew;
pNew->dbbe.Put = sqliteGdbmPut;
pNew->dbbe.Delete = sqliteGdbmDelete;
pNew->dbbe.OpenTempFile = sqliteGdbmOpenTempFile;
pNew->dbbe.CloseTempFile = sqliteGdbmCloseTempFile;
pNew->zDir = (char*)&pNew[1];
strcpy(pNew->zDir, zName);
pNew->write = writeFlag;
pNew->pOpen = 0;
time(&statbuf.st_ctime);
rc4init(&pNew->rc4, (char*)&statbuf, sizeof(statbuf));
return &pNew->dbbe;
}

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/
**
*************************************************************************
** $Id: pg.c,v 1.4 2001/01/25 01:45:41 drh Exp $
** $Id: pg.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#include <assert.h>
#include <sys/types.h>

View File

@ -21,7 +21,7 @@
** http://www.hwaci.com/drh/
**
*************************************************************************
** $Id: pg.h,v 1.3 2001/01/21 00:58:09 drh Exp $
** $Id: pg.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Pgr Pgr;

View File

@ -26,7 +26,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.24 2001/01/15 22:51:11 drh Exp $
** $Id: main.c,v 1.25 2001/02/11 16:56:24 drh Exp $
*/
#include "sqliteInt.h"
#include <unistd.h>
@ -124,7 +124,7 @@ static int sqliteInit(sqlite *db, char **pzErrMsg){
** database scheme.
*/
static VdbeOp initProg[] = {
{ OP_OpenTbl, 0, 0, MASTER_NAME},
{ OP_OpenTbl, 0, 0, MASTER_NAME},
{ OP_Next, 0, 9, 0}, /* 1 */
{ OP_Field, 0, 0, 0},
{ OP_String, 0, 0, "meta"},

View File

@ -27,7 +27,7 @@
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
**
** $Id: tokenize.c,v 1.16 2000/12/10 18:23:51 drh Exp $
** $Id: tokenize.c,v 1.17 2001/02/11 16:56:24 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@ -308,7 +308,7 @@ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
static FILE *trace = 0;
extern void *sqliteParserAlloc(void*(*)(int));
extern void sqliteParserFree(void*, void(*)(void*));
extern int sqliteParser(void*, int, ...);
extern int sqliteParser(void*, int, Token, Parse*);
extern void sqliteParserTrace(FILE*, char *);
pParse->db->flags &= ~SQLITE_Interrupt;

View File

@ -17,6 +17,17 @@ proc chng {date desc} {
puts "<DD><P><UL>$desc</UL></P></DD>"
}
chng {2001 Feb 11 (1.0.20)} {
<li>Merge development changes into the main trunk. Future work toward
using a BTree file structure will use a separate CVS source tree. This
CVS tree will continue to support the GDBM version of SQLite only.</li>
}
chng {2001 Feb 6 (1.0.19)} {
<li>Fix a strange (but valid) C declaration that was causing problems
for QNX. No logical changes.</li>
}
chng {2001 Jan 4 (1.0.18)} {
<li>Print the offending SQL statement when an error occurs.</li>
<li>Do not require commas between constraints in CREATE TABLE statements.</li>