1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Removing some surplus files. (CVS 1723)

FossilOrigin-Name: 8ad996fdac6801768e94ca1710a0a3da03e1e7ea
This commit is contained in:
drh
2001-09-15 00:59:33 +00:00
parent 3543b3e0ba
commit b3e05a662a
10 changed files with 6 additions and 3752 deletions

View File

@ -1,5 +1,5 @@
C Release\s2.0-alpha-1\s(CVS\s247)
D 2001-09-15T00:58:00
C Removing\ssome\ssurplus\sfiles.\s(CVS\s1723)
D 2001-09-15T00:59:33
F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4
F Makefile.in 6f4536369ce59b0ee7941f743c65c2c7f703ad2a
F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958
@ -17,14 +17,6 @@ F src/btree.c 3adf545b8e072000923f7e0f7f91d33072a9f869
F src/btree.h a3d9c20fa876e837680745ac60500be697026b7b
F src/build.c 8359e553db8138d09f44957e2d1bcc9b8720117b
F src/delete.c c84b5a26e29fda3c3de51345073a76bb161271fd
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/ex/sizes.tcl f54bad4a2ac567624be59131a6ee42d71b41a3d7
F src/expr.c bcd91d0487c71cfa44413a46efe5e2c2244901b6
F src/insert.c 750a44c0d205779b2c42b0791a163937cfb00e74
F src/main.c 73be8d00a8a9bbec715a6260840a19020a074090
@ -105,7 +97,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f
F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f
F www/tclsqlite.tcl 06f81c401f79a04f2c5ebfb97e7c176225c0aef2
F www/vdbe.tcl 0c8aaa529dd216ccbf7daaabd80985e413d5f9ad
P 14474fa144fe7c5dc63e0990d6cc92d769e6013e
R e56932550882bb08b5533a7aa309b70c
P 264f23315e682909abb47912f48733f641772a4c
R b059993507a776d66a3417f913643af9
U drh
Z a235c7cc967e6bb6f207b8f3132939fd
Z 6c5da555882197ba94055adde72070fe

View File

@ -1 +1 @@
264f23315e682909abb47912f48733f641772a4c
8ad996fdac6801768e94ca1710a0a3da03e1e7ea

View File

@ -1,9 +0,0 @@
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.

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: db.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Db Db;
typedef struct DbCursor DbCursor;
int sqliteDbOpen(const char *filename, Db**);
int sqliteDbClose(Db*);
int sqliteDbBeginTransaction(Db*);
int sqliteDbCommit(Db*);
int sqliteDbRollback(Db*);
int sqliteDbCreateTable(Db*, int *pTblno);
int sqliteDbDropTable(Db*, int tblno);
int sqliteDbCursorOpen(Db*, int tblno, DbCursor**);
int sqliteDbCursorClose(DbCursor*);
int sqliteDbCursorFirst(DbCursor*);
int sqliteDbCursorNext(DbCursor*);
int sqliteDbCursorDatasize(DbCursor*);
int sqliteDbCursorKeysize(DbCursor*);
int sqliteDbCursorRead(DbCursor*, int amt, int offset, void *buf);
int sqliteDbCursorReadKey(DbCursor*, int amt, int offset, void *buf);
int sqliteDbCursorWrite(DbCursor*, int amt, int offset, const void *buf);
int sqliteDbCursorFind(DbCursor*, int nKey, const void *pKey, int createFlag);
int sqliteDbCursorResize(DbCursor*, int nData);

View File

