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:
18
manifest
18
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
264f23315e682909abb47912f48733f641772a4c
|
||||
8ad996fdac6801768e94ca1710a0a3da03e1e7ea
|
@ -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.
|
1495
src/ex/db.c
1495
src/ex/db.c
File diff suppressed because it is too large
Load Diff
51
src/ex/db.h
51
src/ex/db.h
@ -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);
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
708
src/ex/pg.c
708
src/ex/pg.c
@ -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;
|
||||
}
|
40
src/ex/pg.h
40
src/ex/pg.h
@ -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*);
|
@ -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 ""
|
Reference in New Issue
Block a user