summaryrefslogtreecommitdiff
path: root/ext/sqlite/libsqlite/src/pager.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/sqlite/libsqlite/src/pager.c')
-rw-r--r--ext/sqlite/libsqlite/src/pager.c2022
1 files changed, 2022 insertions, 0 deletions
diff --git a/ext/sqlite/libsqlite/src/pager.c b/ext/sqlite/libsqlite/src/pager.c
new file mode 100644
index 0000000000..7b32f6e089
--- /dev/null
+++ b/ext/sqlite/libsqlite/src/pager.c
@@ -0,0 +1,2022 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the implementation of the page cache subsystem or "pager".
+**
+** The pager is used to access a database disk file. It implements
+** atomic commit and rollback through the use of a journal file that
+** is separate from the database file. The pager also implements file
+** locking to prevent two processes from writing the same database
+** file simultaneously, or one process from reading the database while
+** another is writing.
+**
+** @(#) $Id$
+*/
+#include "os.h" /* Must be first to enable large file support */
+#include "sqliteInt.h"
+#include "pager.h"
+#include <assert.h>
+#include <string.h>
+
+/*
+** Macros for troubleshooting. Normally turned off
+*/
+#if 0
+static Pager *mainPager = 0;
+#define SET_PAGER(X) if( mainPager==0 ) mainPager = (X)
+#define CLR_PAGER(X) if( mainPager==(X) ) mainPager = 0
+#define TRACE1(X) if( pPager==mainPager ) fprintf(stderr,X)
+#define TRACE2(X,Y) if( pPager==mainPager ) fprintf(stderr,X,Y)
+#define TRACE3(X,Y,Z) if( pPager==mainPager ) fprintf(stderr,X,Y,Z)
+#else
+#define SET_PAGER(X)
+#define CLR_PAGER(X)
+#define TRACE1(X)
+#define TRACE2(X,Y)
+#define TRACE3(X,Y,Z)
+#endif
+
+
+/*
+** The page cache as a whole is always in one of the following
+** states:
+**
+** SQLITE_UNLOCK The page cache is not currently reading or
+** writing the database file. There is no
+** data held in memory. This is the initial
+** state.
+**
+** SQLITE_READLOCK The page cache is reading the database.
+** Writing is not permitted. There can be
+** multiple readers accessing the same database
+** file at the same time.
+**
+** SQLITE_WRITELOCK The page cache is writing the database.
+** Access is exclusive. No other processes or
+** threads can be reading or writing while one
+** process is writing.
+**
+** The page cache comes up in SQLITE_UNLOCK. The first time a
+** sqlite_page_get() occurs, the state transitions to SQLITE_READLOCK.
+** After all pages have been released using sqlite_page_unref(),
+** the state transitions back to SQLITE_UNLOCK. The first time
+** that sqlite_page_write() is called, the state transitions to
+** SQLITE_WRITELOCK. (Note that sqlite_page_write() can only be
+** called on an outstanding page which means that the pager must
+** be in SQLITE_READLOCK before it transitions to SQLITE_WRITELOCK.)
+** The sqlite_page_rollback() and sqlite_page_commit() functions
+** transition the state from SQLITE_WRITELOCK back to SQLITE_READLOCK.
+*/
+#define SQLITE_UNLOCK 0
+#define SQLITE_READLOCK 1
+#define SQLITE_WRITELOCK 2
+
+
+/*
+** Each in-memory image of a page begins with the following header.
+** This header is only visible to this pager module. The client
+** code that calls pager sees only the data that follows the header.
+*/
+typedef struct PgHdr PgHdr;
+struct PgHdr {
+ Pager *pPager; /* The pager to which this page belongs */
+ Pgno pgno; /* The page number for this page */
+ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
+ int nRef; /* Number of users of this page */
+ PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */
+ PgHdr *pNextAll, *pPrevAll; /* A list of all pages */
+ PgHdr *pNextCkpt, *pPrevCkpt; /* List of pages in the checkpoint journal */
+ u8 inJournal; /* TRUE if has been written to journal */
+ u8 inCkpt; /* TRUE if written to the checkpoint journal */
+ u8 dirty; /* TRUE if we need to write back changes */
+ u8 needSync; /* Sync journal before writing this page */
+ u8 alwaysRollback; /* Disable dont_rollback() for this page */
+ PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */
+ /* SQLITE_PAGE_SIZE bytes of page data follow this header */
+ /* Pager.nExtra bytes of local data follow the page data */
+};
+
+/*
+** Convert a pointer to a PgHdr into a pointer to its data
+** and back again.
+*/
+#define PGHDR_TO_DATA(P) ((void*)(&(P)[1]))
+#define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1])
+#define PGHDR_TO_EXTRA(P) ((void*)&((char*)(&(P)[1]))[SQLITE_PAGE_SIZE])
+
+/*
+** How big to make the hash table used for locating in-memory pages
+** by page number.
+*/
+#define N_PG_HASH 2048
+
+/*
+** Hash a page number
+*/
+#define pager_hash(PN) ((PN)&(N_PG_HASH-1))
+
+/*
+** A open page cache is an instance of the following structure.
+*/
+struct Pager {
+ char *zFilename; /* Name of the database file */
+ char *zJournal; /* Name of the journal file */
+ OsFile fd, jfd; /* File descriptors for database and journal */
+ OsFile cpfd; /* File descriptor for the checkpoint journal */
+ int dbSize; /* Number of pages in the file */
+ int origDbSize; /* dbSize before the current change */
+ int ckptSize; /* Size of database (in pages) at ckpt_begin() */
+ off_t ckptJSize; /* Size of journal at ckpt_begin() */
+ int nRec; /* Number of pages written to the journal */
+ u32 cksumInit; /* Quasi-random value added to every checksum */
+ int ckptNRec; /* Number of records in the checkpoint journal */
+ int nExtra; /* Add this many bytes to each in-memory page */
+ void (*xDestructor)(void*); /* Call this routine when freeing pages */
+ int nPage; /* Total number of in-memory pages */
+ int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
+ int mxPage; /* Maximum number of pages to hold in cache */
+ int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */
+ u8 journalOpen; /* True if journal file descriptors is valid */
+ u8 journalStarted; /* True if initial magic of journal is synced */
+ u8 useJournal; /* Do not use a rollback journal on this file */
+ u8 ckptOpen; /* True if the checkpoint journal is open */
+ u8 ckptInUse; /* True we are in a checkpoint */
+ u8 ckptAutoopen; /* Open ckpt journal when main journal is opened*/
+ u8 noSync; /* Do not sync the journal if true */
+ u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
+ u8 errMask; /* One of several kinds of errors */
+ u8 tempFile; /* zFilename is a temporary file */
+ u8 readOnly; /* True for a read-only database */
+ u8 needSync; /* True if an fsync() is needed on the journal */
+ u8 dirtyFile; /* True if database file has changed in any way */
+ u8 alwaysRollback; /* Disable dont_rollback() for all pages */
+ u8 *aInJournal; /* One bit for each page in the database file */
+ u8 *aInCkpt; /* One bit for each page in the database */
+ PgHdr *pFirst, *pLast; /* List of free pages */
+ PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */
+ PgHdr *pAll; /* List of all pages */
+ PgHdr *pCkpt; /* List of pages in the checkpoint journal */
+ PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */
+};
+
+/*
+** These are bits that can be set in Pager.errMask.
+*/
+#define PAGER_ERR_FULL 0x01 /* a write() failed */
+#define PAGER_ERR_MEM 0x02 /* malloc() failed */
+#define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */
+#define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */
+#define PAGER_ERR_DISK 0x10 /* general disk I/O error - bad hard drive? */
+
+/*
+** The journal file contains page records in the following
+** format.
+**
+** Actually, this structure is the complete page record for pager
+** formats less than 3. Beginning with format 3, this record is surrounded
+** by two checksums.
+*/
+typedef struct PageRecord PageRecord;
+struct PageRecord {
+ Pgno pgno; /* The page number */
+ char aData[SQLITE_PAGE_SIZE]; /* Original data for page pgno */
+};
+
+/*
+** Journal files begin with the following magic string. The data
+** was obtained from /dev/random. It is used only as a sanity check.
+**
+** There are three journal formats (so far). The 1st journal format writes
+** 32-bit integers in the byte-order of the host machine. New
+** formats writes integers as big-endian. All new journals use the
+** new format, but we have to be able to read an older journal in order
+** to rollback journals created by older versions of the library.
+**
+** The 3rd journal format (added for 2.8.0) adds additional sanity
+** checking information to the journal. If the power fails while the
+** journal is being written, semi-random garbage data might appear in
+** the journal file after power is restored. If an attempt is then made
+** to roll the journal back, the database could be corrupted. The additional
+** sanity checking data is an attempt to discover the garbage in the
+** journal and ignore it.
+**
+** The sanity checking information for the 3rd journal format consists
+** of a 32-bit checksum on each page of data. The checksum covers both
+** the page number and the SQLITE_PAGE_SIZE bytes of data for the page.
+** This cksum is initialized to a 32-bit random value that appears in the
+** journal file right after the header. The random initializer is important,
+** because garbage data that appears at the end of a journal is likely
+** data that was once in other files that have now been deleted. If the
+** garbage data came from an obsolete journal file, the checksums might
+** be correct. But by initializing the checksum to random value which
+** is different for every journal, we minimize that risk.
+*/
+static const unsigned char aJournalMagic1[] = {
+ 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd4,
+};
+static const unsigned char aJournalMagic2[] = {
+ 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd5,
+};
+static const unsigned char aJournalMagic3[] = {
+ 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd6,
+};
+#define JOURNAL_FORMAT_1 1
+#define JOURNAL_FORMAT_2 2
+#define JOURNAL_FORMAT_3 3
+
+/*
+** The following integer determines what format to use when creating
+** new primary journal files. By default we always use format 3.
+** When testing, we can set this value to older journal formats in order to
+** make sure that newer versions of the library are able to rollback older
+** journal files.
+**
+** Note that checkpoint journals always use format 2 and omit the header.
+*/
+#ifdef SQLITE_TEST
+int journal_format = 3;
+#else
+# define journal_format 3
+#endif
+
+/*
+** The size of the header and of each page in the journal varies according
+** to which journal format is being used. The following macros figure out
+** the sizes based on format numbers.
+*/
+#define JOURNAL_HDR_SZ(X) \
+ (sizeof(aJournalMagic1) + sizeof(Pgno) + ((X)>=3)*2*sizeof(u32))
+#define JOURNAL_PG_SZ(X) \
+ (SQLITE_PAGE_SIZE + sizeof(Pgno) + ((X)>=3)*sizeof(u32))
+
+/*
+** Enable reference count tracking here:
+*/
+#ifdef SQLITE_TEST
+ int pager_refinfo_enable = 0;
+ static void pager_refinfo(PgHdr *p){
+ static int cnt = 0;
+ if( !pager_refinfo_enable ) return;
+ printf(
+ "REFCNT: %4d addr=0x%08x nRef=%d\n",
+ p->pgno, (int)PGHDR_TO_DATA(p), p->nRef
+ );
+ cnt++; /* Something to set a breakpoint on */
+ }
+# define REFINFO(X) pager_refinfo(X)
+#else
+# define REFINFO(X)
+#endif
+
+/*
+** Read a 32-bit integer from the given file descriptor
+*/
+static int read32bits(int format, OsFile *fd, u32 *pRes){
+ u32 res;
+ int rc;
+ rc = sqliteOsRead(fd, &res, sizeof(res));
+ if( rc==SQLITE_OK && format>JOURNAL_FORMAT_1 ){
+ unsigned char ac[4];
+ memcpy(ac, &res, 4);
+ res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3];
+ }
+ *pRes = res;
+ return rc;
+}
+
+/*
+** Write a 32-bit integer into the given file descriptor. Writing
+** is always done using the new journal format.
+*/
+static int write32bits(OsFile *fd, u32 val){
+ unsigned char ac[4];
+ if( journal_format<=1 ){
+ return sqliteOsWrite(fd, &val, 4);
+ }
+ ac[0] = (val>>24) & 0xff;
+ ac[1] = (val>>16) & 0xff;
+ ac[2] = (val>>8) & 0xff;
+ ac[3] = val & 0xff;
+ return sqliteOsWrite(fd, ac, 4);
+}
+
+/*
+** Write a 32-bit integer into a page header right before the
+** page data. This will overwrite the PgHdr.pDirty pointer.
+*/
+static void store32bits(u32 val, PgHdr *p, int offset){
+ unsigned char *ac;
+ ac = &((char*)PGHDR_TO_DATA(p))[offset];
+ if( journal_format<=1 ){
+ memcpy(ac, &val, 4);
+ }else{
+ ac[0] = (val>>24) & 0xff;
+ ac[1] = (val>>16) & 0xff;
+ ac[2] = (val>>8) & 0xff;
+ ac[3] = val & 0xff;
+ }
+}
+
+
+/*
+** Convert the bits in the pPager->errMask into an approprate
+** return code.
+*/
+static int pager_errcode(Pager *pPager){
+ int rc = SQLITE_OK;
+ if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL;
+ if( pPager->errMask & PAGER_ERR_DISK ) rc = SQLITE_IOERR;
+ if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL;
+ if( pPager->errMask & PAGER_ERR_MEM ) rc = SQLITE_NOMEM;
+ if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT;
+ return rc;
+}
+
+/*
+** Add or remove a page from the list of all pages that are in the
+** checkpoint journal.
+**
+** The Pager keeps a separate list of pages that are currently in
+** the checkpoint journal. This helps the sqlitepager_ckpt_commit()
+** routine run MUCH faster for the common case where there are many
+** pages in memory but only a few are in the checkpoint journal.
+*/
+static void page_add_to_ckpt_list(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ if( pPg->inCkpt ) return;
+ assert( pPg->pPrevCkpt==0 && pPg->pNextCkpt==0 );
+ pPg->pPrevCkpt = 0;
+ if( pPager->pCkpt ){
+ pPager->pCkpt->pPrevCkpt = pPg;
+ }
+ pPg->pNextCkpt = pPager->pCkpt;
+ pPager->pCkpt = pPg;
+ pPg->inCkpt = 1;
+}
+static void page_remove_from_ckpt_list(PgHdr *pPg){
+ if( !pPg->inCkpt ) return;
+ if( pPg->pPrevCkpt ){
+ assert( pPg->pPrevCkpt->pNextCkpt==pPg );
+ pPg->pPrevCkpt->pNextCkpt = pPg->pNextCkpt;
+ }else{
+ assert( pPg->pPager->pCkpt==pPg );
+ pPg->pPager->pCkpt = pPg->pNextCkpt;
+ }
+ if( pPg->pNextCkpt ){
+ assert( pPg->pNextCkpt->pPrevCkpt==pPg );
+ pPg->pNextCkpt->pPrevCkpt = pPg->pPrevCkpt;
+ }
+ pPg->pNextCkpt = 0;
+ pPg->pPrevCkpt = 0;
+ pPg->inCkpt = 0;
+}
+
+/*
+** Find a page in the hash table given its page number. Return
+** a pointer to the page or NULL if not found.
+*/
+static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
+ PgHdr *p = pPager->aHash[pager_hash(pgno)];
+ while( p && p->pgno!=pgno ){
+ p = p->pNextHash;
+ }
+ return p;
+}
+
+/*
+** Unlock the database and clear the in-memory cache. This routine
+** sets the state of the pager back to what it was when it was first
+** opened. Any outstanding pages are invalidated and subsequent attempts
+** to access those pages will likely result in a coredump.
+*/
+static void pager_reset(Pager *pPager){
+ PgHdr *pPg, *pNext;
+ for(pPg=pPager->pAll; pPg; pPg=pNext){
+ pNext = pPg->pNextAll;
+ sqliteFree(pPg);
+ }
+ pPager->pFirst = 0;
+ pPager->pFirstSynced = 0;
+ pPager->pLast = 0;
+ pPager->pAll = 0;
+ memset(pPager->aHash, 0, sizeof(pPager->aHash));
+ pPager->nPage = 0;
+ if( pPager->state>=SQLITE_WRITELOCK ){
+ sqlitepager_rollback(pPager);
+ }
+ sqliteOsUnlock(&pPager->fd);
+ pPager->state = SQLITE_UNLOCK;
+ pPager->dbSize = -1;
+ pPager->nRef = 0;
+ assert( pPager->journalOpen==0 );
+}
+
+/*
+** When this routine is called, the pager has the journal file open and
+** a write lock on the database. This routine releases the database
+** write lock and acquires a read lock in its place. The journal file
+** is deleted and closed.
+*/
+static int pager_unwritelock(Pager *pPager){
+ int rc;
+ PgHdr *pPg;
+ if( pPager->state<SQLITE_WRITELOCK ) return SQLITE_OK;
+ sqlitepager_ckpt_commit(pPager);
+ if( pPager->ckptOpen ){
+ sqliteOsClose(&pPager->cpfd);
+ pPager->ckptOpen = 0;
+ }
+ if( pPager->journalOpen ){
+ sqliteOsClose(&pPager->jfd);
+ pPager->journalOpen = 0;
+ sqliteOsDelete(pPager->zJournal);
+ sqliteFree( pPager->aInJournal );
+ pPager->aInJournal = 0;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ pPg->inJournal = 0;
+ pPg->dirty = 0;
+ pPg->needSync = 0;
+ }
+ }else{
+ assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
+ }
+ rc = sqliteOsReadLock(&pPager->fd);
+ if( rc==SQLITE_OK ){
+ pPager->state = SQLITE_READLOCK;
+ }else{
+ /* This can only happen if a process does a BEGIN, then forks and the
+ ** child process does the COMMIT. Because of the semantics of unix
+ ** file locking, the unlock will fail.
+ */
+ pPager->state = SQLITE_UNLOCK;
+ }
+ return rc;
+}
+
+/*
+** Compute and return a checksum for the page of data.
+*/
+static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
+ u32 cksum = pPager->cksumInit + pgno;
+ return cksum;
+}
+
+/*
+** Read a single page from the journal file opened on file descriptor
+** jfd. Playback this one page.
+**
+** There are three different journal formats. The format parameter determines
+** which format is used by the journal that is played back.
+*/
+static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
+ int rc;
+ PgHdr *pPg; /* An existing page in the cache */
+ PageRecord pgRec;
+ u32 cksum;
+
+ rc = read32bits(format, jfd, &pgRec.pgno);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqliteOsRead(jfd, &pgRec.aData, sizeof(pgRec.aData));
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Sanity checking on the page. This is more important that I originally
+ ** thought. If a power failure occurs while the journal is being written,
+ ** it could cause invalid data to be written into the journal. We need to
+ ** detect this invalid data (with high probability) and ignore it.
+ */
+ if( pgRec.pgno==0 ){
+ return SQLITE_DONE;
+ }
+ if( pgRec.pgno>pPager->dbSize ){
+ return SQLITE_OK;
+ }
+ if( format>=JOURNAL_FORMAT_3 ){
+ rc = read32bits(format, jfd, &cksum);
+ if( rc ) return rc;
+ if( pager_cksum(pPager, pgRec.pgno, pgRec.aData)!=cksum ){
+ return SQLITE_DONE;
+ }
+ }
+
+ /* Playback the page. Update the in-memory copy of the page
+ ** at the same time, if there is one.
+ */
+ pPg = pager_lookup(pPager, pgRec.pgno);
+ TRACE2("PLAYBACK %d\n", pgRec.pgno);
+ sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*(off_t)SQLITE_PAGE_SIZE);
+ rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
+ if( pPg ){
+ if( pPg->nRef==0 ||
+ memcmp(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE)==0
+ ){
+ /* Do not update the data on this page if the page is in use
+ ** and the page has never been modified. This avoids resetting
+ ** the "extra" data. That in turn avoids invalidating BTree cursors
+ ** in trees that have never been modified. The end result is that
+ ** you can have a SELECT going on in one table and ROLLBACK changes
+ ** to a different table and the SELECT is unaffected by the ROLLBACK.
+ */
+ memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
+ memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
+ }
+ pPg->dirty = 0;
+ pPg->needSync = 0;
+ }
+ return rc;
+}
+
+/*
+** Playback the journal and thus restore the database file to
+** the state it was in before we started making changes.
+**
+** The journal file format is as follows: There is an initial
+** file-type string for sanity checking. Then there is a single
+** Pgno number which is the number of pages in the database before
+** changes were made. The database is truncated to this size.
+** Next come zero or more page records where each page record
+** consists of a Pgno and SQLITE_PAGE_SIZE bytes of data. See
+** the PageRecord structure for details.
+**
+** If the file opened as the journal file is not a well-formed
+** journal file (as determined by looking at the magic number
+** at the beginning) then this routine returns SQLITE_PROTOCOL.
+** If any other errors occur during playback, the database will
+** likely be corrupted, so the PAGER_ERR_CORRUPT bit is set in
+** pPager->errMask and SQLITE_CORRUPT is returned. If it all
+** works, then this routine returns SQLITE_OK.
+*/
+static int pager_playback(Pager *pPager, int useJournalSize){
+ off_t szJ; /* Size of the journal file in bytes */
+ int nRec; /* Number of Records in the journal */
+ int i; /* Loop counter */
+ Pgno mxPg = 0; /* Size of the original file in pages */
+ int format; /* Format of the journal file. */
+ unsigned char aMagic[sizeof(aJournalMagic1)];
+ int rc;
+
+ /* Figure out how many records are in the journal. Abort early if
+ ** the journal is empty.
+ */
+ assert( pPager->journalOpen );
+ sqliteOsSeek(&pPager->jfd, 0);
+ rc = sqliteOsFileSize(&pPager->jfd, &szJ);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+ if( szJ < sizeof(aMagic)+sizeof(Pgno) ){
+ goto end_playback;
+ }
+
+ /* Read the beginning of the journal and truncate the
+ ** database file back to its original size.
+ */
+ rc = sqliteOsRead(&pPager->jfd, aMagic, sizeof(aMagic));
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_PROTOCOL;
+ goto end_playback;
+ }
+ if( memcmp(aMagic, aJournalMagic3, sizeof(aMagic))==0 ){
+ format = JOURNAL_FORMAT_3;
+ }else if( memcmp(aMagic, aJournalMagic2, sizeof(aMagic))==0 ){
+ format = JOURNAL_FORMAT_2;
+ }else if( memcmp(aMagic, aJournalMagic1, sizeof(aMagic))==0 ){
+ format = JOURNAL_FORMAT_1;
+ }else{
+ rc = SQLITE_PROTOCOL;
+ goto end_playback;
+ }
+ if( format>=JOURNAL_FORMAT_3 ){
+ rc = read32bits(format, &pPager->jfd, &nRec);
+ if( rc ) goto end_playback;
+ rc = read32bits(format, &pPager->jfd, &pPager->cksumInit);
+ if( rc ) goto end_playback;
+ if( nRec==0xffffffff || useJournalSize ){
+ nRec = (szJ - JOURNAL_HDR_SZ(3))/JOURNAL_PG_SZ(3);
+ }
+ }else{
+ nRec = (szJ - JOURNAL_HDR_SZ(2))/JOURNAL_PG_SZ(2);
+ assert( nRec*JOURNAL_PG_SZ(2)+JOURNAL_HDR_SZ(2)==szJ );
+ }
+ rc = read32bits(format, &pPager->jfd, &mxPg);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+ assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg );
+ rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)mxPg);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+ pPager->dbSize = mxPg;
+
+ /* Copy original pages out of the journal and back into the database file.
+ */
+ for(i=0; i<nRec; i++){
+ rc = pager_playback_one_page(pPager, &pPager->jfd, format);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ }
+ break;
+ }
+ }
+
+ /* Pages that have been written to the journal but never synced
+ ** where not restored by the loop above. We have to restore those
+ ** pages by reading the back from the original database.
+ */
+ if( rc==SQLITE_OK ){
+ PgHdr *pPg;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ char zBuf[SQLITE_PAGE_SIZE];
+ if( !pPg->dirty ) continue;
+ if( (int)pPg->pgno <= pPager->origDbSize ){
+ sqliteOsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1));
+ rc = sqliteOsRead(&pPager->fd, zBuf, SQLITE_PAGE_SIZE);
+ if( rc ) break;
+ }else{
+ memset(zBuf, 0, SQLITE_PAGE_SIZE);
+ }
+ if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE) ){
+ memcpy(PGHDR_TO_DATA(pPg), zBuf, SQLITE_PAGE_SIZE);
+ memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
+ }
+ pPg->needSync = 0;
+ pPg->dirty = 0;
+ }
+ }
+
+end_playback:
+ if( rc!=SQLITE_OK ){
+ pager_unwritelock(pPager);
+ pPager->errMask |= PAGER_ERR_CORRUPT;
+ rc = SQLITE_CORRUPT;
+ }else{
+ rc = pager_unwritelock(pPager);
+ }
+ return rc;
+}
+
+/*
+** Playback the checkpoint journal.
+**
+** This is similar to playing back the transaction journal but with
+** a few extra twists.
+**
+** (1) The number of pages in the database file at the start of
+** the checkpoint is stored in pPager->ckptSize, not in the
+** journal file itself.
+**
+** (2) In addition to playing back the checkpoint journal, also
+** playback all pages of the transaction journal beginning
+** at offset pPager->ckptJSize.
+*/
+static int pager_ckpt_playback(Pager *pPager){
+ off_t szJ; /* Size of the full journal */
+ int nRec; /* Number of Records */
+ int i; /* Loop counter */
+ int rc;
+
+ /* Truncate the database back to its original size.
+ */
+ rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)pPager->ckptSize);
+ pPager->dbSize = pPager->ckptSize;
+
+ /* Figure out how many records are in the checkpoint journal.
+ */
+ assert( pPager->ckptInUse && pPager->journalOpen );
+ sqliteOsSeek(&pPager->cpfd, 0);
+ nRec = pPager->ckptNRec;
+
+ /* Copy original pages out of the checkpoint journal and back into the
+ ** database file. Note that the checkpoint journal always uses format
+ ** 2 instead of format 3 since it does not need to be concerned with
+ ** power failures corrupting the journal and can thus omit the checksums.
+ */
+ for(i=nRec-1; i>=0; i--){
+ rc = pager_playback_one_page(pPager, &pPager->cpfd, 2);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_ckpt_playback;
+ }
+
+ /* Figure out how many pages need to be copied out of the transaction
+ ** journal.
+ */
+ rc = sqliteOsSeek(&pPager->jfd, pPager->ckptJSize);
+ if( rc!=SQLITE_OK ){
+ goto end_ckpt_playback;
+ }
+ rc = sqliteOsFileSize(&pPager->jfd, &szJ);
+ if( rc!=SQLITE_OK ){
+ goto end_ckpt_playback;
+ }
+ nRec = (szJ - pPager->ckptJSize)/JOURNAL_PG_SZ(journal_format);
+ for(i=nRec-1; i>=0; i--){
+ rc = pager_playback_one_page(pPager, &pPager->jfd, journal_format);
+ if( rc!=SQLITE_OK ){
+ assert( rc!=SQLITE_DONE );
+ goto end_ckpt_playback;
+ }
+ }
+
+end_ckpt_playback:
+ if( rc!=SQLITE_OK ){
+ pPager->errMask |= PAGER_ERR_CORRUPT;
+ rc = SQLITE_CORRUPT;
+ }
+ return rc;
+}
+
+/*
+** Change the maximum number of in-memory pages that are allowed.
+**
+** The maximum number is the absolute value of the mxPage parameter.
+** If mxPage is negative, the noSync flag is also set. noSync bypasses
+** calls to sqliteOsSync(). The pager runs much faster with noSync on,
+** but if the operating system crashes or there is an abrupt power
+** failure, the database file might be left in an inconsistent and
+** unrepairable state.
+*/
+void sqlitepager_set_cachesize(Pager *pPager, int mxPage){
+ if( mxPage>=0 ){
+ pPager->noSync = pPager->tempFile;
+ }else{
+ pPager->noSync = 1;
+ mxPage = -mxPage;
+ }
+ if( mxPage>10 ){
+ pPager->mxPage = mxPage;
+ }
+}
+
+/*
+** Adjust the robustness of the database to damage due to OS crashes
+** or power failures by changing the number of syncs()s when writing
+** the rollback journal. There are three levels:
+**
+** OFF sqliteOsSync() is never called. This is the default
+** for temporary and transient files.
+**
+** NORMAL The journal is synced once before writes begin on the
+** database. This is normally adequate protection, but
+** it is theoretically possible, though very unlikely,
+** that an inopertune power failure could leave the journal
+** in a state which would cause damage to the database
+** when it is rolled back.
+**
+** FULL The journal is synced twice before writes begin on the
+** database (with some additional information being written
+** in between the two syncs. If we assume that writing a
+** single disk sector is atomic, then this mode provides
+** assurance that the journal will not be corrupted to the
+** point of causing damage to the database during rollback.
+**
+** Numeric values associated with these states are OFF==1, NORMAL=2,
+** and FULL=3.
+*/
+void sqlitepager_set_safety_level(Pager *pPager, int level){
+ pPager->noSync = level==1 || pPager->tempFile;
+ pPager->fullSync = level==3 && !pPager->tempFile;
+}
+
+/*
+** Open a temporary file. Write the name of the file into zName
+** (zName must be at least SQLITE_TEMPNAME_SIZE bytes long.) Write
+** the file descriptor into *fd. Return SQLITE_OK on success or some
+** other error code if we fail.
+**
+** The OS will automatically delete the temporary file when it is
+** closed.
+*/
+static int sqlitepager_opentemp(char *zFile, OsFile *fd){
+ int cnt = 8;
+ int rc;
+ do{
+ cnt--;
+ sqliteOsTempFileName(zFile);
+ rc = sqliteOsOpenExclusive(zFile, fd, 1);
+ }while( cnt>0 && rc!=SQLITE_OK );
+ return rc;
+}
+
+/*
+** Create a new page cache and put a pointer to the page cache in *ppPager.
+** The file to be cached need not exist. The file is not locked until
+** the first call to sqlitepager_get() and is only held open until the
+** last page is released using sqlitepager_unref().
+**
+** If zFilename is NULL then a randomly-named temporary file is created
+** and used as the file to be cached. The file will be deleted
+** automatically when it is closed.
+*/
+int sqlitepager_open(
+ Pager **ppPager, /* Return the Pager structure here */
+ const char *zFilename, /* Name of the database file to open */
+ int mxPage, /* Max number of in-memory cache pages */
+ int nExtra, /* Extra bytes append to each in-memory page */
+ int useJournal /* TRUE to use a rollback journal on this file */
+){
+ Pager *pPager;
+ char *zFullPathname;
+ int nameLen;
+ OsFile fd;
+ int rc;
+ int tempFile;
+ int readOnly = 0;
+ char zTemp[SQLITE_TEMPNAME_SIZE];
+
+ *ppPager = 0;
+ if( sqlite_malloc_failed ){
+ return SQLITE_NOMEM;
+ }
+ if( zFilename ){
+ zFullPathname = sqliteOsFullPathname(zFilename);
+ rc = sqliteOsOpenReadWrite(zFullPathname, &fd, &readOnly);
+ tempFile = 0;
+ }else{
+ rc = sqlitepager_opentemp(zTemp, &fd);
+ zFilename = zTemp;
+ zFullPathname = sqliteOsFullPathname(zFilename);
+ tempFile = 1;
+ }
+ if( sqlite_malloc_failed ){
+ return SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ sqliteFree(zFullPathname);
+ return SQLITE_CANTOPEN;
+ }
+ nameLen = strlen(zFullPathname);
+ pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 );
+ if( pPager==0 ){
+ sqliteOsClose(&fd);
+ sqliteFree(zFullPathname);
+ return SQLITE_NOMEM;
+ }
+ SET_PAGER(pPager);
+ pPager->zFilename = (char*)&pPager[1];
+ pPager->zJournal = &pPager->zFilename[nameLen+1];
+ strcpy(pPager->zFilename, zFullPathname);
+ strcpy(pPager->zJournal, zFullPathname);
+ sqliteFree(zFullPathname);
+ strcpy(&pPager->zJournal[nameLen], "-journal");
+ pPager->fd = fd;
+ pPager->journalOpen = 0;
+ pPager->useJournal = useJournal;
+ pPager->ckptOpen = 0;
+ pPager->ckptInUse = 0;
+ pPager->nRef = 0;
+ pPager->dbSize = -1;
+ pPager->ckptSize = 0;
+ pPager->ckptJSize = 0;
+ pPager->nPage = 0;
+ pPager->mxPage = mxPage>5 ? mxPage : 10;
+ pPager->state = SQLITE_UNLOCK;
+ pPager->errMask = 0;
+ pPager->tempFile = tempFile;
+ pPager->readOnly = readOnly;
+ pPager->needSync = 0;
+ pPager->noSync = pPager->tempFile || !useJournal;
+ pPager->pFirst = 0;
+ pPager->pFirstSynced = 0;
+ pPager->pLast = 0;
+ pPager->nExtra = nExtra;
+ memset(pPager->aHash, 0, sizeof(pPager->aHash));
+ *ppPager = pPager;
+ return SQLITE_OK;
+}
+
+/*
+** Set the destructor for this pager. If not NULL, the destructor is called
+** when the reference count on each page reaches zero. The destructor can
+** be used to clean up information in the extra segment appended to each page.
+**
+** The destructor is not called as a result sqlitepager_close().
+** Destructors are only called by sqlitepager_unref().
+*/
+void sqlitepager_set_destructor(Pager *pPager, void (*xDesc)(void*)){
+ pPager->xDestructor = xDesc;
+}
+
+/*
+** Return the total number of pages in the disk file associated with
+** pPager.
+*/
+int sqlitepager_pagecount(Pager *pPager){
+ off_t n;
+ assert( pPager!=0 );
+ if( pPager->dbSize>=0 ){
+ return pPager->dbSize;
+ }
+ if( sqliteOsFileSize(&pPager->fd, &n)!=SQLITE_OK ){
+ pPager->errMask |= PAGER_ERR_DISK;
+ return 0;
+ }
+ n /= SQLITE_PAGE_SIZE;
+ if( pPager->state!=SQLITE_UNLOCK ){
+ pPager->dbSize = n;
+ }
+ return n;
+}
+
+/*
+** Shutdown the page cache. Free all memory and close all files.
+**
+** If a transaction was in progress when this routine is called, that
+** transaction is rolled back. All outstanding pages are invalidated
+** and their memory is freed. Any attempt to use a page associated
+** with this page cache after this function returns will likely
+** result in a coredump.
+*/
+int sqlitepager_close(Pager *pPager){
+ PgHdr *pPg, *pNext;
+ switch( pPager->state ){
+ case SQLITE_WRITELOCK: {
+ sqlitepager_rollback(pPager);
+ sqliteOsUnlock(&pPager->fd);
+ assert( pPager->journalOpen==0 );
+ break;
+ }
+ case SQLITE_READLOCK: {
+ sqliteOsUnlock(&pPager->fd);
+ break;
+ }
+ default: {
+ /* Do nothing */
+ break;
+ }
+ }
+ for(pPg=pPager->pAll; pPg; pPg=pNext){
+ pNext = pPg->pNextAll;
+ sqliteFree(pPg);
+ }
+ sqliteOsClose(&pPager->fd);
+ assert( pPager->journalOpen==0 );
+ /* Temp files are automatically deleted by the OS
+ ** if( pPager->tempFile ){
+ ** sqliteOsDelete(pPager->zFilename);
+ ** }
+ */
+ CLR_PAGER(pPager);
+ sqliteFree(pPager);
+ return SQLITE_OK;
+}
+
+/*
+** Return the page number for the given page data.
+*/
+Pgno sqlitepager_pagenumber(void *pData){
+ PgHdr *p = DATA_TO_PGHDR(pData);
+ return p->pgno;
+}
+
+/*
+** Increment the reference count for a page. If the page is
+** currently on the freelist (the reference count is zero) then
+** remove it from the freelist.
+*/
+#define page_ref(P) ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++)
+static void _page_ref(PgHdr *pPg){
+ if( pPg->nRef==0 ){
+ /* The page is currently on the freelist. Remove it. */
+ if( pPg==pPg->pPager->pFirstSynced ){
+ PgHdr *p = pPg->pNextFree;
+ while( p && p->needSync ){ p = p->pNextFree; }
+ pPg->pPager->pFirstSynced = p;
+ }
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg->pNextFree;
+ }else{
+ pPg->pPager->pFirst = pPg->pNextFree;
+ }
+ if( pPg->pNextFree ){
+ pPg->pNextFree->pPrevFree = pPg->pPrevFree;
+ }else{
+ pPg->pPager->pLast = pPg->pPrevFree;
+ }
+ pPg->pPager->nRef++;
+ }
+ pPg->nRef++;
+ REFINFO(pPg);
+}
+
+/*
+** Increment the reference count for a page. The input pointer is
+** a reference to the page data.
+*/
+int sqlitepager_ref(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ page_ref(pPg);
+ return SQLITE_OK;
+}
+
+/*
+** Sync the journal and then write all free dirty pages to the database
+** file.
+**
+** Writing all free dirty pages to the database after the sync is a
+** non-obvious optimization. fsync() is an expensive operation so we
+** want to minimize the number ot times it is called. After an fsync() call,
+** we are free to write dirty pages back to the database. It is best
+** to go ahead and write as many dirty pages as possible to minimize
+** the risk of having to do another fsync() later on. Writing dirty
+** free pages in this way was observed to make database operations go
+** up to 10 times faster.
+**
+** If we are writing to temporary database, there is no need to preserve
+** the integrity of the journal file, so we can save time and skip the
+** fsync().
+*/
+static int syncAllPages(Pager *pPager){
+ PgHdr *pPg;
+ int rc = SQLITE_OK;
+
+ /* Sync the journal before modifying the main database
+ ** (assuming there is a journal and it needs to be synced.)
+ */
+ if( pPager->needSync ){
+ if( !pPager->tempFile ){
+ assert( pPager->journalOpen );
+ assert( !pPager->noSync );
+#ifndef NDEBUG
+ {
+ off_t hdrSz, pgSz, jSz;
+ hdrSz = JOURNAL_HDR_SZ(journal_format);
+ pgSz = JOURNAL_PG_SZ(journal_format);
+ rc = sqliteOsFileSize(&pPager->jfd, &jSz);
+ if( rc!=0 ) return rc;
+ assert( pPager->nRec*pgSz+hdrSz==jSz );
+ }
+#endif
+ if( journal_format>=3 ){
+ off_t szJ;
+ if( pPager->fullSync ){
+ TRACE1("SYNC\n");
+ rc = sqliteOsSync(&pPager->jfd);
+ if( rc!=0 ) return rc;
+ }
+ sqliteOsSeek(&pPager->jfd, sizeof(aJournalMagic1));
+ rc = write32bits(&pPager->jfd, pPager->nRec);
+ if( rc ) return rc;
+ szJ = JOURNAL_HDR_SZ(journal_format) +
+ pPager->nRec*JOURNAL_PG_SZ(journal_format);
+ sqliteOsSeek(&pPager->jfd, szJ);
+ }
+ TRACE1("SYNC\n");
+ rc = sqliteOsSync(&pPager->jfd);
+ if( rc!=0 ) return rc;
+ pPager->journalStarted = 1;
+ }
+ pPager->needSync = 0;
+
+ /* Erase the needSync flag from every page.
+ */
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ pPg->needSync = 0;
+ }
+ pPager->pFirstSynced = pPager->pFirst;
+ }
+
+#ifndef NDEBUG
+ /* If the Pager.needSync flag is clear then the PgHdr.needSync
+ ** flag must also be clear for all pages. Verify that this
+ ** invariant is true.
+ */
+ else{
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ assert( pPg->needSync==0 );
+ }
+ assert( pPager->pFirstSynced==pPager->pFirst );
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** Given a list of pages (connected by the PgHdr.pDirty pointer) write
+** every one of those pages out to the database file and mark them all
+** as clean.
+*/
+static int pager_write_pagelist(PgHdr *pList){
+ Pager *pPager;
+ int rc;
+
+ if( pList==0 ) return SQLITE_OK;
+ pPager = pList->pPager;
+ while( pList ){
+ assert( pList->dirty );
+ sqliteOsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
+ rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);
+ if( rc ) return rc;
+ pList->dirty = 0;
+ pList = pList->pDirty;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Collect every dirty page into a dirty list and
+** return a pointer to the head of that list. All pages are
+** collected even if they are still in use.
+*/
+static PgHdr *pager_get_all_dirty_pages(Pager *pPager){
+ PgHdr *p, *pList;
+ pList = 0;
+ for(p=pPager->pAll; p; p=p->pNextAll){
+ if( p->dirty ){
+ p->pDirty = pList;
+ pList = p;
+ }
+ }
+ return pList;
+}
+
+/*
+** Acquire a page.
+**
+** A read lock on the disk file is obtained when the first page is acquired.
+** This read lock is dropped when the last page is released.
+**
+** A _get works for any page number greater than 0. If the database
+** file is smaller than the requested page, then no actual disk
+** read occurs and the memory image of the page is initialized to
+** all zeros. The extra data appended to a page is always initialized
+** to zeros the first time a page is loaded into memory.
+**
+** The acquisition might fail for several reasons. In all cases,
+** an appropriate error code is returned and *ppPage is set to NULL.
+**
+** See also sqlitepager_lookup(). Both this routine and _lookup() attempt
+** to find a page in the in-memory cache first. If the page is not already
+** in memory, this routine goes to disk to read it in whereas _lookup()
+** just returns 0. This routine acquires a read-lock the first time it
+** has to go to disk, and could also playback an old journal if necessary.
+** Since _lookup() never goes to disk, it never has to deal with locks
+** or journal files.
+*/
+int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
+ PgHdr *pPg;
+ int rc;
+
+ /* Make sure we have not hit any critical errors.
+ */
+ assert( pPager!=0 );
+ assert( pgno!=0 );
+ if( pPager->errMask & ~(PAGER_ERR_FULL) ){
+ return pager_errcode(pPager);
+ }
+
+ /* If this is the first page accessed, then get a read lock
+ ** on the database file.
+ */
+ if( pPager->nRef==0 ){
+ rc = sqliteOsReadLock(&pPager->fd);
+ if( rc!=SQLITE_OK ){
+ *ppPage = 0;
+ return rc;
+ }
+ pPager->state = SQLITE_READLOCK;
+
+ /* If a journal file exists, try to play it back.
+ */
+ if( pPager->useJournal && sqliteOsFileExists(pPager->zJournal) ){
+ int rc, dummy;
+
+ /* Get a write lock on the database
+ */
+ rc = sqliteOsWriteLock(&pPager->fd);
+ if( rc!=SQLITE_OK ){
+ if( sqliteOsUnlock(&pPager->fd)!=SQLITE_OK ){
+ /* This should never happen! */
+ rc = SQLITE_INTERNAL;
+ }
+ *ppPage = 0;
+ return rc;
+ }
+ pPager->state = SQLITE_WRITELOCK;
+
+ /* Open the journal for exclusive access. Return SQLITE_BUSY if
+ ** we cannot get exclusive access to the journal file.
+ **
+ ** Even though we will only be reading from the journal, not writing,
+ ** we have to open the journal for writing in order to obtain an
+ ** exclusive access lock.
+ */
+ rc = sqliteOsOpenReadWrite(pPager->zJournal, &pPager->jfd, &dummy);
+ if( rc!=SQLITE_OK ){
+ rc = sqliteOsUnlock(&pPager->fd);
+ assert( rc==SQLITE_OK );
+ *ppPage = 0;
+ return SQLITE_BUSY;
+ }
+ pPager->journalOpen = 1;
+ pPager->journalStarted = 0;
+
+ /* Playback and delete the journal. Drop the database write
+ ** lock and reacquire the read lock.
+ */
+ rc = pager_playback(pPager, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+ pPg = 0;
+ }else{
+ /* Search for page in cache */
+ pPg = pager_lookup(pPager, pgno);
+ }
+ if( pPg==0 ){
+ /* The requested page is not in the page cache. */
+ int h;
+ pPager->nMiss++;
+ if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 ){
+ /* Create a new page */
+ pPg = sqliteMallocRaw( sizeof(*pPg) + SQLITE_PAGE_SIZE
+ + sizeof(u32) + pPager->nExtra );
+ if( pPg==0 ){
+ *ppPage = 0;
+ pager_unwritelock(pPager);
+ pPager->errMask |= PAGER_ERR_MEM;
+ return SQLITE_NOMEM;
+ }
+ memset(pPg, 0, sizeof(*pPg));
+ pPg->pPager = pPager;
+ pPg->pNextAll = pPager->pAll;
+ if( pPager->pAll ){
+ pPager->pAll->pPrevAll = pPg;
+ }
+ pPg->pPrevAll = 0;
+ pPager->pAll = pPg;
+ pPager->nPage++;
+ }else{
+ /* Find a page to recycle. Try to locate a page that does not
+ ** require us to do an fsync() on the journal.
+ */
+ pPg = pPager->pFirstSynced;
+
+ /* If we could not find a page that does not require an fsync()
+ ** on the journal file then fsync the journal file. This is a
+ ** very slow operation, so we work hard to avoid it. But sometimes
+ ** it can't be helped.
+ */
+ if( pPg==0 ){
+ int rc = syncAllPages(pPager);
+ if( rc!=0 ){
+ sqlitepager_rollback(pPager);
+ *ppPage = 0;
+ return SQLITE_IOERR;
+ }
+ pPg = pPager->pFirst;
+ }
+ assert( pPg->nRef==0 );
+
+ /* Write the page to the database file if it is dirty.
+ */
+ if( pPg->dirty ){
+ assert( pPg->needSync==0 );
+ pPg->pDirty = 0;
+ rc = pager_write_pagelist( pPg );
+ if( rc!=SQLITE_OK ){
+ sqlitepager_rollback(pPager);
+ *ppPage = 0;
+ return SQLITE_IOERR;
+ }
+ }
+ assert( pPg->dirty==0 );
+
+ /* If the page we are recycling is marked as alwaysRollback, then
+ ** set the global alwaysRollback flag, thus disabling the
+ ** sqlite_dont_rollback() optimization for the rest of this transaction.
+ ** It is necessary to do this because the page marked alwaysRollback
+ ** might be reloaded at a later time but at that point we won't remember
+ ** that is was marked alwaysRollback. This means that all pages must
+ ** be marked as alwaysRollback from here on out.
+ */
+ if( pPg->alwaysRollback ){
+ pPager->alwaysRollback = 1;
+ }
+
+ /* Unlink the old page from the free list and the hash table
+ */
+ if( pPg==pPager->pFirstSynced ){
+ PgHdr *p = pPg->pNextFree;
+ while( p && p->needSync ){ p = p->pNextFree; }
+ pPager->pFirstSynced = p;
+ }
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg->pNextFree;
+ }else{
+ assert( pPager->pFirst==pPg );
+ pPager->pFirst = pPg->pNextFree;
+ }
+ if( pPg->pNextFree ){
+ pPg->pNextFree->pPrevFree = pPg->pPrevFree;
+ }else{
+ assert( pPager->pLast==pPg );
+ pPager->pLast = pPg->pPrevFree;
+ }
+ pPg->pNextFree = pPg->pPrevFree = 0;
+ if( pPg->pNextHash ){
+ pPg->pNextHash->pPrevHash = pPg->pPrevHash;
+ }
+ if( pPg->pPrevHash ){
+ pPg->pPrevHash->pNextHash = pPg->pNextHash;
+ }else{
+ h = pager_hash(pPg->pgno);
+ assert( pPager->aHash[h]==pPg );
+ pPager->aHash[h] = pPg->pNextHash;
+ }
+ pPg->pNextHash = pPg->pPrevHash = 0;
+ pPager->nOvfl++;
+ }
+ pPg->pgno = pgno;
+ if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
+ sqliteCheckMemory(pPager->aInJournal, pgno/8);
+ assert( pPager->journalOpen );
+ pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
+ pPg->needSync = 0;
+ }else{
+ pPg->inJournal = 0;
+ pPg->needSync = 0;
+ }
+ if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize
+ && (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0 ){
+ page_add_to_ckpt_list(pPg);
+ }else{
+ page_remove_from_ckpt_list(pPg);
+ }
+ pPg->dirty = 0;
+ pPg->nRef = 1;
+ REFINFO(pPg);
+ pPager->nRef++;
+ h = pager_hash(pgno);
+ pPg->pNextHash = pPager->aHash[h];
+ pPager->aHash[h] = pPg;
+ if( pPg->pNextHash ){
+ assert( pPg->pNextHash->pPrevHash==0 );
+ pPg->pNextHash->pPrevHash = pPg;
+ }
+ if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);
+ if( pPager->dbSize<(int)pgno ){
+ memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
+ }else{
+ int rc;
+ sqliteOsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);
+ rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
+ if( rc!=SQLITE_OK ){
+ off_t fileSize;
+ if( sqliteOsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK
+ || fileSize>=pgno*SQLITE_PAGE_SIZE ){
+ return rc;
+ }else{
+ memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
+ }
+ }
+ }
+ if( pPager->nExtra>0 ){
+ memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
+ }
+ }else{
+ /* The requested page is in the page cache. */
+ pPager->nHit++;
+ page_ref(pPg);
+ }
+ *ppPage = PGHDR_TO_DATA(pPg);
+ return SQLITE_OK;
+}
+
+/*
+** Acquire a page if it is already in the in-memory cache. Do
+** not read the page from disk. Return a pointer to the page,
+** or 0 if the page is not in cache.
+**
+** See also sqlitepager_get(). The difference between this routine
+** and sqlitepager_get() is that _get() will go to the disk and read
+** in the page if the page is not already in cache. This routine
+** returns NULL if the page is not in cache or if a disk I/O error
+** has ever happened.
+*/
+void *sqlitepager_lookup(Pager *pPager, Pgno pgno){
+ PgHdr *pPg;
+
+ assert( pPager!=0 );
+ assert( pgno!=0 );
+ if( pPager->errMask & ~(PAGER_ERR_FULL) ){
+ return 0;
+ }
+ /* if( pPager->nRef==0 ){
+ ** return 0;
+ ** }
+ */
+ pPg = pager_lookup(pPager, pgno);
+ if( pPg==0 ) return 0;
+ page_ref(pPg);
+ return PGHDR_TO_DATA(pPg);
+}
+
+/*
+** Release a page.
+**
+** If the number of references to the page drop to zero, then the
+** page is added to the LRU list. When all references to all pages
+** are released, a rollback occurs and the lock on the database is
+** removed.
+*/
+int sqlitepager_unref(void *pData){
+ PgHdr *pPg;
+
+ /* Decrement the reference count for this page
+ */
+ pPg = DATA_TO_PGHDR(pData);
+ assert( pPg->nRef>0 );
+ pPg->nRef--;
+ REFINFO(pPg);
+
+ /* When the number of references to a page reach 0, call the
+ ** destructor and add the page to the freelist.
+ */
+ if( pPg->nRef==0 ){
+ Pager *pPager;
+ pPager = pPg->pPager;
+ pPg->pNextFree = 0;
+ pPg->pPrevFree = pPager->pLast;
+ pPager->pLast = pPg;
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg;
+ }else{
+ pPager->pFirst = pPg;
+ }
+ if( pPg->needSync==0 && pPager->pFirstSynced==0 ){
+ pPager->pFirstSynced = pPg;
+ }
+ if( pPager->xDestructor ){
+ pPager->xDestructor(pData);
+ }
+
+ /* When all pages reach the freelist, drop the read lock from
+ ** the database file.
+ */
+ pPager->nRef--;
+ assert( pPager->nRef>=0 );
+ if( pPager->nRef==0 ){
+ pager_reset(pPager);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Create a journal file for pPager. There should already be a write
+** lock on the database file when this routine is called.
+**
+** Return SQLITE_OK if everything. Return an error code and release the
+** write lock if anything goes wrong.
+*/
+static int pager_open_journal(Pager *pPager){
+ int rc;
+ assert( pPager->state==SQLITE_WRITELOCK );
+ assert( pPager->journalOpen==0 );
+ assert( pPager->useJournal );
+ pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
+ if( pPager->aInJournal==0 ){
+ sqliteOsReadLock(&pPager->fd);
+ pPager->state = SQLITE_READLOCK;
+ return SQLITE_NOMEM;
+ }
+ rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
+ if( rc!=SQLITE_OK ){
+ sqliteFree(pPager->aInJournal);
+ pPager->aInJournal = 0;
+ sqliteOsReadLock(&pPager->fd);
+ pPager->state = SQLITE_READLOCK;
+ return SQLITE_CANTOPEN;
+ }
+ pPager->journalOpen = 1;
+ pPager->journalStarted = 0;
+ pPager->needSync = 0;
+ pPager->alwaysRollback = 0;
+ pPager->nRec = 0;
+ sqlitepager_pagecount(pPager);
+ pPager->origDbSize = pPager->dbSize;
+ if( journal_format==JOURNAL_FORMAT_3 ){
+ rc = sqliteOsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
+ if( rc==SQLITE_OK ){
+ rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
+ }
+ if( rc==SQLITE_OK ){
+ pPager->cksumInit = (u32)sqliteRandomInteger();
+ rc = write32bits(&pPager->jfd, pPager->cksumInit);
+ }
+ }else if( journal_format==JOURNAL_FORMAT_2 ){
+ rc = sqliteOsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2));
+ }else{
+ assert( journal_format==JOURNAL_FORMAT_1 );
+ rc = sqliteOsWrite(&pPager->jfd, aJournalMagic1, sizeof(aJournalMagic1));
+ }
+ if( rc==SQLITE_OK ){
+ rc = write32bits(&pPager->jfd, pPager->dbSize);
+ }
+ if( pPager->ckptAutoopen && rc==SQLITE_OK ){
+ rc = sqlitepager_ckpt_begin(pPager);
+ }
+ if( rc!=SQLITE_OK ){
+ rc = pager_unwritelock(pPager);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_FULL;
+ }
+ }
+ return rc;
+}
+
+/*
+** Acquire a write-lock on the database. The lock is removed when
+** the any of the following happen:
+**
+** * sqlitepager_commit() is called.
+** * sqlitepager_rollback() is called.
+** * sqlitepager_close() is called.
+** * sqlitepager_unref() is called to on every outstanding page.
+**
+** The parameter to this routine is a pointer to any open page of the
+** database file. Nothing changes about the page - it is used merely
+** to acquire a pointer to the Pager structure and as proof that there
+** is already a read-lock on the database.
+**
+** A journal file is opened if this is not a temporary file. For
+** temporary files, the opening of the journal file is deferred until
+** there is an actual need to write to the journal.
+**
+** If the database is already write-locked, this routine is a no-op.
+*/
+int sqlitepager_begin(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+ int rc = SQLITE_OK;
+ assert( pPg->nRef>0 );
+ assert( pPager->state!=SQLITE_UNLOCK );
+ if( pPager->state==SQLITE_READLOCK ){
+ assert( pPager->aInJournal==0 );
+ rc = sqliteOsWriteLock(&pPager->fd);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ pPager->state = SQLITE_WRITELOCK;
+ pPager->dirtyFile = 0;
+ TRACE1("TRANSACTION\n");
+ if( pPager->useJournal && !pPager->tempFile ){
+ rc = pager_open_journal(pPager);
+ }
+ }
+ return rc;
+}
+
+/*
+** Mark a data page as writeable. The page is written into the journal
+** if it is not there already. This routine must be called before making
+** changes to a page.
+**
+** The first time this routine is called, the pager creates a new
+** journal and acquires a write lock on the database. If the write
+** lock could not be acquired, this routine returns SQLITE_BUSY. The
+** calling routine must check for that return value and be careful not to
+** change any page data until this routine returns SQLITE_OK.
+**
+** If the journal file could not be written because the disk is full,
+** then this routine returns SQLITE_FULL and does an immediate rollback.
+** All subsequent write attempts also return SQLITE_FULL until there
+** is a call to sqlitepager_commit() or sqlitepager_rollback() to
+** reset.
+*/
+int sqlitepager_write(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+ int rc = SQLITE_OK;
+
+ /* Check for errors
+ */
+ if( pPager->errMask ){
+ return pager_errcode(pPager);
+ }
+ if( pPager->readOnly ){
+ return SQLITE_PERM;
+ }
+
+ /* Mark the page as dirty. If the page has already been written
+ ** to the journal then we can return right away.
+ */
+ pPg->dirty = 1;
+ if( pPg->inJournal && (pPg->inCkpt || pPager->ckptInUse==0) ){
+ pPager->dirtyFile = 1;
+ return SQLITE_OK;
+ }
+
+ /* If we get this far, it means that the page needs to be
+ ** written to the transaction journal or the ckeckpoint journal
+ ** or both.
+ **
+ ** First check to see that the transaction journal exists and
+ ** create it if it does not.
+ */
+ assert( pPager->state!=SQLITE_UNLOCK );
+ rc = sqlitepager_begin(pData);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ assert( pPager->state==SQLITE_WRITELOCK );
+ if( !pPager->journalOpen && pPager->useJournal ){
+ rc = pager_open_journal(pPager);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ assert( pPager->journalOpen || !pPager->useJournal );
+ pPager->dirtyFile = 1;
+
+ /* The transaction journal now exists and we have a write lock on the
+ ** main database file. Write the current page to the transaction
+ ** journal if it is not there already.
+ */
+ if( !pPg->inJournal && pPager->useJournal ){
+ if( (int)pPg->pgno <= pPager->origDbSize ){
+ int szPg;
+ u32 saved;
+ if( journal_format>=JOURNAL_FORMAT_3 ){
+ u32 cksum = pager_cksum(pPager, pPg->pgno, pData);
+ saved = *(u32*)PGHDR_TO_EXTRA(pPg);
+ store32bits(cksum, pPg, SQLITE_PAGE_SIZE);
+ szPg = SQLITE_PAGE_SIZE+8;
+ }else{
+ szPg = SQLITE_PAGE_SIZE+4;
+ }
+ store32bits(pPg->pgno, pPg, -4);
+ rc = sqliteOsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
+ if( journal_format>=JOURNAL_FORMAT_3 ){
+ *(u32*)PGHDR_TO_EXTRA(pPg) = saved;
+ }
+ if( rc!=SQLITE_OK ){
+ sqlitepager_rollback(pPager);
+ pPager->errMask |= PAGER_ERR_FULL;
+ return rc;
+ }
+ pPager->nRec++;
+ assert( pPager->aInJournal!=0 );
+ pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ pPg->needSync = !pPager->noSync;
+ pPg->inJournal = 1;
+ if( pPager->ckptInUse ){
+ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_ckpt_list(pPg);
+ }
+ TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
+ }else{
+ pPg->needSync = !pPager->journalStarted && !pPager->noSync;
+ TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);
+ }
+ if( pPg->needSync ){
+ pPager->needSync = 1;
+ }
+ }
+
+ /* If the checkpoint journal is open and the page is not in it,
+ ** then write the current page to the checkpoint journal. Note that
+ ** the checkpoint journal always uses the simplier format 2 that lacks
+ ** checksums. The header is also omitted from the checkpoint journal.
+ */
+ if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
+ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
+ store32bits(pPg->pgno, pPg, -4);
+ rc = sqliteOsWrite(&pPager->cpfd, &((char*)pData)[-4], SQLITE_PAGE_SIZE+4);
+ if( rc!=SQLITE_OK ){
+ sqlitepager_rollback(pPager);
+ pPager->errMask |= PAGER_ERR_FULL;
+ return rc;
+ }
+ pPager->ckptNRec++;
+ assert( pPager->aInCkpt!=0 );
+ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_ckpt_list(pPg);
+ }
+
+ /* Update the database size and return.
+ */
+ if( pPager->dbSize<(int)pPg->pgno ){
+ pPager->dbSize = pPg->pgno;
+ }
+ return rc;
+}
+
+/*
+** Return TRUE if the page given in the argument was previously passed
+** to sqlitepager_write(). In other words, return TRUE if it is ok
+** to change the content of the page.
+*/
+int sqlitepager_iswriteable(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ return pPg->dirty;
+}
+
+/*
+** A call to this routine tells the pager that it is not necessary to
+** write the information on page "pgno" back to the disk, even though
+** that page might be marked as dirty.
+**
+** The overlying software layer calls this routine when all of the data
+** on the given page is unused. The pager marks the page as clean so
+** that it does not get written to disk.
+**
+** Tests show that this optimization, together with the
+** sqlitepager_dont_rollback() below, more than double the speed
+** of large INSERT operations and quadruple the speed of large DELETEs.
+**
+** When this routine is called, set the alwaysRollback flag to true.
+** Subsequent calls to sqlitepager_dont_rollback() for the same page
+** will thereafter be ignored. This is necessary to avoid a problem
+** where a page with data is added to the freelist during one part of
+** a transaction then removed from the freelist during a later part
+** of the same transaction and reused for some other purpose. When it
+** is first added to the freelist, this routine is called. When reused,
+** the dont_rollback() routine is called. But because the page contains
+** critical data, we still need to be sure it gets rolled back in spite
+** of the dont_rollback() call.
+*/
+void sqlitepager_dont_write(Pager *pPager, Pgno pgno){
+ PgHdr *pPg;
+
+ pPg = pager_lookup(pPager, pgno);
+ pPg->alwaysRollback = 1;
+ if( pPg && pPg->dirty ){
+ if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
+ /* If this pages is the last page in the file and the file has grown
+ ** during the current transaction, then do NOT mark the page as clean.
+ ** When the database file grows, we must make sure that the last page
+ ** gets written at least once so that the disk file will be the correct
+ ** size. If you do not write this page and the size of the file
+ ** on the disk ends up being too small, that can lead to database
+ ** corruption during the next transaction.
+ */
+ }else{
+ TRACE2("DONT_WRITE %d\n", pgno);
+ pPg->dirty = 0;
+ }
+ }
+}
+
+/*
+** A call to this routine tells the pager that if a rollback occurs,
+** it is not necessary to restore the data on the given page. This
+** means that the pager does not have to record the given page in the
+** rollback journal.
+*/
+void sqlitepager_dont_rollback(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+
+ if( pPager->state!=SQLITE_WRITELOCK || pPager->journalOpen==0 ) return;
+ if( pPg->alwaysRollback || pPager->alwaysRollback ) return;
+ if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
+ assert( pPager->aInJournal!=0 );
+ pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ pPg->inJournal = 1;
+ if( pPager->ckptInUse ){
+ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_ckpt_list(pPg);
+ }
+ TRACE2("DONT_ROLLBACK %d\n", pPg->pgno);
+ }
+ if( pPager->ckptInUse && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
+ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
+ assert( pPager->aInCkpt!=0 );
+ pPager->aInCkpt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_ckpt_list(pPg);
+ }
+}
+
+/*
+** Commit all changes to the database and release the write lock.
+**
+** If the commit fails for any reason, a rollback attempt is made
+** and an error code is returned. If the commit worked, SQLITE_OK
+** is returned.
+*/
+int sqlitepager_commit(Pager *pPager){
+ int rc;
+ PgHdr *pPg;
+
+ if( pPager->errMask==PAGER_ERR_FULL ){
+ rc = sqlitepager_rollback(pPager);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_FULL;
+ }
+ return rc;
+ }
+ if( pPager->errMask!=0 ){
+ rc = pager_errcode(pPager);
+ return rc;
+ }
+ if( pPager->state!=SQLITE_WRITELOCK ){
+ return SQLITE_ERROR;
+ }
+ TRACE1("COMMIT\n");
+ if( pPager->dirtyFile==0 ){
+ /* Exit early (without doing the time-consuming sqliteOsSync() calls)
+ ** if there have been no changes to the database file. */
+ assert( pPager->needSync==0 );
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+ }
+ assert( pPager->journalOpen );
+ if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
+ goto commit_abort;
+ }
+ pPg = pager_get_all_dirty_pages(pPager);
+ if( pPg ){
+ rc = pager_write_pagelist(pPg);
+ if( rc || (!pPager->noSync && sqliteOsSync(&pPager->fd)!=SQLITE_OK) ){
+ goto commit_abort;
+ }
+ }
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+
+ /* Jump here if anything goes wrong during the commit process.
+ */
+commit_abort:
+ rc = sqlitepager_rollback(pPager);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_FULL;
+ }
+ return rc;
+}
+
+/*
+** Rollback all changes. The database falls back to read-only mode.
+** All in-memory cache pages revert to their original data contents.
+** The journal is deleted.
+**
+** This routine cannot fail unless some other process is not following
+** the correct locking protocol (SQLITE_PROTOCOL) or unless some other
+** process is writing trash into the journal file (SQLITE_CORRUPT) or
+** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error
+** codes are returned for all these occasions. Otherwise,
+** SQLITE_OK is returned.
+*/
+int sqlitepager_rollback(Pager *pPager){
+ int rc;
+ TRACE1("ROLLBACK\n");
+ if( !pPager->dirtyFile || !pPager->journalOpen ){
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+ }
+
+ if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
+ if( pPager->state>=SQLITE_WRITELOCK ){
+ pager_playback(pPager, 1);
+ }
+ return pager_errcode(pPager);
+ }
+ if( pPager->state!=SQLITE_WRITELOCK ){
+ return SQLITE_OK;
+ }
+ rc = pager_playback(pPager, 1);
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_CORRUPT;
+ pPager->errMask |= PAGER_ERR_CORRUPT;
+ }
+ pPager->dbSize = -1;
+ return rc;
+}
+
+/*
+** Return TRUE if the database file is opened read-only. Return FALSE
+** if the database is (in theory) writable.
+*/
+int sqlitepager_isreadonly(Pager *pPager){
+ return pPager->readOnly;
+}
+
+/*
+** This routine is used for testing and analysis only.
+*/
+int *sqlitepager_stats(Pager *pPager){
+ static int a[9];
+ a[0] = pPager->nRef;
+ a[1] = pPager->nPage;
+ a[2] = pPager->mxPage;
+ a[3] = pPager->dbSize;
+ a[4] = pPager->state;
+ a[5] = pPager->errMask;
+ a[6] = pPager->nHit;
+ a[7] = pPager->nMiss;
+ a[8] = pPager->nOvfl;
+ return a;
+}
+
+/*
+** Set the checkpoint.
+**
+** This routine should be called with the transaction journal already
+** open. A new checkpoint journal is created that can be used to rollback
+** changes of a single SQL command within a larger transaction.
+*/
+int sqlitepager_ckpt_begin(Pager *pPager){
+ int rc;
+ char zTemp[SQLITE_TEMPNAME_SIZE];
+ if( !pPager->journalOpen ){
+ pPager->ckptAutoopen = 1;
+ return SQLITE_OK;
+ }
+ assert( pPager->journalOpen );
+ assert( !pPager->ckptInUse );
+ pPager->aInCkpt = sqliteMalloc( pPager->dbSize/8 + 1 );
+ if( pPager->aInCkpt==0 ){
+ sqliteOsReadLock(&pPager->fd);
+ return SQLITE_NOMEM;
+ }
+#ifndef NDEBUG
+ rc = sqliteOsFileSize(&pPager->jfd, &pPager->ckptJSize);
+ if( rc ) goto ckpt_begin_failed;
+ assert( pPager->ckptJSize ==
+ pPager->nRec*JOURNAL_PG_SZ(journal_format)+JOURNAL_HDR_SZ(journal_format) );
+#endif
+ pPager->ckptJSize = pPager->nRec*JOURNAL_PG_SZ(journal_format)
+ + JOURNAL_HDR_SZ(journal_format);
+ pPager->ckptSize = pPager->dbSize;
+ if( !pPager->ckptOpen ){
+ rc = sqlitepager_opentemp(zTemp, &pPager->cpfd);
+ if( rc ) goto ckpt_begin_failed;
+ pPager->ckptOpen = 1;
+ pPager->ckptNRec = 0;
+ }
+ pPager->ckptInUse = 1;
+ return SQLITE_OK;
+
+ckpt_begin_failed:
+ if( pPager->aInCkpt ){
+ sqliteFree(pPager->aInCkpt);
+ pPager->aInCkpt = 0;
+ }
+ return rc;
+}
+
+/*
+** Commit a checkpoint.
+*/
+int sqlitepager_ckpt_commit(Pager *pPager){
+ if( pPager->ckptInUse ){
+ PgHdr *pPg, *pNext;
+ sqliteOsSeek(&pPager->cpfd, 0);
+ /* sqliteOsTruncate(&pPager->cpfd, 0); */
+ pPager->ckptNRec = 0;
+ pPager->ckptInUse = 0;
+ sqliteFree( pPager->aInCkpt );
+ pPager->aInCkpt = 0;
+ for(pPg=pPager->pCkpt; pPg; pPg=pNext){
+ pNext = pPg->pNextCkpt;
+ assert( pPg->inCkpt );
+ pPg->inCkpt = 0;
+ pPg->pPrevCkpt = pPg->pNextCkpt = 0;
+ }
+ pPager->pCkpt = 0;
+ }
+ pPager->ckptAutoopen = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Rollback a checkpoint.
+*/
+int sqlitepager_ckpt_rollback(Pager *pPager){
+ int rc;
+ if( pPager->ckptInUse ){
+ rc = pager_ckpt_playback(pPager);
+ sqlitepager_ckpt_commit(pPager);
+ }else{
+ rc = SQLITE_OK;
+ }
+ pPager->ckptAutoopen = 0;
+ return rc;
+}
+
+#ifdef SQLITE_TEST
+/*
+** Print a listing of all referenced pages and their ref count.
+*/
+void sqlitepager_refdump(Pager *pPager){
+ PgHdr *pPg;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ if( pPg->nRef<=0 ) continue;
+ printf("PAGE %3d addr=0x%08x nRef=%d\n",
+ pPg->pgno, (int)PGHDR_TO_DATA(pPg), pPg->nRef);
+ }
+}
+#endif