-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
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
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
-264f23315e682909abb47912f48733f641772a4c
\ No newline at end of file
+8ad996fdac6801768e94ca1710a0a3da03e1e7ea
\ No newline at end of file
+++ /dev/null
-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.
+++ /dev/null
-/*
-** 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.c,v 1.1 2001/02/11 16:56:24 drh Exp $
-*/
-#include "sqliteInt.h"
-#include "pg.h"
-
-/*
-** Everything we need to know about an open database
-*/
-struct Db {
- Pgr *pPgr; /* The pager for the database */
- DbCursor *pCursor; /* All open cursors */
- int inTransaction; /* True if a transaction is in progress */
- u32 freeList; /* List of free blocks */
- int nTable; /* Number of slots in aContent[] */
- u32 *aTable; /* Root page numbers for all tables */
-};
-
-/*
-** The maximum depth of a cursor
-*/
-#define MX_LEVEL 10
-
-/*
-** Within a cursor, each level off the search tree is an instance of
-** this structure.
-*/
-typedef struct DbIdxpt DbIdxpt;
-struct DbIdxpt {
- int pgno; /* The page number */
- u32 *aPage; /* The page data */
- int idx; /* Index into pPage[] */
-};
-
-/*
-** Everything we need to know about a cursor
-*/
-struct DbCursor {
- Db *pDb; /* The whole database */
- DbCursor *pPrev, *pNext; /* Linked list of all cursors */
- u32 rootPgno; /* Root page of table for this cursor */
- int onEntry; /* True if pointing to a table entry */
- int nLevel; /* Number of levels of indexing used */
- DbIdxpt aLevel[MX_LEVEL]; /* The index levels */
-};
-
-/*
-** Data layouts
-**
-** LEAF:
-** x[0] Magic number: BLOCK_LEAF
-** x[1] If root page, total number of entries in this table
-** ... One or more entries follow the leaf.
-**
-** Entry:
-** x[N+0] Number of u32-sized words in this entry
-** x[N+1] Hash value for this entry
-** x[N+2] Number of bytes of key in the payload
-** x[N+3] Number of bytes of data in the payload
-** x{N+4]... The payload area.
-**
-** INDEX:
-** x[0] Magic number: BLOCK_INDEX
-** x[1] If root page: total number of entries in this table
-** x[2] Number of slots in this index (Max value of N)
-** x[2*N+3] Page number containing entries with hash <= x[2*N+4]
-** x[2*N+4] The maximum hash value for entries on page x[2*N+3].
-**
-** FREE:
-** x[0] Magic number: BLOCK_FREE
-** x[1] Page number of the next free block on the free list
-**
-** PAGE1:
-** x[0] Magic number: BLOCK_PAGE1
-** x[1] First page of the freelist
-** x[2] Number of tables in this database
-** x[N+3] Root page for table N
-
-/*
-** The first word of every page is some combination of these values
-** used to indicate its function.
-*/
-#define BLOCK_PAGE1 0x24e47191
-#define BLOCK_INDEX 0x7ac53b46
-#define BLOCK_LEAF 0x60c45eef
-#define BLOCK_FREE 0x5b2dda47
-
-/*
-** The number of u32-sized objects that will fit on one page.
-*/
-#define U32_PER_PAGE (SQLITE_PAGE_SIZE/sizeof(u32))
-
-/*
-** Number of direct overflow pages per database entry
-*/
-#define N_DIRECT 10
-
-/*
-** The maximum amount of payload (in bytes) that will fit on on the same
-** page as a leaf. In other words, the maximum amount of payload
-** that does not require any overflow pages.
-**
-** This size is chosen so that a least 3 entry will fit on every
-** leaf. That guarantees it will always be possible to add a new
-** entry after a page split.
-*/
-#define LOCAL_PAYLOAD (((U32_PER_PAGE-2)/3 - (6+N_DIRECT))*sizeof(u32))
-
-/*
-** Allocate a new page. Return both the page number and a pointer
-** to the page data. The calling function is responsible for unref-ing
-** the page when it is no longer needed.
-**
-** The page is obtained from the freelist if there is anything there.
-** If the freelist is empty, the new page comes from the end of the
-** database file.
-*/
-int allocPage(Db *pDb, u32 *pPgno, u32 **ppPage){
- u32 pgno;
- int rc;
-
- if( pDb->aTable==0 ) return SQLITE_NOMEM;
-
- /* Try to reuse a page from the freelist
- */
- if( pDb->freeList==0 ){
- u32 *pPage;
- rc = sqlitePgGet(pDb->pPgr, pDb->freeList, &pPage);
- if( rc==SQLITE_OK ){
- if( pPage[0]==BLOCK_FREE ){
- *pPgno = pDb->freeList;
- *ppPage = aPage;
- pDb->freeList = aPage[1];
- memset(*ppPage, 0, SQLITE_PAGE_SIZE);
- return SQLITE_OK;
- }
- /* This only happens if we have database corruption */
- sqlitePgUnref(pPage);
- }
- }
-
- /* If the freelist is empty, or we cannot access it,
- ** then allocate a new page from the end of the file.
- */
- if( (rc = sqlitePgCount(pDb->pPgr, &pgno))==SQLITE_OK &&
- (rc = sqlitePgGet(pDb->pPgr, pgno, (void**)ppPage))==SQLITE_OK ){
- *pPgno = pgno;
- memset(*ppPage, 0, SQLITE_PAGE_SIZE);
- return SQLITE_OK;
- }
- return rc;
-}
-
-/*
-** Return a page to the freelist and dereference the page.
-*/
-static void freePage(DB *pDb, u32 pgno, u32 *aPage){
- if( pgno==0 ) return
- if( aPage==0 ){
- int rc;
- rc = sqlitePgGet(pDb->pPgr, pgno, &aPage);
- if( rc!=SQLITE_OK ) return;
- }
- assert( sqlitePgNum(aPage)==pgno );
- aPage[0] = BLOCK_FREE;
- aPage[1] = pDb->freeList;
- pDb->freeList = pgno;
- memset(&aPage[2], 0, SQLITE_PAGE_SIZE - 2*sizeof(u32));
- sqlitePgTouch(aPage);
- sqlitePgUnref(aPage);
-}
-
-/*
-** Return the number of bytes of payload storage required on the leaf
-** node to hold the amount of payload specified by the argument.
-** Overflow pages do not count, only memory on the leaf page.
-**
-** Return -1 if nTotal is more than sqlite is able to store.
-*/
-static int payloadLocalSize(int nTotal){
- int nLocal, i;
- if( nTotal<0 ) nTotal = 0;
- if( nTotal <= LOCAL_PAYLOAD ){
- /* All the data fits on the leaf page */
- return (nTotal + 3)/4;
- }
- nLocal = LOCAL_PAYLOAD;
- nTotal -= LOCAL_PAYLOAD;
- if( nTotal < 10*SQLITE_PAGE_SIZE ){
- return nLocal + ((nTotal+SQLITE_PAGE_SIZE-1)/SQLITE_PAGE_SIZE)*sizeof(u32);
- }
- nLocal += N_DIRECT*sizeof(u32);
- nTotal -= N_DIRECT*SQLITE_PAGE_SIZE;
- if( nTotal < U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- return nLocal + sizeof(u32);
- }
- nLocal += sizeof(u32);
- nTotal -= U32_PER_PAGE*SQLITE_PAGE_SIZE;
- if( nTotal < U32_PER_PAGE*U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- return nLocal + sizeof(u32);
- }
- return -1; /* This payload will not fit. */
-}
-
-/*
-** Read data from the payload area.
-**
-** aPage points directly at the beginning of the payload. No bounds
-** checking is done on offset or amt -- it is assumed that the payload
-** area is big enough to accomodate.
-*/
-static int payloadRead(Db *pDb, u32 *aPage, int offset, int amt, void *pBuf){
- int rc;
- int tomove;
- int i;
-
- /* First read local data off of the leaf page itself.
- ** This is all that ever happens in 99% of accesses.
- */
- assert( offset>=0 && amt>=0 );
- if( offset < LOCAL_PAYLOAD ){
- /* Data stored directly in the leaf block of the BTree */
- if( amt+offset>LOCAL_PAYLOAD ){
- tomove = LOCAL_PAYLOAD - offset;
- }else{
- tomove = amt;
- }
- memcpy(pBuf, &((char*)aPage)[offset], tomove);
- pBuf = &((char*)pBuf)[tomove];
- offset += tomove;
- amt -= tomove;
- if( amt<=0 ) return SQLITE_OK;
- }
- offset -= LOCAL_PAYLOAD;
- aPage += LOCAL_PAYLOAD/sizeof(aPage[0]);
-
- /* If not all of the data fits locally, read from the first
- ** ten direct-access overflow pages.
- */
- if( offset < N_DIRECT*SQLITE_PAGE_SIZE ){
- for(i=offset/SQLITE_PAGE_SIZE; i<N_DIRECT && amt>0; i++){
- char *aData;
- base = offset - i*SQLITE_PAGE_SIZE;
- rc = sqlitePgGet(pDb->pPgr, aPage[i], &aData);
- if( rc!=SQLITE_OK ) return rc;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(pBuf, &aData[base], tomove);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- }
- offset -= N_DIRECT*SQLITE_PAGE_SIZE;
- aPage += N_DIRECT;
-
- /* If the first N_DIRECT overflow pages do not contain everything, then
- ** read from an overflow page that is filled with pointer to
- ** U32_PER_PAGE more overflow pages.
- */
- if( offset < U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- u32 *indirPage;
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &indirPage);
- if( rc!=SQLITE_OK ) return rc;
- for(i=offset/SQLITE_PAGE_SIZE; i<U32_PER_PAGE && amt>0; i++){
- int base;
- char *aData;
- base = offset - i*SQLITE_PAGE_SIZE;
- rc = sqlitePgGet(pDb->pPgr, indirPage[idx], &aData);
- if( rc!=SQLITE_OK ) break;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(pBuf, &aData[base], tomove);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- sqlitePgUnref(indirPage);
- if( rc!=SQLITE_OK ) return rc;
- if( amt<=0 ) return SQLITE_OK;
- }
- offset -= U32_PER_PAGE*SQLITE_PAGE_SIZE;
- aPage++;
-
- /* If there is still more data, then read using a double-indirect
- ** overflow. The overflow page points to U32_PER_PAGE additional
- ** overflow pages, each of which pointer to U32_PER_PAGE more overflow
- ** pages which contain data.
- **
- ** This is hard to test. To exercise this code, you have to make
- ** a database entry of more than 273336 bytes in side, assuming a
- ** pagesize of 1024 bytes and 10 direct overflow pages. By the
- ** time this code runs, you have already used 267 overflow pages.
- */
- if( offset < U32_PER_PAGE*U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- u32 *dblIndirPage;
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &dblIndirPage);
- if( rc!=SQLITE_OK ) return rc;
- i = offset/(U32_PER_PAGE*SQLITE_PAGE_SIZE);
- for(; i<U32_PER_PAGE && amt>0; i++){
- u32 *indirPage;
- int basis;
- int j;
- rc = sqlitePgGet(pDb->pPgr, dblIndirPage[i], &indirPage);
- if( rc!=SQLITE_OK ) break;
- basis = i*U32_PER_PAGE*SQLITE_PAGE_SIZE;
- j = (offset - basis)/SQLITE_PAGE_SIZE;
- for(; j<U32_PER_PAGE && amt>0; j++){
- char *aData;
- base = (offset - basis) - ij*SQLITE_PAGE_SIZE;
- rc = sqlitePgGet(pDb->pPgr, indirPage[j], &aData);
- if( rc!=SQLITE_OK ) break;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(pBuf, &aData[base], tomove);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- sqlitePgUnref(indirPage);
- if( rc!=SQLITE_OK ) break;
- }
- sqlitePgUnref(dblIndirPage);
- }
-
- /* Anything beyond the double-indirect pages, just fill in with
- ** zeros. You have to write 67382200 bytes to go past the
- ** double-indirect pages, assuming a 1024 byte page size.
- */
- if( amt>0 ) memset(pBuf, 0, amt);
- return SQLITE_OK;
-}
-
-/*
-** Write data into the payload area.
-**
-** If pages have already been allocated for the payload, they are
-** simply overwritten. New pages are allocated as necessary to
-** fill in gaps. sqlitePgTouch() is called on all overflow pages,
-** but the calling function must invoke sqlitePgTouch() for aPage
-** itself.
-*/
-static int payloadWrite(Db *pDb, u32 *aPage, int offset, int amt, void *pBuf){
- assert( offset>=0 && amt>=0 );
-
- /* Local data
- */
- if( offset < LOCAL_PAYLOAD ){
- if( amt+offset>LOCAL_PAYLOAD ){
- tomove = LOCAL_PAYLOAD - offset;
- }else{
- tomove = amt;
- }
- memcpy(&((char*)aPage)[offset], pBuf, tomove);
- pBuf = &((char*)pBuf)[tomove];
- offset += tomove;
- amt -= tomove;
- if( amt<=0 ) return SQLITE_OK;
- }
- offset -= LOCAL_PAYLOAD;
- aPage += LOCAL_PAYLOAD/sizeof(aPage[0]);
-
- /* Direct overflow pages
- */
- if( offset < N_DIRECT*SQLITE_PAGE_SIZE ){
- for(i=offset/SQLITE_PAGE_SIZE; i<N_DIRECT && amt>0; i++){
- base = offset - i*SQLITE_PAGE_SIZE;
- if( aPage[i] ){
- rc = sqlitePgGet(pDb->pPgr, aPage[i], &aData);
- }else{
- rc = allocPage(pDb, &aPage[i], &aData);
- }
- if( rc!=SQLITE_OK ) return rc;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(&aData[base], pBuf, tomove);
- sqlitePgTouch(aData);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- if( amt<=0 ) return SQLITE_OK;
- }
- offset -= N_DIRECT*SQLITE_PAGE_SIZE;
- aPage += N_DIRECT;
-
- /* Indirect overflow pages
- */
- if( offset < U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- u32 *indirPage;
- if( aPage[0] ){
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &indirPage);
- }else{
- rc = allocPage(pDb, &aPage[0], &indirPage);
- }
- if( rc!=SQLITE_OK ) return rc;
- for(i=offset/SQLITE_PAGE_SIZE; i<U32_PER_PAGE && amt>0; i++){
- int base;
- char *aData;
- base = offset - i*SQLITE_PAGE_SIZE;
- if( indirPage[i] ){
- rc = sqlitePgGet(pDb->pPgr, indirPage[i], &aData);
- }else{
- rc = allocPage(pDb, &indirPage[i], &aData);
- sqlitePgTouch(indirPage);
- }
- if( rc!=SQLITE_OK ) break;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(&aData[base], pBuf, tomove);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- sqlitePgUnref(indirPage);
- if( rc!=SQLITE_OK ) return rc;
- if( amt<=0 ) return SQLITE_OK;
- }
- offset -= U32_PER_PAGE*SQLITE_PAGE_SIZE;
- aPage++;
-
- /* Double-indirect overflow pages
- */
- if( offset < U32_PER_PAGE*U32_PER_PAGE*SQLITE_PAGE_SIZE ){
- u32 *dblIndirPage;
- if( aPage[0] ){
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &dblIndirPage);
- }else{
- rc = allocPage(pDb, &aPage[0], &dblIndirPage);
- }
- if( rc!=SQLITE_OK ) return rc;
- i = offset/(U32_PER_PAGE*SQLITE_PAGE_SIZE);
- for(; i<U32_PER_PAGE && amt>0; i++){
- u32 *indirPage;
- int basis;
- int j;
- if( aPage[0] ){
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &dblIndirPage);
- }else{
- rc = allocPage(pDb, &aPage[0], &dblIndirPage);
- sqlitePgTouch(dblIndirPage);
- }
- rc = sqlitePgGet(pDb->pPgr, dblIndirPage[i], &indirPage);
- if( rc!=SQLITE_OK ) break;
- basis = i*U32_PER_PAGE*SQLITE_PAGE_SIZE;
- j = (offset - basis)/SQLITE_PAGE_SIZE;
- for(; j<U32_PER_PAGE && amt>0; j++){
- char *aData;
- base = (offset - basis) - ij*SQLITE_PAGE_SIZE;
- if( indirPage[j] ){
- rc = sqlitePgGet(pDb->pPgr, indirPage[j], &aData);
- }else{
- rc = allocPage(pDb, &indirPage[j], &aData);
- sqlitePgTouch(indirPage);
- }
- if( rc!=SQLITE_OK ) break;
- if( amt+base > SQLITE_PAGE_SIZE ){
- tomove = SQLITE_PAGE_SIZE - base;
- }else{
- tomove = amt;
- }
- memcpy(&aData[base], pBuf, tomove);
- sqlitePgTouch(aData);
- sqlitePgUnref(aData);
- pBuf = &((char*)pBuf)[tomove];
- amt -= tomove;
- }
- sqlitePgUnref(indirPage);
- if( rc!=SQLITE_OK ) break;
- }
- sqlitePgUnref(dblIndirPage);
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Resize the payload area. If the payload area descreases in size,
-** this routine deallocates unused overflow pages. If the payload
-** area increases in size, this routine is a no-op.
-*/
-static int payloadResize(Db *pDb, u32 *aPage, int oldSize, int newSize){
- int i, j; /* Loop counters */
- int first, last; /* Indices of first and last pages to be freed */
- int rc; /* Return code from sqlitePgGet() */
-
- /* Skip over the local data. We do not need to free it.
- */
- if( newSize>=oldSize ) return SQLITE_OK;
- oldSize -= LOCAL_PAYLOAD;
- if( oldSize<=0 ) return SQLITE_OK;
- newSize -= LOCAL_PAYLOAD;
- aPage += LOCAL_PAYLOAD/sizeof(u32);
-
- /* Compute the indices of the first and last overflow pages to
- ** be freed.
- */
- first = (newSize - 1)/SQLITE_PAGE_SIZE + 1;
- last = (oldSize - 1)/SQLITE_PAGE_SIZE;
-
- /* Free the direct overflow pages
- */
- if( first < N_DIRECT ){
- for(i=first; i<N_DIRECT && i<=last; i++){
- freePage(pDb, aPage[i], 0);
- aPage[i] = 0;
- }
- }
- aPage += N_DIRECT;
- first -= N_DIRECT;
- last -= N_DIRECT;
- if( last<0 ) return SQLITE_OK;
- if( first<0 ) first = 0;
-
- /* Free indirect overflow pages
- */
- if( first < U32_PER_PAGE ){
- u32 *indirPage;
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &indirPage);
- if( rc!=SQLITE_OK ) return rc;
- for(i=first; i<U32_PER_PAGE && i<=last; i++){
- freePage(pDb, indirPage[i], 0);
- indirPage[i] = 0;
- touch = 1;
- }
- if( first<=0 ){
- freepage(pDb, aPage[0], indirPage);
- aPage[0] = 0;
- }else{
- sqlitePgTouch(indirPage);
- sqlitePgUnref(indirPage);
- }
- }
- aPage++;
- first -= U32_PER_PAGE;
- last -= U32_PER_PAGE;
- if( last<0 ) return SQLITE_OK;
- if( first<0 ) first = 0;
-
- /* Free double-indirect overflow pages
- */
- if( first < U32_PER_PAGE*U32_PER_PAGE ){
- u32 *dblIndirPage;
- rc = sqlitePgGet(pDb->pPgr, aPage[0], &dblIndirPage);
- if( rc!=SQLITE_OK ) return rc;
- for(i=first/U32_PER_PAGE; i<U32_PER_PAGE; i++){
- u32 *indirPage;
- basis = i*U32_PER_PAGE;
- if( last < basis ) break;
- rc = sqlitePgGet(pDb->pPgr, dblIndirPage[i], &indirPage);
- if( rc!=SQLITE_OK ) return rc;
- for(j=first>basis?first-basis:0 ; j<U32_PER_PAGE; j++){
- if( j + basis > last ) break;
- freePage(pDb, indirPage[j], 0);
- indirPage[j] = 0;
- }
- if( first<=basis ){
- freepage(pDb, dblIndirPage[i], 0);
- dblIndirPage[i] = 0;
- }else{
- sqlitePgTouch(indirPage);
- sqlitePgUnref(indirPage);
- }
- }
- if( first<=0 ){
- freepage(pDb, aPage[0], dblIndirPage);
- aPage[0] = 0;
- }else{
- sqlitePgTouch(dblIndirPage);
- sqlitePgUnref(dblIndirPage);
- }
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Allocate space for the content table in the given Db structure.
-** return SQLITE_OK on success and SQLITE_NOMEM if it fails.
-*/
-static int sqliteDbExpandTableArray(Db *pDb){
- pDb->aTable = sqliteRealloc( pDb->aTable, pDb->nTable*sizeof(u32));
- if( pDb->aTable==0 ){
- pDb->inTranaction = 0;
- return SQLITE_NOMEM;
- }
- return SQLITE_OK;
-}
-
-/*
-** Open a database.
-*/
-int sqliteDbOpen(const char *filename, Db **ppDb){
- Db *pDb = 0;
- Pgr *pPgr = 0;
- u32 *aPage1;
- int rc;
- u32 nPage;
-
- rc = sqlitePgOpen(filename, &pPgr);
- if( rc!=SQLITE_OK ) goto open_err;
- pDb = sqliteMalloc( sizeof(*pDb) );
- if( pDb==0 ){
- rc = SQLITE_NOMEM;
- goto open_err;
- }
- pDb->pPgr = pPgr;
- pDb->pCursor = 0;
- pDb->inTransaction = 0;
- sqlitePgCount(pDb->pPgr, &nPage);
- rc = sqlitePgGet(pDb->pPgr, 1, &aPage1);
- if( rc!=0 ) goto open_err;
- if( nPage==0 ){
- sqlitePgBeginTransaction(pDb->pPgr);
- aPage1[0] = BLOCK_PAGE1;
- sqlitePgTouch(aPage1);
- sqlitePgCommit(pDb->pPgr);
- }
- pDb->freeList = aPage[1];
- pDb->nTable = aPage[2];
- rc = sqliteDbExpandTableArray(pDb);
- if( rc!=SQLITE_OK ) goto open_err;
- rc = payloadRead(pDb, &aPage1[3], 0, pDb->nTable*sizeof(u32), pDb->aTable);
- sqlitePgUnref(aPage1);
- if( rc!=SQLITE_OK ) goto open_err;
- *ppDb = pDb;
- return SQLITE_OK;
-
-open_err:
- *ppDb = 0;
- if( pPgr ) sqlitePgClose(pPgr);
- if( pDb ){
- sqliteFree(pDb->aContent);
- sqliteFree(pDb);
- }
- return rc;
-}
-
-/*
-** Close a database
-*/
-int sqliteDbClose(Db *pDb){
- while( pDb->pCursor ){
- sqliteDbCursorClose(pDb->pCursor);
- }
- sqlitePgClose(pDb->pPgr);
- sqliteFree(pDb->aContent);
- sqliteFree(pDb);
- return SQLITE_OK;
-}
-
-/*
-** Begin a transaction
-*/
-int sqliteDbBeginTransaction(Db *pDb){
- int rc;
- if( pDb->aContent==0 ){
- return SQLITE_NOMEM;
- }
- if( pDb->inTransaction ){
- return SQLITE_INTERNAL;
- }
- rc = sqlitePgBeginTransaction(pDb->pPgr);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pDb->inTransaction = 1;
- return SQLITE_OK;
-}
-
-/*
-** Commit changes to the database
-*/
-int sqliteDbCommit(Db *pDb){
- u32 *aPage1;
- int rc;
- if( !pDb->inTransaction ){
- return SQLITE_OK;
- }
- rc = sqlitePgGet(pDb->pPgr, 1, &aPage1);
- if( rc!=SQLITE_OK ) return rc;
- aPage1[1] = pDb->freeList;
- aPage1[2] = pDb->nTable;
- payloadWrite(pDb, &aPage1[3], 0, pDb->nTable*sizeof(u32), pDb->aTable);
- sqlitePgUnref(aPage1);
- rc = sqlitePgCommit(pDb->pPgr);
- if( rc!=SQLITE_OK ) return rc;
- pDb->inTransaction = 0;
- return SQLITE_OK;
-}
-
-/*
-** Rollback the database to its state prior to the beginning of
-** the transaction
-*/
-int sqliteDbRollback(Db *pDb){
- u32 *aPage1;
- if( !pDb->inTransaction ) return SQLITE_OK;
- rc = sqlitePgRollback(pDb->pPgr);
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlitePgGet(pDb->pPgr, 1, &aPage1);
- if( rc!=SQLITE_OK ) return rc;
- pDb->freeList = aPage1[1];
- pDb->nTable = aPage1[2];
- if( sqliteDbExpandTableArray(pDb)!=SQLITE_OK ){
- return SQLITE_NOMEM;
- }
- payloadRead(pDb, &aPage1[3], 0, pDb->nTable*sizeof(u32), pDb->aTable);
- sqlitePgUnref(aPage1);
- pDb->inTransaction = 0;
- return SQLITE_OK;
-}
-
-/*
-** Create a new table in the database. Write the table number
-** that is used to open a cursor into that table into *pTblno.
-*/
-int sqliteDbCreateTable(Db *pDb, int *pTblno){
- u32 *aPage;
- u32 pgno;
- int rc;
- int swTblno;
- int i;
-
- rc = allocPage(pDb, &pgno, &aPage);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- tblno = -1;
- for(i=0; i<pDb->nTable; i++){
- if( pDb->aTable[i]==0 ){
- tblno = i;
- break;
- }
- }
- if( tblno<0 ){
- pDb->nTable++;
- rc = sqliteExpandTableArray(pDb);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }
- pDb->aTable[tblno] = pgno;
- aPage[0] = BLOCK_LEAF;
- memset(&aPage[1], 0, SQLITE_PAGE_SIZE - sizeof(u32));
- sqlitePgTouch(aPage);
- sqlitePgUnref(aPage);
- return rc;
-}
-
-/*
-** Recursively add a page to the free list
-*/
-static int sqliteDbDropPage(Db *pDb, u32 pgno){
- u32 *aPage;
- int rc;
-
- rc = sqlitePgGet(pDb->pPgr, pgno, (void**)&aPage);
- if( rc!=SQLITE_OK ) return rc;
- switch( aPage[0] ){
- case BLOCK_INDEX: {
- int n, i;
- n = aPage[2];
- for(i=0; i<n; i++){
- u32 subpgno = aPage[3 + i*2];
- if( subpgno>0 ) sqliteDbDropPage(pDb, subpgno);
- }
- freePage(pDb, pgno, aPage);
- break;
- }
- case BLOCK_LEAF: {
- int i = 2;
- while( i<U32_PER_PAGE ){
- int entrySize = aPage[i];
- if( entrySize==0 ) break;
- payloadResize(pDb, &aPage[i+4], aPage[i+2]+aPage[i+3], 0);
- i += entrySize;
- }
- freePage(pDb, pgno, aPage);
- break;
- }
- default: {
- /* Do nothing */
- break;
- }
- }
-}
-
-/*
-** Delete the current associate of a cursor and release all the
-** pages it holds. Except, do not release pages at levels less
-** than N.
-*/
-static void sqliteDbResetCursor(DbCursor *pCur, int N){
- int i;
- for(i=pCur->nLevel-1; i>=N; i--){
- sqlitePgUnref(pCur->aLevel[i].aPage);
- }
- pCur->nLevel = N;
- pCur->onEntry = 0;
-}
-
-/*
-** Delete an entire table.
-*/
-static int sqliteDbDropTable(Db *pDb, int tblno){
- DbCursor *pCur;
- u32 pgno;
-
- /* Find the root page for the table to be dropped.
- */
- if( pDb->aTable==0 ){
- return SQLITE_NOMEM;
- }
- if( tblno<0 || tblno>=pDb->nTable || pDb->aTable[tblno]==0 ){
- return SQLITE_NOTFOUND;
- }
- pgno = pDb->aTable[tblno];
- pDb->aTable[tblno] = 0;
- if( tblno==pDb->nTable-1 ){
- pDb->nTable--;
- }
-
- /* Reset any cursors pointing to the table that is about to
- ** be dropped */
- for(pCur=pDb->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->rootPgno==pgno ){
- sqliteDbResetCursor(pCur, 0);
- }
- }
-
- /* Move all pages associated with this table to the freelist
- */
- sqliteDbDropPage(pDb, pgno);
- return SQLITE_OK;
-}
-
-/*
-** Create a new cursor
-*/
-int sqliteDbCursorOpen(Db *pDb, int tblno, DbCursor **ppCur){
- u32 pgno;
- DbCursor *pCur;
-
- /* Translate the table number into a page number
- */
- if( pDb->aTable==0 ){
- *ppCur = 0;
- return SQLITE_NOMEM;
- }
- if( tblno<0 || tblno>=pDb->nContent || pDb->aTable[tblno]==0 ){
- *ppCur = 0;
- return SQLITE_NOTFOUND;
- }
- pgno = pDb->aTable[tblno];
-
- /* Allocate the cursor
- */
- pCur = sqliteMalloc( sizeof(*pCur) );
- pCur->pgno = pgno;
- pCur->pDb = pDb;
- pCur->pNext = pDb->pCursor;
- pCur->pPrev = 0;
- if( pDb->pCursor ){
- pDb->pCursor->pPrev = pCur;
- }
- pDb->pCursor = pCur;
- *ppCur = pCur;
- return SQLITE_OK;
-}
-
-/*
-** Delete a cursor
-*/
-int sqliteDbCursorClose(DbCursor *pCur){
- int i;
- if( pCur->pPrev ){
- pCur->pPrev->pNext = pCur->pNext;
- }else if( pCur->pDb->pCursor==pCur ){
- pCur->pDb->pCursor = pCur->pNext;
- }
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur->pPrev;
- }
- sqliteDbResetCursor(pCur, 0);
- sqliteFree(pCur);
- return SQLITE_OK;
-}
-
-/*
-** Beginning at index level "i" (the outer most index is 0), move down
-** to the first entry of the table. Levels above i (less than i) are
-** unchanged.
-*/
-static int sqliteDbGotoFirst(DbCursor *pCur, int i){
- int rc = -1;
-
- assert( i>=0 && i<MAX_LEVEL );
- if( pCur->nLevel > i+1 ){
- sqliteDbResetCursor(pCur, i+1);
- }
- assert( pCur->nLevel==i+1 );
- while( rc < 0 ){
- u32 *aPage = pCur->aLevel[i].aPage;
- assert( aPage!=0 );
- switch( aPage[0] ){
- case BLOCK_LEAF: {
- if( aPage[2]!=0 ){
- pCur->aLevel[i].idx = 2;
- pCur->onEntry = 1;
- }else{
- sqliteDbResetCursor(pCur, 1);
- }
- rc = SQLITE_OK;
- break;
- }
- case BLOCK_INDEX: {
- int n = aPage[2];
- if( n<2 || n>=((U32_PER_PAGE - 3)/2) ){
- sqliteDbResetCur(pCur, 1);
- rc = SQLITE_CORRUPT;
- break;
- }
- pCur->nLevel++;
- i++;
- pCur->aLevel[i].pgno = aPage[3];
- rc = sqlitePgGet(pCur->pDb->pPgr, pCur->aLevel[i].pgno,
- &pCur->aLevel[i].aPage);
- if( rc != SQLITE_OK ){
- sqliteDbResetCursor(pCur, 1);
- }else{
- rc = -1;
- }
- break;
- }
- default: {
- sqliteDbResetCursor(pCur, 1);
- rc = SQLITE_CORRUPT;
- }
- }
- }
- return rc;
-}
-
-################
-
-/*
-** Move the cursor to the first entry in the table.
-*/
-int sqliteDbCursorFirst(DbCursor *pCur){
- if( pCur->nLevel==0 ){
- int rc;
- pCur->aLevel[0].pgno = pCur->rootPgno;
- rc = sqlitePgGet(pCur->pDb->pPgr, pCur->rootPgno, pCur->aLevel[0].aPage);
- if( rc!=SQLITE_OK ){
- sqliteDbResetCursor(pCur, 0);
- return rc;
- }
- pCur->nLevel = 1;
- }
- return sqliteDbGotoFirst(pCur, 0);
-}
-
-/*
-** Advance the cursor to the next entry in the table.
-*/
-int sqliteDbCursorNext(DbCursor *pCur){
- int i, idx, n, rc;
- u32 pgno, *aPage;
- if( !pCur->onEntry ){
- return sqliteDbCursorFirst(pCur);
- }
- i = pCur->nLevel-1;
- aPage = pCur->aLevel[i].aPage;
- idx = pCur->aLevel[i].idx;
- idx += SWB(aPage[idx]);
- if( idx >= SQLITE_PAGE_SIZE/sizeof(u32) ){
- sqliteDbResetCursor(pCur, 1);
- return SQLITE_CORRUPT;
- }
- if( aPage[idx]!=0 ){
- pCur->aLabel[i].idx = idx;
- return SQLITE_OK;
- }
- rc = SQLITE_OK;
- while( pCur->nLevel>1 ){
- pCur->nLevel--;
- i = pCur->nLevel-1;
- sqlitePgUnref(pCur->aLevel[pCur->nLevel].aPage);
- aPage = pCur->aLevel[i].aPage;
- idx = pCur->aLevel[i].idx;
- assert( SWB(aPage[0])==BLOCK_MAGIC|BLOCK_INDEX );
- n = SWB(aPage[2]);
- idx += 2;
- if( (idx-3)/2 < n ){
- pCur->aLevel[i].idx = idx;
- pCur->nLevel++;
- i++;
- pgno = pCur->aLevel[i].pgno = SWB(aPage[idx+1]);
- rc = sqlitePgGet(pDb->pPgr, pgno, &pCur->aLevel[i].aPage);
- if( rc!=SQLITE_OK ) break;
- rc = sqliteDbGotoFirst(pCur, i);
- break;
- }
- }
- sqliteDbResetCursor(pCur, 0);
- return SQLITE_OK;
-}
-
-/*
-** Return the amount of data on the entry that the cursor points
-** to.
-*/
-int sqliteDbCursorDatasize(DbCursor *pCur){
- u32 *aPage;
- int idx, i;
- if( !pCur->onEntry ) return 0;
- i = pCur->nLevel-1;
- idx = pCur->aLevel[i].idx;
- aPage = pCur->aLevel[i].aPage;
- assert( aPage );
- assert( idx>=2 && idx+4<U32_PER_PAGE );
- return aPage[idx+3];
-}
-
-/*
-** Return the number of bytes of key on the entry that the cursor points
-** to.
-*/
-int sqliteDbCursorKeysize(DbCursor *pCur){
- u32 *aPage;
- int idx, i;
- if( !pCur->onEntry ) return 0;
- i = pCur->nLevel-1;
- idx = pCur->aLevel[i].idx;
- aPage = pCur->aLevel[i].aPage;
- assert( aPage );
- assert( idx>=2 && idx+4<U32_PER_PAGE );
- return aPage[idx+2];
-}
-
-/*
-** Read data from the cursor.
-*/
-int sqliteDbCursorRead(DbCursor *pCur, int amt, int offset, void *buf){
- u32 *aPage;
- int idx, i;
- int nData;
- int nKey;
- if( !pCur->onEntry ){
- memset(cbuf, 0, amt);
- return SQLITE_OK;
- }
- if( amt<=0 || offset<0 ){
- return SQLITE_ERR;
- }
- i = pCur->nLevel-1;
- idx = pCur->aLevel[i].idx;
- aPage = pCur->aLevel[i].aPage;
- assert( aPage );
- assert( idx>=2 && idx+4<U32_PER_PAGE );
- nData = aPage[idx+3];
- if( offset>=nData ){
- memset(buf, 0, amt);
- return SQLITE_OK;
- }
- nKey = aPage[idx+2];
- if( nData<offset+amt ){
- memset(&((char*)buf)[nData-offset], 0, amt+offset-nData);
- amt = nData - offset;
- }
- payloadRead(pCur->pDb, &aPage[idx+4], amt, offset + nKey, buf);
- return SQLITE_OK;
-}
-
-/*
-** Read the current key from the cursor.
-*/
-int sqliteDbCursorReadKey(DbCursor *pCur, int amt, int offset, void *buf){
- u32 *aPage;
- int idx, i;
- int nData;
- int nKey;
- if( !pCur->onEntry ){
- memset(cbuf, 0, amt);
- return SQLITE_OK;
- }
- if( amt<=0 || offset<0 ){
- return SQLITE_ERR;
- }
- i = pCur->nLevel-1;
- idx = pCur->aLevel[i].idx;
- aPage = pCur->aLevel[i].aPage;
- assert( aPage );
- assert( idx>=2 && idx+4<(SQLITE_PAGE_SIZE/sizeof(u32))
- nKey = aPage[idx+2];
- if( offset>=nKey ){
- memset(buf, 0, amt);
- return SQLITE_OK;
- }
- if( nKey<offset+amt ){
- memset(&((char*)buf)[nKey-offset], 0, amt+offset-nKey);
- amt = nKey - offset;
- }
- payloadRead(pCur->pDb, &aPage[idx+4], amt, offset, buf);
- return SQLITE_OK;
-}
-
-/*
-** Generate a 32-bit hash from the given key.
-*/
-static u32 sqliteDbHash(int nKey, void *pKey){
- u32 h;
- unsigned char *key;
- if( nKey==4 ){
- return *(u32*)pKey;
- }
- key = pKey;
- h = 0;
- while( 0 < nKey-- ){
- h = (h<<13) ^ (h<<3) ^ h ^ *(key++)
- }
- return h;
-}
-
-/*
-** Move the cursor so that the lowest level is the leaf page that
-** contains (or might contain) the given key.
-*/
-static int sqliteDbFindLeaf(DbCursor *pCur, int nKey, void *pKey, u32 h;){
- int i, j, rc;
- u32 h;
-
- h = sqliteDbHash(nKey, pKey);
- sqliteDbResetCursor(pCur, 1);
- i = 0;
- for(;;){
- u32 nxPgno;
- u32 *aPage = pCur->aLevel[i].aPage;
- if( SWB(aPage[0])==BLOCK_MAGIC|BLOCK_LEAF ) break;
- if( SWB(aPage[0])!=BLOCK_MAGIC|BLOCK_INDEX ){
- return SQLITE_CORRUPT;
- }
- if( i==MAX_LEVEL-1 ){
- return SQLITE_FULL;
- }
- n = SWB(aPage[2]);
- if( n<2 || n>=(SQLITE_PAGE_SIZE/2*sizeof(u32))-2 ){
- return SQLITE_CORRUPT;
- }
- for(j=0; j<n-1; j++){
- if( h < SWB(aPage[j*2+3]) ) break;
- }
- nxPgno = SWB(aPage[j*2+4]);
- pCur->aLevel[i].idx = j;
- pCur->aLevel[i].pgno = nxPgno;
- rc = sqlitePgGet(pCur->pDb->pPgr, nxPgno, &pCur->aLevel[i].aPage);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pCur->nLevel++;
- i++;
- }
- return SQLITE_OK;
-}
-
-/*
-** Position the cursor on the entry that matches the given key.
-*/
-int sqliteDbCursorMoveTo(DbCursor *pCur, int nKey, void *pKey){
- int rc, i;
- u32 *aPage;
- int idx;
- u32 h;
-
- h = sqliteDbHash(nKey, pKey);
- rc = sqliteDbFindLeaf(pCur, nKey, pKey, h);
- if( rc!=SQLITE_OK ) return rc;
- i = pCur->nLevel-1;
- aPage = pCur->aLevel[i].aPage;
- idx = 2;
- rc = SQLITE_NOTFOUND;
- while( idx>=2 && idx<(SQLITE_PAGE_SIZE/sizeof(u32))-3 && aPage[idx]!=0 ){
- if( sqliteDbKeyMatch(&aPage[idx], nKey, pKey, h) ){
- pCur->aLevel[i].idx = idx;
- pCur->onEntry = 1;
- rc = SQLITE_OK;
- break;
- }
- idx += SWB(aPage[idx]);
- }
- return rc;
-}
-
-/*
-** Insert a new entry into the table. The cursor is left pointing at
-** the new entry.
-*/
-int sqliteDbCursorInsert(
- DbCursor *pCur, /* A cursor on the table in which to insert */
- int nKey, void *pKey, /* The insertion key */
- int nData, void *pData /* The data to be inserted */
-){
- int minNeeded, maxNeeded; /* In u32-sized objects */
- int rc;
- u32 h;
- int available;
- int i, j, k;
- int nKeyU, nDataU;
- u32 *aPage;
- int incr = 1;
-
- /* Null data is the same as a delete.
- */
- if( nData<=0 || pData==0 ){
- if( sqliteDbCursorMoveTo(pCur, nKey, pKey);
- return sqliteDbCursorDelete(pCur);
- }else{
- return SQLITE_OK;
- }
- }
-
- /* Figure out how much free space is needed on a leaf block in order
- ** to hold the new record.
- */
- minNeeded = maxNeeded = 6;
- nKeyU = (nKey+3)/4;
- nDataU = (nData+3)/4;
- if( nKeyU + maxNeeded + 2 <= SQLITE_PAGE_SIZE/sizeof(u32) ){
- maxNeeded += nKeyU;
- }
- if( nKeyU < SQLITE_PAGE_SIZE/(3*sizeof(u32)) ){
- minNeeded += nKeyU;
- }
- if( nDataU + maxNeeded + 2 <= SQLITE_PAGE_SIZE/sizeof(u32) ){
- maxNeeded += nDataU
- }
- if( nDataU < SQLITE_PAGE_SIZE/(3*sizeof(u32)) ){
- minNeeded += nDataU;
- }
-
- /* Move the cursor to the leaf block where the new record will be
- ** inserted.
- */
- h = sqliteDbHash(nKey, pKey);
- rc = sqliteDbFindLeaf(pCur, nKey, pKey, h);
- if( rc!=SQLITE_OK ) return rc;
-
- /* Walk thru the leaf once and do two things:
- ** 1. Remove any prior entry with the same key.
- ** 2. Figure out how much space is available on this leaf.
- */
- i = j = 2;
- aPage = pCur->aLevel[pCur->nLevel-1].aPage;
- for(;;){
- int entrySize = SWB(aPage[i]);
- if( entrySize<=0 || entrySize + i >= SQLITE_PAGE_SIZE/sizeof(u32) ) break;
- if( !sqliteDbKeyMatch(&aPage[i], nKey, pKey, h) ){
- if( j<i ){
- for(k=0; k<entrySize; k++){
- aPage[j+k] = aPage[i+k];
- }
- }
- j += entrySize;
- }else{
- sqliteDbClearEntry(pCur->pDb, &aPage[i]);
- incr--;
- }
- i += entrySize;
- }
- available = SQLITE_PAGE_SIZE/sizeof(u32) - j;
-
- /* If the new entry will not fit, try to move some of the entries
- ** from this leaf onto sibling leaves.
- */
- if( available<minNeeded ){
- int newSpace;
- newSpace = sqliteDbSpreadLoad(pCur, maxNeeded); ############
- available += newSpace;
- }
-
- /* If the new entry still will not fit, try to split this leaf into
- ** two adjacent leaves.
- */
- if( available<minNeeded && pCur->nLevel>1 ){
- int newAvail;
- newAvail = sqliteDbSplit(pCur, maxNeeded); ##############
- if( newAvail>0 ){
- available += newAvail;
- }
- }
-
- /* If the new entry does not fit after splitting, turn this leaf into
- ** and index node with one leaf, go down into the new leaf and try
- ** to split again.
- */
- if( available<minNeeded && pCur->nLevel<MAX_LEVEL-1 ){
- int newAvail;
- sqliteDbNewIndexLevel(pCur); ###############
- newAvail = sqliteDbSplit(pCur, maxNeeded);
- if( newAvail>0 ){
- available = newAvail;
- }
- }
-
- /* If the entry still will not fit, it means the database is full.
- */
- if( available<minNeeded ){
- return SQLITE_FULL;
- }
-
- /* Add the new entry to the leaf block.
- */
- aPage = pCur->aLevel[pCur->nLevel-1].aPage;
- i = 2;
- for(;;){
- int entrySize = SWB(aPage[i]);
- if( entrySize<=0 || entrySize + i >= SQLITE_PAGE_SIZE/sizeof(u32) ) break;
- i += entrySize;
- }
- assert( available==SQLITE_PAGE_SIZE/sizeof(u32) - i );
- aPage[i+1] = SWB(h);
- available -= 5;
- if( nKeyU <= available ){
- aPage[i+2] = SWB(nKey);
- memcpy(&aPage[i+4], pKey, nKey);
- j = i + 4 + nKeyU;
- available -= nKeyU;
- }else{
- u32 newPgno, *newPage;
- aPage[i+2] = SWB(nKey | 0x80000000);
- rc = allocPage(pCur->pDb, &newPgno, &newPage);
- if( rc!=SQLITE_OK ) goto write_err;
- aPage[i+4] = SWB(newPgno);
- newPage[0] = SWB(BLOCK_MAGIC | BLOCK_OVERFLOW);
- rc = sqliteDbWriteOvfl(pCur->pDb, newPage, nKey, pKey);
- if( rc!=SQLITE_OK ) goto write_err;
- j = i + 5;
- available -= 1;
- }
- if( nDataU <= available ){
- aPage[i+3] = SWB(nData);
- memcpy(&aPage[j], pData, nData);
- available -= nDataU;
- j += nDataU;
- }else{
- u32 newPgno, *newPage;
- aPage[i+3] = SWB(nData | 0x80000000);
- rc = allocPage(pCur->pDb, &newPgno, &newPage);
- if( rc!=SQLITE_OK ) goto write_err;
- aPage[j] = SWB(newPgno);
- newPage[0] = SWB(BLOCK_MAGIC | BLOCK_OVERFLOW);
- rc = sqliteDbWriteOvfl(pCur->pDb, newPage, nData, pData);
- if( rc!=SQLITE_OK ) goto write_err;
- available -= 1;
- j++;
- }
- if( j<SQLITE_PAGE_SIZE/sizeof(u32) ){
- aPage[j] = 0;
- }
- sqlitePgTouch(aPage);
- pCur->aLevel[pCur->nLevel-1].idx = i;
- pCur->onEntry = 1;
-
- /* Increment the entry count for this table.
- */
- if( incr!=0 ){
- pCur->aLevel[0].aPage[1] = SWB(SWB(pCur->aLevel[0].aPage[1])+incr);
- sqlitePgTouch(pCur->aLevel[0].aPage);
- }
- return SQLITE_OK;
-
-write_err:
- aPage[i] = 0;
- pCur->onEntry = 0;
- return rc;
-}
-
-/*
-** Delete the entry that the cursor points to.
-*/
-int sqliteDbCursorDelete(DbCursor *pCur){
- int i, idx;
- int from, to, limit, n;
- int entrySize;
- u32 *aPage;
- if( !pCur->onEntry ) return SQLITE_NOTFOUND;
-
- /* Delete the entry that the cursor is pointing to.
- */
- i = pCur->nLevel - 1;
- aPage = pCur->aLevel[i].aPage;
- idx = pCur->aLevel[i].idx;
- assert( SWB(aPage[0])==BLOCK_MAGIC|BLOCK_LEAF );
- assert( idx>=2 && idx<SQLITE_PAGE_SIZE/sizeof(u32)-4 );
- entrySize = SWB(aPage[idx]);
- assert( entrySize>=6 && idx+entrySize<=SQLITE_PAGE_SIZE/sizeof(u32) );
- sqliteDbClearEntry(pCur->pDb, &aPage[idx]);
- to = idx;
- from = idx + entrySize;
- while( from<SQLITE_PAGE_SIZE/sizeof(u32) ){
- int k;
- entrySize = SWB(aPage[from]);
- if( entrySize<=0 ) break;
- for(k=0; k<entrySize; k++){
- aPage[to++] = aPage[from++]
- }
- }
- aPage[to] = 0;
-
- /* Decrement the entry count for this table.
- */
- pCur->aLevel[0].aPage[1] = SWB(SWB(pCur->aLevel[0].aPage[1])-1);
- sqlitePgTouch(pCur->aLevel[0].aPage);
-
- /* If there are more entries on this leaf or this leaf is the root
- ** of the table, then we are done.
- */
- if( to>2 || pCur->nLevel==1 ) return SQLITE_OK;
-
- /* Collapse the tree into a more compact form.
- */
- sqliteDbResetCursor(pCur, pCur->nLevel-1);
-
- i = pCur->nLevel-1;
- assert( i>=0 && i<MAX_LEVEL );
- idx = pCur->aLevel[i].idx;
- aPage = pCur->aLevel[i].aPage;
- assert( SWB(aPage[0])==BLOCK_MAGIC|BLOCK_INDEX );
- assert( idx>=3 && idx<SQLITE_PAGE_SIZE/sizeof(u32) );
- n = SWB(aPage[2]);
- assert( n>=2 && n<=SQLITE_PAGE_SIZE/2*sizeof(u32)-2 );
- sqliteDbDropPage(pCur->pDb, SWB(aPage[idx+1]);
- to = idx;
- from = idx+2;
- limit = n*2 + 3;
- while( from<limit ){
- aPage[to++] = aPage[from++];
- }
- n--;
- if( n==1 ){
- u32 oldPgno, *oldPage;
- oldPgno = SWB(aPage[4]);
- rc = sqlitePgGet(pCur->pDb->pPgr, oldPgno, &oldPage);
- if( rc!=SQLITE_OK ){
- return rc; /* Do something smarter here */
- }
- memcpy(aPage, oldPage, SQLITE_PAGE_SIZE);
- oldPage[0] = SWB(BLOCK_MAGIC|BLOCK_OVERFLOW);
- oldPage[1] = 0;
- sqliteDbDropPage(pCur->pDb, oldPgno);
- sqlitePgUnref(oldPage);
- }else{
- aPage[2] = SWB(n);
- }
- sqlitePgTouch(aPage);
- return SQLITE_OK;
-}
+++ /dev/null
-/*
-** 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);
+++ /dev/null
-/*
-** 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;
-}
+++ /dev/null
-/*
-** 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;
-}
+++ /dev/null
-/*
-** 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;
-}
+++ /dev/null
-/*
-** 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*);
+++ /dev/null
-
-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 ""