DELETED src/ex/README Index: src/ex/README ================================================================== --- src/ex/README +++ /dev/null @@ -1,9 +0,0 @@ -This directory is intended to hold "experimental" files. - -The code in this directory does not necessary work. It may -or may not be added to future SQLite releases. We just need -a place to put ideas and works-in-progress and so this -directory was created. - -If you are interested in the production versions of SQLite, -you can safely ignore this directory. DELETED src/ex/db.c Index: src/ex/db.c ================================================================== --- src/ex/db.c +++ /dev/null @@ -1,1495 +0,0 @@ -/* -** Copyright (c) 2001 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** $Id: db.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; i0; 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; i0; 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(; i0; 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(; j0; 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; i0; 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; i0; 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(; i0; 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(; j0; 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; ipPgr, aPage[0], &indirPage); - if( rc!=SQLITE_OK ) return rc; - for(i=first; ipPgr, aPage[0], &dblIndirPage); - if( rc!=SQLITE_OK ) return rc; - for(i=first/U32_PER_PAGE; ipPgr, dblIndirPage[i], &indirPage); - if( rc!=SQLITE_OK ) return rc; - for(j=first>basis?first-basis:0 ; j 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; inTable; 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; i0 ) sqliteDbDropPage(pDb, subpgno); - } - freePage(pDb, pgno, aPage); - break; - } - case BLOCK_LEAF: { - int i = 2; - while( inLevel-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 && inLevel > 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+4onEntry ) return 0; - i = pCur->nLevel-1; - idx = pCur->aLevel[i].idx; - aPage = pCur->aLevel[i].aPage; - assert( aPage ); - assert( idx>=2 && idx+4onEntry ){ - 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=nData ){ - memset(buf, 0, amt); - return SQLITE_OK; - } - nKey = aPage[idx+2]; - if( nDatapDb, &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( nKeypDb, &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; jaLevel[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( jpDb, &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( availablenLevel>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( availablenLevel0 ){ - available = newAvail; - } - } - - /* If the entry still will not fit, it means the database is full. - */ - if( availableaLevel[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( jaLevel[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=6 && idx+entrySize<=SQLITE_PAGE_SIZE/sizeof(u32) ); - sqliteDbClearEntry(pCur->pDb, &aPage[idx]); - to = idx; - from = idx + entrySize; - while( fromaLevel[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 && iaLevel[i].idx; - aPage = pCur->aLevel[i].aPage; - assert( SWB(aPage[0])==BLOCK_MAGIC|BLOCK_INDEX ); - assert( idx>=3 && idx=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( frompDb->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; -} DELETED src/ex/db.h Index: src/ex/db.h ================================================================== --- src/ex/db.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -** Copyright (c) 2001 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** $Id: db.h,v 1.1 2001/02/11 16:56:24 drh Exp $ -*/ - -typedef struct Db Db; -typedef struct DbCursor DbCursor; - -int sqliteDbOpen(const char *filename, Db**); -int sqliteDbClose(Db*); -int sqliteDbBeginTransaction(Db*); -int sqliteDbCommit(Db*); -int sqliteDbRollback(Db*); - -int sqliteDbCreateTable(Db*, int *pTblno); -int sqliteDbDropTable(Db*, int tblno); - -int sqliteDbCursorOpen(Db*, int tblno, DbCursor**); -int sqliteDbCursorClose(DbCursor*); - -int sqliteDbCursorFirst(DbCursor*); -int sqliteDbCursorNext(DbCursor*); -int sqliteDbCursorDatasize(DbCursor*); -int sqliteDbCursorKeysize(DbCursor*); -int sqliteDbCursorRead(DbCursor*, int amt, int offset, void *buf); -int sqliteDbCursorReadKey(DbCursor*, int amt, int offset, void *buf); -int sqliteDbCursorWrite(DbCursor*, int amt, int offset, const void *buf); - -int sqliteDbCursorFind(DbCursor*, int nKey, const void *pKey, int createFlag); -int sqliteDbCursorResize(DbCursor*, int nData); DELETED src/ex/dbbebdb1.c Index: src/ex/dbbebdb1.c ================================================================== --- src/ex/dbbebdb1.c +++ /dev/null @@ -1,618 +0,0 @@ -/* -** Copyright (c) 2000 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** This file contains code to implement the database backend (DBBE) -** for sqlite. The database backend is the interface between -** sqlite and the code that does the actually reading and writing -** of information to the disk. -** -** This file uses Berkeley Database version 1.85 as the database backend. -** -** $Id: dbbebdb1.c,v 1.1 2001/02/11 16:56:24 drh Exp $ -*/ -#ifdef USE_BDB2 - -#include "sqliteInt.h" -#include -#include -#include -#include -#include -#include -#include - -/* -** 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; -} DELETED src/ex/dbbemird.c Index: src/ex/dbbemird.c ================================================================== --- src/ex/dbbemird.c +++ /dev/null @@ -1,776 +0,0 @@ -/* -** Copyright (c) 2000 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** This file contains code to implement the database backend (DBBE) -** for sqlite. The database backend is the interface between -** sqlite and the code that does the actually reading and writing -** of information to the disk. -** -** This file uses GDBM as the database backend. It should be -** relatively simple to convert to a different database such -** as NDBM, SDBM, or BerkeleyDB. -** -** $Id: dbbemird.c,v 1.1 2001/02/11 16:56:24 drh Exp $ -*/ -#include "sqliteInt.h" -#include -#include -#include -#include -#include - -/* -** 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; inTemp; 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; inTemp; 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; inTemp; 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; -} DELETED src/ex/pg.c Index: src/ex/pg.c ================================================================== --- src/ex/pg.c +++ /dev/null @@ -1,708 +0,0 @@ -/* -** Copyright (c) 2001 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** $Id: pg.c,v 1.1 2001/02/11 16:56:24 drh Exp $ -*/ -#include -#include -#include -#include -#include -#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( got0 ); - assert( fd>=0 ); - lseek(fd, SEEK_SET, (pgno-1)*SQLITE_PAGE_SIZE); - while( done>= 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; ifdMain, 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->nMemPgpLru==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; -} DELETED src/ex/pg.h Index: src/ex/pg.h ================================================================== --- src/ex/pg.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -** Copyright (c) 2001 D. Richard Hipp -** -** This program is free software; you can redistribute it and/or -** modify it under the terms of the GNU General Public -** License as published by the Free Software Foundation; either -** version 2 of the License, or (at your option) any later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -** General Public License for more details. -** -** You should have received a copy of the GNU General Public -** License along with this library; if not, write to the -** Free Software Foundation, Inc., 59 Temple Place - Suite 330, -** Boston, MA 02111-1307, USA. -** -** Author contact information: -** drh@hwaci.com -** http://www.hwaci.com/drh/ -** -************************************************************************* -** $Id: pg.h,v 1.1 2001/02/11 16:56:24 drh Exp $ -*/ - -typedef struct Pgr Pgr; -#define SQLITE_PAGE_SIZE 1024 - - -int sqlitePgOpen(const char *filename, Pgr **pp); -int sqlitePgClose(Pgr*); -int sqlitePgBeginTransaction(Pgr*); -int sqlitePgCommit(Pgr*); -int sqlitePgRollback(Pgr*); -int sqlitePgGet(Pgr*, u32 pgno, void **); -int sqlitePgUnref(void*); -int sqlitePgTouch(void*); -int sqlitePgCount(Pgr*, u32*); -u32 sqlitePgNum(void*); DELETED src/ex/sizes.tcl Index: src/ex/sizes.tcl ================================================================== --- src/ex/sizes.tcl +++ /dev/null @@ -1,41 +0,0 @@ - -set sizes {1024 2048 4096 8192 16384 32768} -set fmt { %-8s} - -puts -nonewline "page size: " -foreach s $sizes { - puts -nonewline [format $fmt $s] -} -puts "" - -puts -nonewline "on leaf: " -foreach s $sizes { - set x [expr {$s - 18*4}] - set p($s) $x - puts -nonewline [format $fmt $x] -} -puts "" - -puts -nonewline "direct: " -foreach s $sizes { - set x [expr {$p($s) + 10*$s}] - set p($s) $x - puts -nonewline [format $fmt $x] -} -puts "" - -puts -nonewline "indirect: " -foreach s $sizes { - set x [expr {$p($s) + ($s/4.0)*$s}] - set p($s) $x - puts -nonewline [format $fmt $x] -} -puts "" - -puts -nonewline "dbl indir: " -foreach s $sizes { - set x [expr {$p($s) + ($s/4.0)*($s/4)*$s}] - set p($s) $x - puts -nonewline [format $fmt $x] -} -puts ""