diff --git a/manifest b/manifest index 685da9f844..33db0ae37c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\syypMinor\savailable\sto\sthe\sstack\soverflow\scallbacks\sin\slemon\ngenerated\sparsers.\s\sThis\sdoes\snot\seffect\sSQLite.\s(CVS\s3761) -D 2007-03-30T13:35:06 +C Refactoring\sthe\sbtree\sand\spager\sroutines\sinto\sdistinct\stwo-phase\scommit\nroutines.\s\sWe've\salways\sdone\sa\stwo-phase\scommit\s-\sthis\schange\sis\sjust\nmaking\sthat\smore\sapparent\sin\sthe\scode.\s(CVS\s3762) +D 2007-03-30T14:06:34 F Makefile.in 2f2c3bf69faf0ae7b8e8af4f94f1986849034530 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -58,8 +58,8 @@ F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3 F src/analyze.c 4bbf5ddf9680587c6d4917e02e378b6037be3651 F src/attach.c a16ada4a4654a0d126b8223ec9494ebb81bc5c3c F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f -F src/btree.c 9b41229e7ff4b0fe8556763ce2b466a90aaaa6a4 -F src/btree.h 540dcbbf83435b77d4b6ef87f909c6cecad4dac9 +F src/btree.c 931d8d6eb3e669d6e42305dce3f196bf071a53b6 +F src/btree.h 9b2cc0d113c0bc2d37d244b9a394d56948c9acbf F src/build.c ad3374b5409554e504300f77e1fbc6b4c106a57f F src/callback.c 31d22b4919c7645cbcbb1591ce2453e8c677c558 F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675 @@ -86,8 +86,8 @@ F src/os_unix.c 4291be23eec73d1ec04010ae702364b781b5f773 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e F src/os_win.c c3a8403ea28bbb89d6507fa984c5919bd3fe7539 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c b89ea0f592e499ee6d6cda10b84688f8e47a05ba -F src/pager.h f1b17bf848b3dce5d9afb2701186d3c9a8826f8c +F src/pager.c f9131543cc602202de8a436ca9207e4c28b3f41e +F src/pager.h e79a24cf200b8771366217f5bca414f5b7823f42 F src/parse.y 207ab04273ae13aa4a729b96008d294d5f334ab3 F src/pragma.c 8fd4f98822007a8d2c34e235ad3c35f1d77b3e51 F src/prepare.c 37207b2b2ccb41d379b01dd62231686bcc48ef1f @@ -102,7 +102,7 @@ F src/sqliteInt.h c8d0e5ce27a862836de70fc3eadc1e65cea7e3d8 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06 F src/tclsqlite.c a8d1166319db5d505b25ac6a9820162afe63fc8a F src/test1.c 439bba8da10fbc61c731019cf2894e6057578878 -F src/test2.c dc48c84ce68b3bc2f2d01871d709f20dc77003b0 +F src/test2.c 710a7252e22a8c690136a2b609e90fdad2697f35 F src/test3.c 65f92247cf8592854e9bf5115b3fb711f8b33280 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f @@ -127,7 +127,7 @@ F src/vdbe.c d5a4fc1e18af2a2873d022450c4a776f6c17bac3 F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691 F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132 F src/vdbeapi.c 7ac14f2e3b2283dcd35f5edefd31a8342cff783c -F src/vdbeaux.c d768c22c8aa0fbf9bf1408b4fc9ed0b38b1146ca +F src/vdbeaux.c 5efa6d0f6096f213fbdffa0b1bf0a4c8ae40d145 F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f F src/vdbemem.c d3696b4b0e5f32272659816cdfa2348c650b1ba0 F src/vtab.c 7fbda947e28cbe7adb3ba752a76ca9ef29936750 @@ -447,7 +447,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P 339941d83ae397d69084f41483afb1ea44d44967 -R b47b2f2fe5cfddcd0105ea435c37f3d5 +P 70c8c7e2ce5213778e63c200a6637849920deea6 +R a5a112b3409ee86a088223323643c5e6 U drh -Z 511499bbee13f7559f6a7e8fb29eeb3d +Z 49017f06f92594291d8addf7b78eb7c9 diff --git a/manifest.uuid b/manifest.uuid index c096c2b752..66ce391f31 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -70c8c7e2ce5213778e63c200a6637849920deea6 \ No newline at end of file +66b3ad09ea657d25d48cb75ec2671ea2dc1b6005 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index fe103ddec9..5f4af95113 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.346 2007/03/30 11:12:08 drh Exp $ +** $Id: btree.c,v 1.347 2007/03/30 14:06:34 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -2407,6 +2407,50 @@ autovacuum_out: } #endif +/* +** This routine does the first phase of a two-phase commit. This routine +** causes a rollback journal to be created (if it does not already exist) +** and populated with enough information so that if a power loss occurs +** the database can be restored to its original state by playing back +** the journal. Then the contents of the journal are flushed out to +** the disk. After the journal is safely on oxide, the changes to the +** database are written into the database file and flushed to oxide. +** At the end of this call, the rollback journal still exists on the +** disk and we are still holding all locks, so the transaction has not +** committed. See sqlite3BtreeCommit() for the second phase of the +** commit process. +** +** This call is a no-op if no write-transaction is currently active on pBt. +** +** Otherwise, sync the database file for the btree pBt. zMaster points to +** the name of a master journal file that should be written into the +** individual journal file, or is NULL, indicating no master journal file +** (single database transaction). +** +** When this is called, the master journal should already have been +** created, populated with this journal pointer and synced to disk. +** +** Once this is routine has returned, the only thing required to commit +** the write-transaction for this database file is to delete the journal. +*/ +int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ + int rc = SQLITE_OK; + if( p->inTrans==TRANS_WRITE ){ + BtShared *pBt = p->pBt; + Pgno nTrunc = 0; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + rc = autoVacuumCommit(pBt, &nTrunc); + if( rc!=SQLITE_OK ){ + return rc; + } + } +#endif + rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc); + } + return rc; +} + /* ** Commit the transaction currently in progress. ** @@ -2421,7 +2465,7 @@ autovacuum_out: ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ -int sqlite3BtreeCommit(Btree *p){ +int sqlite3BtreeCommitPhaseTwo(Btree *p){ BtShared *pBt = p->pBt; btreeIntegrity(p); @@ -2433,7 +2477,7 @@ int sqlite3BtreeCommit(Btree *p){ int rc; assert( pBt->inTransaction==TRANS_WRITE ); assert( pBt->nTransaction>0 ); - rc = sqlite3PagerCommit(pBt->pPager); + rc = sqlite3PagerCommitPhaseTwo(pBt->pPager); if( rc!=SQLITE_OK ){ return rc; } @@ -2464,6 +2508,18 @@ int sqlite3BtreeCommit(Btree *p){ return SQLITE_OK; } +/* +** Do both phases of a commit. +*/ +int sqlite3BtreeCommit(Btree *p){ + int rc; + rc = sqlite3BtreeCommitPhaseOne(p, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeCommitPhaseTwo(p); + } + return rc; +} + #ifndef NDEBUG /* ** Return the number of write-cursors open on this handle. This is for use @@ -6522,50 +6578,6 @@ int sqlite3BtreeIsInReadTrans(Btree *p){ return (p && (p->inTrans!=TRANS_NONE)); } -/* -** This routine does the first phase of a 2-phase commit. This routine -** causes a rollback journal to be created (if it does not already exist) -** and populated with enough information so that if a power loss occurs -** the database can be restored to its original state by playing back -** the journal. Then the contents of the journal are flushed out to -** the disk. After the journal is safely on oxide, the changes to the -** database are written into the database file and flushed to oxide. -** At the end of this call, the rollback journal still exists on the -** disk and we are still holding all locks, so the transaction has not -** committed. See sqlite3BtreeCommit() for the second phase of the -** commit process. -** -** This call is a no-op if no write-transaction is currently active on pBt. -** -** Otherwise, sync the database file for the btree pBt. zMaster points to -** the name of a master journal file that should be written into the -** individual journal file, or is NULL, indicating no master journal file -** (single database transaction). -** -** When this is called, the master journal should already have been -** created, populated with this journal pointer and synced to disk. -** -** Once this is routine has returned, the only thing required to commit -** the write-transaction for this database file is to delete the journal. -*/ -int sqlite3BtreeSync(Btree *p, const char *zMaster){ - int rc = SQLITE_OK; - if( p->inTrans==TRANS_WRITE ){ - BtShared *pBt = p->pBt; - Pgno nTrunc = 0; -#ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum ){ - rc = autoVacuumCommit(pBt, &nTrunc); - if( rc!=SQLITE_OK ){ - return rc; - } - } -#endif - rc = sqlite3PagerSync(pBt->pPager, zMaster, nTrunc); - } - return rc; -} - /* ** This function returns a pointer to a blob of memory associated with ** a single shared-btree. The memory is used by client code for it's own diff --git a/src/btree.h b/src/btree.h index 513e9a0826..50b5317b62 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.73 2007/03/29 05:51:49 drh Exp $ +** @(#) $Id: btree.h,v 1.74 2007/03/30 14:06:34 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -67,6 +67,8 @@ int sqlite3BtreeGetReserve(Btree*); int sqlite3BtreeSetAutoVacuum(Btree *, int); int sqlite3BtreeGetAutoVacuum(Btree *); int sqlite3BtreeBeginTrans(Btree*,int); +int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); +int sqlite3BtreeCommitPhaseTwo(Btree*); int sqlite3BtreeCommit(Btree*); int sqlite3BtreeRollback(Btree*); int sqlite3BtreeBeginStmt(Btree*); @@ -76,7 +78,6 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags); int sqlite3BtreeIsInTrans(Btree*); int sqlite3BtreeIsInStmt(Btree*); int sqlite3BtreeIsInReadTrans(Btree*); -int sqlite3BtreeSync(Btree*, const char *zMaster); void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); int sqlite3BtreeSchemaLocked(Btree *); int sqlite3BtreeLockTable(Btree *, int, u8); diff --git a/src/pager.c b/src/pager.c index 82d7f4ecd2..fdf0c9379b 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.306 2007/03/29 18:19:52 drh Exp $ +** @(#) $Id: pager.c,v 1.307 2007/03/30 14:06:34 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -160,7 +160,7 @@ struct PgHdr { u8 inStmt; /* TRUE if in the statement subjournal */ 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 */ + u8 alwaysRollback; /* Disable DontRollback() for this page */ short int nRef; /* Number of users of this page */ PgHdr *pDirty, *pPrevDirty; /* Dirty pages */ u32 notUsed; /* Buffer space */ @@ -236,9 +236,12 @@ struct Pager { u8 readOnly; /* True for a read-only database */ u8 needSync; /* True if an fsync() is needed on the journal */ u8 dirtyCache; /* True if cached pages have changed */ - u8 alwaysRollback; /* Disable dont_rollback() for all pages */ + u8 alwaysRollback; /* Disable DontRollback() for all pages */ u8 memDb; /* True to inhibit all file I/O */ u8 setMaster; /* True if a m-j name has been written to jrnl */ + u8 doNotSync; /* Boolean. While true, do not spill the cache */ + u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ + u8 changeCountDone; /* Set after incrementing the change-counter */ int errCode; /* One of several kinds of errors */ int dbSize; /* Number of pages in the file */ int origDbSize; /* dbSize before the current change */ @@ -286,9 +289,6 @@ struct Pager { #endif char *pTmpSpace; /* Pager.pageSize bytes of space for tmp use */ u32 iChangeCount; /* Db change-counter for which cache is valid */ - u8 doNotSync; /* Boolean. While true, do not spill the cache */ - u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */ - u8 changeCountDone; /* Set after incrementing the change-counter */ }; /* @@ -910,16 +910,23 @@ static void pager_reset(Pager *pPager){ } /* +** This routine ends a transaction. A transaction is ended by either +** a COMMIT or a ROLLBACK. +** ** When this routine is called, the pager has the journal file open and -** a RESERVED or EXCLUSIVE lock on the database. This routine releases -** the database lock and acquires a SHARED lock in its place. The journal -** file is deleted and closed. +** a RESERVED or EXCLUSIVE lock on the database. This routine will release +** the database lock and acquires a SHARED lock in its place if that is +** the appropriate thing to do. Release locks usually is appropriate, +** unless we are in exclusive access mode or unless this is a +** COMMIT AND BEGIN or ROLLBACK AND BEGIN operation. +** +** The journal file is either deleted or truncated. ** ** TODO: Consider keeping the journal file open for temporary databases. ** This might give a performance improvement on windows where opening ** a file is an expensive operation. */ -static int pager_unwritelock(Pager *pPager){ +static int pager_end_transaction(Pager *pPager){ PgHdr *pPg; int rc = SQLITE_OK; int rc2 = SQLITE_OK; @@ -1423,7 +1430,7 @@ static int pager_playback(Pager *pPager, int isHot){ end_playback: if( rc==SQLITE_OK ){ - rc = pager_unwritelock(pPager); + rc = pager_end_transaction(pPager); } if( zMaster ){ /* If there was a master journal and this routine will return success, @@ -2563,7 +2570,7 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){ /* 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. + ** sqlite3PagerDontRollback() 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 @@ -3096,7 +3103,7 @@ static int pager_open_journal(Pager *pPager){ rc = sqlite3PagerStmtBegin(pPager); } if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ - rc = pager_unwritelock(pPager); + rc = pager_end_transaction(pPager); if( rc==SQLITE_OK ){ rc = SQLITE_FULL; } @@ -3127,7 +3134,7 @@ failed_to_open_journal: ** Acquire a write-lock on the database. The lock is removed when ** the any of the following happen: ** -** * sqlite3PagerCommit() is called. +** * sqlite3PagerCommitPhaseTwo() is called. ** * sqlite3PagerRollback() is called. ** * sqlite3PagerClose() is called. ** * sqlite3PagerUnref() is called to on every outstanding page. @@ -3528,9 +3535,9 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void *pData){ ** 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. +** the sqlite3PagerDontRollback() routine is called. But because the +** page contains critical data, we still need to be sure it gets +** rolled back in spite of the sqlite3PagerDontRollback() call. */ void sqlite3PagerDontWrite(Pager *pPager, Pgno pgno){ PgHdr *pPg; @@ -3594,6 +3601,129 @@ void sqlite3PagerDontRollback(DbPage *pPg){ } +/* +** This routine is called to increment the database file change-counter, +** stored at byte 24 of the pager file. +*/ +static int pager_incr_changecounter(Pager *pPager){ + PgHdr *pPgHdr; + u32 change_counter; + int rc; + + if( !pPager->changeCountDone ){ + /* Open page 1 of the file for writing. */ + rc = sqlite3PagerGet(pPager, 1, &pPgHdr); + if( rc!=SQLITE_OK ) return rc; + rc = sqlite3PagerWrite(pPgHdr); + if( rc!=SQLITE_OK ) return rc; + + /* Read the current value at byte 24. */ + change_counter = retrieve32bits(pPgHdr, 24); + + /* Increment the value just read and write it back to byte 24. */ + change_counter++; + put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter); + pPager->iChangeCount = change_counter; + + /* Release the page reference. */ + sqlite3PagerUnref(pPgHdr); + pPager->changeCountDone = 1; + } + return SQLITE_OK; +} + +/* +** Sync the database file for the pager pPager. zMaster points to the name +** of a master journal file that should be written into the individual +** journal file. zMaster may be NULL, which is interpreted as no master +** journal (a single database transaction). +** +** This routine ensures that the journal is synced, all dirty pages written +** to the database file and the database file synced. The only thing that +** remains to commit the transaction is to delete the journal file (or +** master journal file if specified). +** +** Note that if zMaster==NULL, this does not overwrite a previous value +** passed to an sqlite3PagerCommitPhaseOne() call. +** +** If parameter nTrunc is non-zero, then the pager file is truncated to +** nTrunc pages (this is used by auto-vacuum databases). +*/ +int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){ + int rc = SQLITE_OK; + + PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", + pPager->zFilename, zMaster, nTrunc); + + /* If this is an in-memory db, or no pages have been written to, or this + ** function has already been called, it is a no-op. + */ + if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){ + PgHdr *pPg; + assert( pPager->journalOpen ); + + /* If a master journal file name has already been written to the + ** journal file, then no sync is required. This happens when it is + ** written, then the process fails to upgrade from a RESERVED to an + ** EXCLUSIVE lock. The next time the process tries to commit the + ** transaction the m-j name will have already been written. + */ + if( !pPager->setMaster ){ + rc = pager_incr_changecounter(pPager); + if( rc!=SQLITE_OK ) goto sync_exit; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( nTrunc!=0 ){ + /* If this transaction has made the database smaller, then all pages + ** being discarded by the truncation must be written to the journal + ** file. + */ + Pgno i; + int iSkip = PAGER_MJ_PGNO(pPager); + for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){ + if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){ + rc = sqlite3PagerGet(pPager, i, &pPg); + if( rc!=SQLITE_OK ) goto sync_exit; + rc = sqlite3PagerWrite(pPg); + sqlite3PagerUnref(pPg); + if( rc!=SQLITE_OK ) goto sync_exit; + } + } + } +#endif + rc = writeMasterJournal(pPager, zMaster); + if( rc!=SQLITE_OK ) goto sync_exit; + rc = syncJournal(pPager); + if( rc!=SQLITE_OK ) goto sync_exit; + } + +#ifndef SQLITE_OMIT_AUTOVACUUM + if( nTrunc!=0 ){ + rc = sqlite3PagerTruncate(pPager, nTrunc); + if( rc!=SQLITE_OK ) goto sync_exit; + } +#endif + + /* Write all dirty pages to the database file */ + pPg = pager_get_all_dirty_pages(pPager); + rc = pager_write_pagelist(pPg); + if( rc!=SQLITE_OK ) goto sync_exit; + + /* Sync the database file. */ + if( !pPager->noSync ){ + rc = sqlite3OsSync(pPager->fd, 0); + } + IOTRACE(("DBSYNC %p\n", pPager)) + + pPager->state = PAGER_SYNCED; + }else if( MEMDB && nTrunc!=0 ){ + rc = sqlite3PagerTruncate(pPager, nTrunc); + } + +sync_exit: + return rc; +} + + /* ** Commit all changes to the database and release the write lock. ** @@ -3601,7 +3731,7 @@ void sqlite3PagerDontRollback(DbPage *pPg){ ** and an error code is returned. If the commit worked, SQLITE_OK ** is returned. */ -int sqlite3PagerCommit(Pager *pPager){ +int sqlite3PagerCommitPhaseTwo(Pager *pPager){ int rc; PgHdr *pPg; @@ -3640,12 +3770,12 @@ int sqlite3PagerCommit(Pager *pPager){ /* Exit early (without doing the time-consuming sqlite3OsSync() calls) ** if there have been no changes to the database file. */ assert( pPager->needSync==0 ); - rc = pager_unwritelock(pPager); + rc = pager_end_transaction(pPager); }else{ assert( pPager->journalOpen ); - rc = sqlite3PagerSync(pPager, 0, 0); + rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0); if( rc==SQLITE_OK ){ - rc = pager_unwritelock(pPager); + rc = pager_end_transaction(pPager); } } return pager_error(pPager, rc); @@ -3703,7 +3833,7 @@ int sqlite3PagerRollback(Pager *pPager){ } if( !pPager->dirtyCache || !pPager->journalOpen ){ - rc = pager_unwritelock(pPager); + rc = pager_end_transaction(pPager); return rc; } @@ -3716,7 +3846,7 @@ int sqlite3PagerRollback(Pager *pPager){ if( pPager->state==PAGER_RESERVED ){ int rc2; rc = pager_playback(pPager, 0); - rc2 = pager_unwritelock(pPager); + rc2 = pager_end_transaction(pPager); if( rc==SQLITE_OK ){ rc = rc2; } @@ -3926,128 +4056,6 @@ void sqlite3PagerSetCodec( pPager->pCodecArg = pCodecArg; } -/* -** This routine is called to increment the database file change-counter, -** stored at byte 24 of the pager file. -*/ -static int pager_incr_changecounter(Pager *pPager){ - PgHdr *pPgHdr; - u32 change_counter; - int rc; - - if( !pPager->changeCountDone ){ - /* Open page 1 of the file for writing. */ - rc = sqlite3PagerGet(pPager, 1, &pPgHdr); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3PagerWrite(pPgHdr); - if( rc!=SQLITE_OK ) return rc; - - /* Read the current value at byte 24. */ - change_counter = retrieve32bits(pPgHdr, 24); - - /* Increment the value just read and write it back to byte 24. */ - change_counter++; - put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter); - pPager->iChangeCount = change_counter; - - /* Release the page reference. */ - sqlite3PagerUnref(pPgHdr); - pPager->changeCountDone = 1; - } - return SQLITE_OK; -} - -/* -** Sync the database file for the pager pPager. zMaster points to the name -** of a master journal file that should be written into the individual -** journal file. zMaster may be NULL, which is interpreted as no master -** journal (a single database transaction). -** -** This routine ensures that the journal is synced, all dirty pages written -** to the database file and the database file synced. The only thing that -** remains to commit the transaction is to delete the journal file (or -** master journal file if specified). -** -** Note that if zMaster==NULL, this does not overwrite a previous value -** passed to an sqlite3PagerSync() call. -** -** If parameter nTrunc is non-zero, then the pager file is truncated to -** nTrunc pages (this is used by auto-vacuum databases). -*/ -int sqlite3PagerSync(Pager *pPager, const char *zMaster, Pgno nTrunc){ - int rc = SQLITE_OK; - - PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", - pPager->zFilename, zMaster, nTrunc); - - /* If this is an in-memory db, or no pages have been written to, or this - ** function has already been called, it is a no-op. - */ - if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){ - PgHdr *pPg; - assert( pPager->journalOpen ); - - /* If a master journal file name has already been written to the - ** journal file, then no sync is required. This happens when it is - ** written, then the process fails to upgrade from a RESERVED to an - ** EXCLUSIVE lock. The next time the process tries to commit the - ** transaction the m-j name will have already been written. - */ - if( !pPager->setMaster ){ - rc = pager_incr_changecounter(pPager); - if( rc!=SQLITE_OK ) goto sync_exit; -#ifndef SQLITE_OMIT_AUTOVACUUM - if( nTrunc!=0 ){ - /* If this transaction has made the database smaller, then all pages - ** being discarded by the truncation must be written to the journal - ** file. - */ - Pgno i; - int iSkip = PAGER_MJ_PGNO(pPager); - for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){ - if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){ - rc = sqlite3PagerGet(pPager, i, &pPg); - if( rc!=SQLITE_OK ) goto sync_exit; - rc = sqlite3PagerWrite(pPg); - sqlite3PagerUnref(pPg); - if( rc!=SQLITE_OK ) goto sync_exit; - } - } - } -#endif - rc = writeMasterJournal(pPager, zMaster); - if( rc!=SQLITE_OK ) goto sync_exit; - rc = syncJournal(pPager); - if( rc!=SQLITE_OK ) goto sync_exit; - } - -#ifndef SQLITE_OMIT_AUTOVACUUM - if( nTrunc!=0 ){ - rc = sqlite3PagerTruncate(pPager, nTrunc); - if( rc!=SQLITE_OK ) goto sync_exit; - } -#endif - - /* Write all dirty pages to the database file */ - pPg = pager_get_all_dirty_pages(pPager); - rc = pager_write_pagelist(pPg); - if( rc!=SQLITE_OK ) goto sync_exit; - - /* Sync the database file. */ - if( !pPager->noSync ){ - rc = sqlite3OsSync(pPager->fd, 0); - } - IOTRACE(("DBSYNC %p\n", pPager)) - - pPager->state = PAGER_SYNCED; - }else if( MEMDB && nTrunc!=0 ){ - rc = sqlite3PagerTruncate(pPager, nTrunc); - } - -sync_exit: - return rc; -} - #ifndef SQLITE_OMIT_AUTOVACUUM /* ** Move the page identified by pData to location pgno in the file. diff --git a/src/pager.h b/src/pager.h index e92bd0693a..a6f639e05d 100644 --- a/src/pager.h +++ b/src/pager.h @@ -13,7 +13,7 @@ ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. ** -** @(#) $Id: pager.h,v 1.56 2007/03/27 16:19:52 danielk1977 Exp $ +** @(#) $Id: pager.h,v 1.57 2007/03/30 14:06:34 drh Exp $ */ #ifndef _PAGER_H_ @@ -101,8 +101,8 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void*); int sqlite3PagerPagecount(Pager*); int sqlite3PagerTruncate(Pager*,Pgno); int sqlite3PagerBegin(DbPage*, int exFlag); -int sqlite3PagerCommit(Pager*); -int sqlite3PagerSync(Pager*,const char *zMaster, Pgno); +int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno); +int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerIsreadonly(Pager*); int sqlite3PagerStmtBegin(Pager*); diff --git a/src/test2.c b/src/test2.c index 828423e4e3..00e05c0379 100644 --- a/src/test2.c +++ b/src/test2.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test2.c,v 1.41 2007/03/19 17:44:28 danielk1977 Exp $ +** $Id: test2.c,v 1.42 2007/03/30 14:06:34 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -163,7 +163,12 @@ static int pager_commit( return TCL_ERROR; } pPager = sqlite3TextToPtr(argv[1]); - rc = sqlite3PagerCommit(pPager); + rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, errorName(rc), 0); + return TCL_ERROR; + } + rc = sqlite3PagerCommitPhaseTwo(pPager); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 23bd3b6b03..9befa36f59 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -1090,19 +1090,19 @@ static int vdbeCommit(sqlite3 *db){ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - rc = sqlite3BtreeSync(pBt, 0); + rc = sqlite3BtreeCommitPhaseOne(pBt, 0); } } - /* Do the commit only if all databases successfully synced. - ** If one of the BtreeCommit() calls fails, this indicates an IO error - ** while deleting or truncating a journal file. It is unlikely, but - ** could happen. In this case abandon processing and return the error. + /* Do the commit only if all databases successfully complete phase 1. + ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an + ** IO error while deleting or truncating a journal file. It is unlikely, + ** but could happen. In this case abandon processing and return the error. */ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - rc = sqlite3BtreeCommit(pBt); + rc = sqlite3BtreeCommitPhaseTwo(pBt); } } if( rc==SQLITE_OK ){ @@ -1182,16 +1182,16 @@ static int vdbeCommit(sqlite3 *db){ ** sets the master journal pointer in each individual journal. If ** an error occurs here, do not delete the master journal file. ** - ** If the error occurs during the first call to sqlite3BtreeSync(), - ** then there is a chance that the master journal file will be - ** orphaned. But we cannot delete it, in case the master journal - ** file name was written into the journal file before the failure - ** occured. + ** If the error occurs during the first call to + ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the + ** master journal file will be orphaned. But we cannot delete it, + ** in case the master journal file name was written into the journal + ** file before the failure occured. */ for(i=0; rc==SQLITE_OK && inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt && sqlite3BtreeIsInTrans(pBt) ){ - rc = sqlite3BtreeSync(pBt, zMaster); + rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster); } } sqlite3OsClose(&master); @@ -1223,17 +1223,17 @@ static int vdbeCommit(sqlite3 *db){ } /* All files and directories have already been synced, so the following - ** calls to sqlite3BtreeCommit() are only closing files and deleting - ** journals. If something goes wrong while this is happening we don't - ** really care. The integrity of the transaction is already guaranteed, - ** but some stray 'cold' journals may be lying around. Returning an - ** error code won't help matters. + ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and + ** deleting or truncating journals. If something goes wrong while + ** this is happening we don't really care. The integrity of the + ** transaction is already guaranteed, but some stray 'cold' journals + ** may be lying around. Returning an error code won't help matters. */ disable_simulated_io_errors(); for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ - sqlite3BtreeCommit(pBt); + sqlite3BtreeCommitPhaseTwo(pBt); } } enable_simulated_io_errors();