@ -1,618 +0,0 @@
/*
** 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;
}

View File

@ -1,776 +0,0 @@
/*
** 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

@ -1,708 +0,0 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: pg.c,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "sqliteInt.h"
#include "pg.h"
/*
** Uncomment the following for a debug trace
*/
#if 1
# define TRACE(X) printf X; fflush(stdout);
#endif
/*
** Hash table sizes
*/
#define J_HASH_SIZE 127 /* Size of the journal page hash table */
#define PG_HASH_SIZE 349 /* Size of the database page hash table */
/*
** Forward declaration of structure
*/
typedef struct Pghdr Pghdr;
/*
** All information about a single paging file is contained in an
** instance of the following structure.
*/
struct Pgr {
int fdMain; /* The main database file */
char *zMain; /* Name of the database file */
int fdJournal; /* The journal file */
char *zJournal; /* Name of the journal file */
int nMemPg; /* Number of memory-resident pages */
int nJPg; /* Number of pages in the journal */
int nDbPg; /* Number of pages in the database */
int nRefPg; /* Number of pages currently in use */
Pghdr *pLru, *pMru; /* Least and most recently used mem-page */
Pghdr *pJidx; /* List of journal index pages */
Pghdr *pAll; /* All pages, except journal index pages */
u32 aJHash[J_HASH_SIZE]; /* Journal page hash table */
Pghdr *aPgHash[PG_HASH_SIZE]; /* Mem-page hash table */
};
/*
** Each memory-resident page of the paging file has a header which
** is an instance of the following structure.
*/
struct Pghdr {
Pgr *p; /* Pointer back to the Pgr structure */
int nRef; /* Number of references to this page */
int isDirty; /* TRUE if needs to be written to disk */
u32 dbpgno; /* Page number in the database file */
u32 jpgno; /* Page number in the journal file */
Pghdr *pNx; /* Next page on a list of them all */
Pghdr *pLru; /* Less recently used pages */
Pghdr *pMru; /* More recently used pages */
Pghdr *pNxHash; /* Next with same dbpgno hash */
Pghdr *pPvHash; /* Previous with the same dbpgno hash */
};
/*
** For a memory-resident page, the page data comes immediately after
** the page header. The following macros can be used to change a
** pointer to a page header into a pointer to the data, or vice
** versa.
*/
#define PG_TO_DATA(X) ((void*)&(X)[1])
#define DATA_TO_PG(X) (&((Pghdr*)(X))[-1])
/*
** The number of in-memory pages that we accumulate before trying
** to reuse older pages when new ones are requested.
*/
#define MX_MEM_PAGE 100
/*
** The number of journal data pages that come between consecutive
** journal index pages.
*/
#define N_J_DATAPAGE (SQLITE_PAGE_SIZE/(2*sizeof(u32)))
/*
** An index page in the journal consists of an array of N_J_DATAPAGE
** of the following structures. There is one instance of the following
** structure for each of the N_J_DATAPAGE data pages that follow the
** index.
**
** Let the journal page number that a JidxEntry describes be J. Then
** the JidxEntry.dbpgno field is the page of the database file that
** corresponds to the J page in the journal. The JidxEntry.next_jpgno
** field hold the number of another journal page that contains
** a database file page with the same hash as JidxEntry.dbpgno.
**
** All information is written to the journal index in big-endian
** notation.
*/
typedef struct JidxEntry JidxEntry;
struct JidxEntry {
char dbpgno[sizeof(u32)]; /* Database page number for this entry */
char next_jpgno[sizeof(u32)]; /* Next entry with same hash on dbpgno */
};
/*
** Read a page from a file into memory. Return SQLITE_OK if successful.
** The "pgno" parameter tells where in the file to read the page.
** The first page is 1. Files do not contain a page 0 since a page
** number of 0 is used to indicate "no such page".
*/
static int sqlitePgRead(int fd, char *zBuf, u32 pgno){
int got = 0;
int amt;
assert( pgno>0 );
assert( fd>=0 );
lseek(fd, SEEK_SET, (pgno-1)*SQLITE_PAGE_SIZE);
while( got<SQLITE_PAGE_SIZE ){
amt = read(fd, &zBuf[got], SQLITE_PAGE_SIZE - got);
if( amt<=0 ){
memset(&zBuf[got], 0, SQLITE_PAGE_SIZE - got);
return amt==0 ? SQLITE_OK : SQLITE_IOERR;
}
got += amt;
}
return SQLITE_OK;
}
/*
** Read a page from a file into memory. Return SQLITE_OK if successful.
** The "pgno" parameter tells where in the file to write the page.
** The first page is 1. Files do not contain a page 0 since a page
** number of 0 is used to indicate "no such page".
*/
static int sqlitePgWrite(int fd, char *zBuf, u32 pgno){
int done = 0;
int amt;
assert( pgno>0 );
assert( fd>=0 );
lseek(fd, SEEK_SET, (pgno-1)*SQLITE_PAGE_SIZE);
while( done<SQLITE_PAGE_SIZE ){
amt = write(fd, &zBuf[done], SQLITE_PAGE_SIZE - done);
if( amt<=0 ) return SQLITE_IOERR;
done += amt;
}
return SQLITE_OK;
}
/*
** Turn four bytes into an integer. The first byte is always the
** most significant 8 bits.
*/
static u32 sqlitePgGetInt(const char *p){
return ((p[0]&0xff)<<24) | ((p[1]&0xff)<<16) | ((p[2]&0xff)<<8) | (p[3]&0xff);
}
/*
** Turn an integer into 4 bytes. The first byte is always the
** most significant 8 bits.
*/
static void sqlitePgPutInt(u32 v, char *p){
p[3] = v & 0xff;
v >>= 8;
p[2] = v & 0xff;
v >>= 8;
p[1] = v & 0xff;
v >>= 8;
p[0] = v & 0xff;
}
/*
** Check the hash table for an in-memory page. Return a pointer to
** the page header if found. Return NULL if the page is not in memory.
*/
static Pghdr *sqlitePgFind(Pgr *p, u32 pgno){
int h;
Pghdr *pPg;
if( pgno==0 ) return 0;
h = pgno % PG_HASH_SIZE;
for(pPg = p->aPgHash[h]; pPg; pPg=pPg->pNxHash){
if( pPg->dbpgno==pgno ) return pPg;
}
TRACE(("PG: data page %u is %#x\n", pgno, (u32)pPg));
return 0;
}
/*
** Locate and return an index page from the journal.
**
** The first page of a journal is the primary index. Additional
** index pages are called secondary indices. Index pages appear
** in the journal as often as needed. (If SQLITE_PAGE_SIZE==1024,
** then there are 1024/sizeof(int)*2 = 128 database between each
** pair of index pages.) Journal index pages are not hashed and
** do no appear on the Pgr.pAll list. Index pages are on the
** Pgr.pJidx list only. Index pages have Pghdr.dbpgno==0.
**
** If the requested index page is not already in memory, then a
** new memory page is created to hold the index.
**
** This routine will return a NULL pointer if we run out of memory.
*/
static Pghdr *sqlitePgFindJidx(Pgr *p, u32 pgno){
Pghdr *pPg;
assert( pgno % (N_J_DATAPAGE+1) == 1 );
for(pPg=p->pJidx; pPg; pPg=pPg->pNx){
if( pPg->jpgno==pgno ){
TRACE(("PG: found j-index %u at %#x\n", pgno, (u32)pPg));
return pPg;
}
}
pPg = sqliteMalloc( sizeof(Pghdr)+SQLITE_PAGE_SIZE );
if( pPg==0 ) return 0;
pPg->jpgno = pgno;
pPg->pNx = p->pJidx;
p->pJidx = pPg;
sqlitePgRead(p->fdJournal, PG_TO_DATA(pPg), pgno);
TRACE(("PG: create j-index %u at %#x\n", pgno, (u32)pPg));
return pPg;
}
/*
** Look in the journal to see if the given database page is stored
** in the journal. If it is, return its journal page number. If
** not, return 0.
*/
static u32 sqlitePgJournalPageNumber(Pgr *p, u32 dbpgno){
u32 jpgno;
if( dbpgno==0 ) return 0;
jpgno = p->aJHash[dbpgno % J_HASH_SIZE];
while( jpgno!=0 ){
int idx_num; /* Which journal index describes page jpgno */
int ipgno; /* Page number for the journal index */
int idx_slot; /* Which entry in index idx_num describes jpgno */
Pghdr *pIdxPg; /* The index page for jpgno */
JidxEntry *aIdx; /* The data for the index page */
idx_num = (jpgno - 1)/(N_J_DATAPAGE + 1);
idx_slot = (jpgno - 1) % (N_J_DATAPAGE + 1) - 2;
ipgno = idx_num * (N_J_DATAPAGE + 1) + 1;
if( ipgno>p->nJPg ){
jpgno = 0;
break;
}
pIdxPg = sqlitePgFindJidx(p, ipgno);
assert( pIdxPg!=0 );
aIdx = PG_TO_DATA(pIdxPg);
if( dbpgno==sqlitePgGetInt(aIdx[idx_slot].dbpgno) ){
break;
}
jpgno = sqlitePgGetInt(aIdx[idx_slot].next_jpgno);
}
return jpgno;
}
/*
** Make a page not dirty by writing it to the journal.
*/
static int sqlitePgMakeClean(Pghdr *pPg){
Pgr *p = pPg->p;
int rc;
assert( pPg->isDirty );
assert( p->fdJournal>=0 );
if( pPg->jpgno==0 ){
int jpgno; /* A newly allocate page in the journal */
int idx_num; /* Which journal index describes page jpgno */
int idx_slot; /* Which entry in index idx_num describes jpgno */
Pghdr *pIdxPg; /* The index page for jpgno */
JidxEntry *aIdx; /* The data for the index page */
int h; /* The hash value for pPg->dbpgno */
jpgno = p->nJPg + 1;
if( jpgno % (N_J_DATAPAGE + 1) == 1 ){
jpgno++;
}
idx_num = (jpgno - 1)/(N_J_DATAPAGE + 1);
idx_slot = (jpgno - 1) % (N_J_DATAPAGE + 1) - 2;
pIdxPg = sqlitePgFindJidx(p, idx_num * (N_J_DATAPAGE + 1) + 1);
assert( pIdxPg!=0 );
aIdx = PG_TO_DATA(pIdxPg);
sqlitePgPutInt(pPg->dbpgno, aIdx[idx_slot].dbpgno);
h = pPg->dbpgno % J_HASH_SIZE;
sqlitePgPutInt(p->aJHash[h], aIdx[idx_slot].next_jpgno);
p->aJHash[h] = jpgno;
p->nJPg = jpgno;
pPg->jpgno = jpgno;
TRACE(("PG: assign d-page %u to j-page %u\n", jpgno, pPg->dbpgno));
}
rc = sqlitePgWrite(p->fdJournal, PG_TO_DATA(pPg), pPg->jpgno);
if( rc==SQLITE_OK ){
pPg->isDirty = 0;
}
return rc;
}
/*
** Find the number of pages in the given file by measuring the size
** of the file. Return 0 if there is any problem.
*/
static int sqlitePgPageCount(int fd){
struct stat statbuf;
if( fstat(fd, &statbuf)!=0 ) return 0;
return statbuf.st_size/SQLITE_PAGE_SIZE;
}
/*
** This routine reads the journal and transfers pages from the
** journal to the database.
*/
static int sqlitePgJournalPlayback(Pgr *p){
Pghdr *pPg;
JidxEntry *aIdx;
int nJpg;
int jpgno = 1;
int i;
int dbpgno;
int rc;
char idx[SQLITE_PAGE_SIZE];
char pgbuf[SQLITE_PAGE_SIZE];
assert( p->fdJournal>=0 );
nJpg = sqlitePgPageCount(p->fdJournal);
while( jpgno<=nJpg ){
if( !sqlitePgRead(p->fdJournal, idx, jpgno++) ) break;
aIdx = (JidxEntry*)idx;
for(i=0; i<N_J_DATAPAGE; i++){
dbpgno = sqlitePgGetInt(&idx[i]);
if( dbpgno==0 ){
jpgno = nJpg+1;
break;
}
pPg = sqlitePgFind(p, dbpgno);
if( pPg ){
rc = sqlitePgWrite(p->fdMain, PG_TO_DATA(pPg), dbpgno);
TRACE(("PG: commit j-page %u to d-page %u from memory\n",jpgno,dbpgno));
}else{
rc = sqlitePgRead(p->fdJournal, pgbuf, jpgno);
if( rc!=SQLITE_OK ){
return rc;
}
rc = sqlitePgWrite(p->fdMain, pgbuf, dbpgno);
TRACE(("PG: commit j-page %u to d-page %u from disk\n",jpgno,dbpgno));
}
jpgno++;
if( rc!=SQLITE_OK ){
return rc;
}
}
}
TRACE(("PG: commit complete. deleting the journal.\n"));
fsync(p->fdMain);
close(p->fdJournal);
p->fdJournal = -1;
unlink(p->zJournal);
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
pPg->isDirty = 0;
pPg->jpgno = 0;
}
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
return SQLITE_OK;
}
/*
** Remove the given page from the LRU list.
*/
static void sqlitePgUnlinkLru(Pghdr *pPg){
Pgr *p = pPg->p;
if( pPg->pLru ){
pPg->pLru->pMru = pPg->pLru;
}
if( pPg->pMru ){
pPg->pMru->pLru = pPg->pMru;
}
if( p->pLru==pPg ){
p->pLru = pPg->pLru;
}
if( p->pMru==pPg ){
p->pMru = pPg->pMru;
}
pPg->pLru = pPg->pMru = 0;
}
/*
** Open the database file and make *ppPgr pointer to a structure describing it.
** Return SQLITE_OK on success or an error code if there is a failure.
**
** If there was an unfinished commit, complete it before returnning.
*/
int sqlitePgOpen(const char *zFilename, Pgr **ppPgr){
Pgr *p;
int n;
n = strlen(zFilename);
p = sqliteMalloc( sizeof(*p) + n*2 + 4 );
if( p==0 ){
*ppPgr = 0;
return SQLITE_NOMEM;
}
p->zMain = (char*)&p[1];
strcpy(p->zMain, zFilename);
p->zJournal = &p->zMain[n+1];
strcpy(p->zJournal, p->zMain);
p->zJournal[n] = '~';
p->zJournal[n+1] = 0;
p->fdJournal = -1;
p->fdMain = open(p->zMain, O_CREAT|O_RDWR, 0600);
if( p->fdMain<0 ){
*ppPgr = 0;
sqliteFree(p);
return SQLITE_PERM;
}
p->nDbPg = sqlitePgPageCount(p->fdMain);
if( access(p->zJournal, R_OK)==0 ){
sqlitePgJournalPlayback(p);
}
*ppPgr = p;
return SQLITE_OK;
}
/*
** Close the database file. Any outstanding transactions are abandoned.
*/
int sqlitePgClose(Pgr *p){
Pghdr *pPg;
if( p->fdMain ) close(p->fdMain);
if( p->fdJournal ) close(p->fdJournal);
unlink(p->zJournal);
while( (pPg = p->pAll)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
sqliteFree(p);
return SQLITE_OK;
}
/*
** Begin a new transaction. Return SQLITE_OK on success or an error
** code if something goes wrong.
*/
int sqlitePgBeginTransaction(Pgr *p){
assert( p->fdJournal<0 );
if( p->nRefPg>0 ){
/* release the read lock */
}
/* write lock the database */
p->fdJournal = open(p->zJournal, O_CREAT|O_EXCL|O_RDWR, 0600);
if( p->fdJournal<0 ){
return SQLITE_PERM;
}
p->nJPg = 0;
TRACE(("PG: begin transaction\n"));
return SQLITE_OK;
}
/*
** Commit the current transaction. Return SQLITE_OK or an error code.
*/
int sqlitePgCommit(Pgr *p){
Pghdr *pPrimaryIdx = 0;
Pghdr *pPg;
int rc;
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
if( pPg->isDirty ){
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
}
for(pPg=p->pJidx; pPg; pPg=pPg->pNx){
if( pPg->jpgno==1 ){
pPrimaryIdx = pPg;
}else{
TRACE(("PG: writing j-index %u\n", pPg->jpgno));
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
}
assert( pPrimaryIdx!=0 );
fsync(p->fdJournal);
TRACE(("PG: writing j-index %u\n", pPrimaryIdx->jpgno));
rc = sqlitePgMakeClean(pPrimaryIdx);
if( rc!=SQLITE_OK ){
return rc;
}
fsync(p->fdJournal);
rc = sqlitePgJournalPlayback(p);
if( rc!=SQLITE_OK ){
return rc;
}
/* remove write lock from database */
if( p->nRefPg>0 ){
/* acquire read lock on database */
}
return SQLITE_OK;
}
/*
** Abandon the current transaction.
*/
int sqlitePgRollback(Pgr *p){
Pghdr *pPg;
TRACE(("PG: begin rollback\n"));
for(pPg=p->pAll; pPg; pPg=pPg->pNx){
if( pPg->isDirty || pPg->jpgno!=0 ){
pPg->isDirty = 0;
pPg->jpgno = 0;
if( pPg->nRef>0 ){
TRACE(("PG: reloading d-page %u\n", pPg->dbpgno));
sqlitePgRead(p->fdMain, PG_TO_DATA(pPg), pPg->dbpgno);
}else{
sqlitePgUnlinkLru(pPg);
}
}
}
close(p->fdJournal);
p->fdJournal = -1;
unlink(p->zJournal);
while( (pPg = p->pJidx)!=0 ){
p->pAll = pPg->pNx;
sqliteFree(pPg);
}
p->nDbPg = sqlitePgPageCount(p->fdMain);
/* remove write lock from database */
if( p->nRefPg>0 ){
/* acquire read lock on database */
}
return SQLITE_OK;
}
/*
** Get a page from the database. Return a pointer to the data for that
** page.
**
** A NULL pointer will be returned if we run out of memory.
*/
int sqlitePgGet(Pgr *p, u32 pgno, void **ppData){
Pghdr *pPg;
int h;
pPg = sqlitePgFind(p, pgno);
if( pPg ){
pPg->nRef++;
if( pPg->nRef==1 ){
sqlitePgUnlinkLru(pPg);
TRACE(("PG: d-page %u pulled from cache\n", pgno));
}
p->nRefPg++;
if( p->nRefPg==1 ){
/* Acquire a read lock */
}
*ppData = PG_TO_DATA(pPg);
return SQLITE_OK;
}
if( p->nMemPg<MX_MEM_PAGE || p->pLru==0 ){
pPg = sqliteMalloc( sizeof(Pghdr) + SQLITE_PAGE_SIZE );
if( pPg==0 ) return SQLITE_NOMEM;
p->nMemPg++;
pPg->pNx = p->pAll;
p->pAll = pPg;
pPg->p = p;
TRACE(("PG: new page %d created.\n", p->nMemPg));
}else{
int rc;
pPg = p->pLru;
if( pPg->isDirty ){
rc = sqlitePgMakeClean(pPg);
if( rc!=SQLITE_OK ) return rc;
}
sqlitePgUnlinkLru(pPg);
h = pPg->dbpgno % PG_HASH_SIZE;
if( pPg->pPvHash ){
pPg->pPvHash->pNxHash = pPg->pNxHash;
}else{
assert( p->aPgHash[h]==pPg );
p->aPgHash[h] = pPg->pNxHash;
}
if( pPg->pNxHash ){
pPg->pNxHash->pPvHash = pPg->pPvHash;
}
TRACE(("PG: recycling d-page %u to d-page %u\n", pPg->dbpgno, pgno));
}
pPg->dbpgno = pgno;
if( pgno>p->nDbPg ){
p->nDbPg = pgno;
}
h = pgno % PG_HASH_SIZE;
pPg->pPvHash = 0;
pPg->pNxHash = p->aPgHash[h];
if( pPg->pNxHash ){
pPg->pNxHash->pPvHash = pPg;
}
p->aPgHash[h] = pPg;
pPg->jpgno = sqlitePgJournalPageNumber(p, pgno);
if( pPg->jpgno!=0 ){
TRACE(("PG: reading d-page %u content from j-page %u\n", pgno, pPg->jpgno));
sqlitePgRead(p->fdJournal, PG_TO_DATA(pPg), pPg->jpgno);
}else if( pPg->dbpgno!=0 ){
TRACE(("PG: reading d-page %u from database\n", pgno));
sqlitePgRead(p->fdMain, PG_TO_DATA(pPg), pPg->dbpgno);
}else{
TRACE(("PG: reading zero page\n");
memset(PG_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
}
pPg->isDirty = 0;
pPg->nRef = 1;
p->nRefPg++;
if( p->nRefPg==1 ){
/* Acquire a read lock */
}
*ppData = PG_TO_DATA(pPg);
return SQLITE_OK;
}
/*
** Release a reference to a database data page.
*/
int sqlitePgUnref(void *pData){
Pghdr *pPg = DATA_TO_PG(pData);
pPg->nRef--;
assert( pPg->nRef>=0 );
if( pPg->nRef==0 ){
Pgr *p = pPg->p;
pPg->pMru = 0;
pPg->pLru = p->pLru;
p->pLru = pPg;
TRACE(("PG: d-page %u is unused\n", pPg->dbpgno));
p->nRefPg--;
if( p->nRefPg==0 ){
/* Release the read lock */
}
}
return SQLITE_OK;
}
/*
** The database page in the argument has been modified. Write it back
** to the database file on the next commit.
*/
int sqlitePgTouch(void *pD){
Pghdr *pPg = DATA_TO_PG(pD);
assert( pPg->p->fdJournal>=0 );
if( pPg->isDirty==0 ){
pPg->isDirty = 1;
TRACE(("PG: d-page %u is dirty\n", pPg->dbpgno));
}
return SQLITE_OK;
}
/*
** Return the number of the first unused page at the end of the
** database file.
*/
int sqlitePgCount(Pgr *p, u32 *pPgno){
*pPgno = p->nDbPg;
return SQLITE_OK;
}
/*
** Return the page number associated with the given page.
*/
u32 sqlitePgNum(void *pD){
Pghdr *pPg = DATA_TO_PG(pD);
return pPg->dbpgno;
}

View File

@ -1,40 +0,0 @@
/*
** Copyright (c) 2001 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/
**
*************************************************************************
** $Id: pg.h,v 1.1 2001/02/11 16:56:24 drh Exp $
*/
typedef struct Pgr Pgr;
#define SQLITE_PAGE_SIZE 1024
int sqlitePgOpen(const char *filename, Pgr **pp);
int sqlitePgClose(Pgr*);
int sqlitePgBeginTransaction(Pgr*);
int sqlitePgCommit(Pgr*);
int sqlitePgRollback(Pgr*);
int sqlitePgGet(Pgr*, u32 pgno, void **);
int sqlitePgUnref(void*);
int sqlitePgTouch(void*);
int sqlitePgCount(Pgr*, u32*);
u32 sqlitePgNum(void*);

View File

@ -1,41 +0,0 @@
set sizes {1024 2048 4096 8192 16384 32768}
set fmt { %-8s}
puts -nonewline "page size: "
foreach s $sizes {
puts -nonewline [format $fmt $s]
}
puts ""
puts -nonewline "on leaf: "
foreach s $sizes {
set x [expr {$s - 18*4}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "direct: "
foreach s $sizes {
set x [expr {$p($s) + 10*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "indirect: "
foreach s $sizes {
set x [expr {$p($s) + ($s/4.0)*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""
puts -nonewline "dbl indir: "
foreach s $sizes {
set x [expr {$p($s) + ($s/4.0)*($s/4)*$s}]
set p($s) $x
puts -nonewline [format $fmt $x]
}
puts ""