From: dan Date: Tue, 16 Sep 2014 20:02:41 +0000 (+0000) Subject: Clarify the effects of the pager_ota_mode pragma. Add tests and fixes for the same. X-Git-Tag: version-3.8.11~252^2~100 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b95de09bced91297927bc16c399b05c4dea8645;p=thirdparty%2Fsqlite.git Clarify the effects of the pager_ota_mode pragma. Add tests and fixes for the same. FossilOrigin-Name: decaccc37cbdcd2a663233469efdf4982a810513 --- diff --git a/ext/ota/ota4.test b/ext/ota/ota4.test new file mode 100644 index 0000000000..d0c9dfae40 --- /dev/null +++ b/ext/ota/ota4.test @@ -0,0 +1,121 @@ +# 2014 August 30 +# +# 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. +# +#*********************************************************************** +# +# Test some properties of the pager_ota_mode pragma. +# + +set testdir [file join [file dirname $argv0] .. .. test] +source $testdir/tester.tcl +set ::testprefix ota4 + +# 1. Cannot set the pager_ota_mode flag on a WAL mode database. +# +# 2. Or if there is an open read transaction. +# +# 3. Cannot start a transaction with pager_ota_mode set if there +# is a WAL file in the file-system. +# +# 4. Or if the wal-mode flag is set in the database file header. +# +# 5. Cannot open a transaction with pager_ota_mode set if the database +# file has been modified by a rollback mode client since the *-oal +# file was started. +# + +do_execsql_test 1.1 { + PRAGMA journal_mode = wal; + SELECT * FROM sqlite_master; +} {wal} +do_catchsql_test 1.2 { + PRAGMA pager_ota_mode = 1 +} {1 {cannot set pager_ota_mode in wal mode}} + + +do_execsql_test 2.1 { + PRAGMA journal_mode = delete; + BEGIN; + SELECT * FROM sqlite_master; +} {delete} +do_catchsql_test 2.2 { + PRAGMA pager_ota_mode = 1 +} {1 {cannot set pager_ota_mode with open transaction}} +do_execsql_test 2.3 { + COMMIT; +} {} + + +do_execsql_test 3.1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); +} {wal} +do_test 3.2 { + forcecopy test.db-wal test.db-bak + execsql { + PRAGMA journal_mode = delete; + PRAGMA pager_ota_mode = 1; + } + forcecopy test.db-bak test.db-wal + catchsql { + SELECT * FROM sqlite_master + } +} {1 {unable to open database file}} + +do_test 4.1 { + db close + forcedelete test.db-wal test.db-oal + sqlite3 db test.db + execsql { + PRAGMA journal_mode = wal; + PRAGMA pager_ota_mode = 1; + } + catchsql { + SELECT * FROM sqlite_master; + } +} {1 {unable to open database file}} + +do_test 5.1 { + forcedelete test.db-oal + reset_db + execsql { + PRAGMA journal_mode = delete; + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES(1, 2); + } + execsql { + PRAGMA pager_ota_mode = 1; + INSERT INTO t1 VALUES(3, 4); + } + db close + sqlite3 db test.db + execsql { + SELECT * FROM t1; + } +} {1 2} +do_execsql_test 5.2 { + PRAGMA pager_ota_mode = 1; + SELECT * FROM t1; + INSERT INTO t1 VALUES(5, 6); +} {1 2 3 4} +do_test 5.3 { + db close + sqlite3 db test.db + execsql { + INSERT INTO t1 VALUES(7, 8); + SELECT * FROM t1; + } +} {1 2 7 8} +do_catchsql_test 5.4 { + PRAGMA pager_ota_mode = 1; + SELECT * FROM t1; +} {1 {database is locked}} + +finish_test diff --git a/manifest b/manifest index 7898dc2ecf..bcb8678d88 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\sexperimental\ssqlite3_transaction_save()\sand\srestore()\sAPIs. -D 2014-09-15T19:34:04.372 +C Clarify\sthe\seffects\sof\sthe\spager_ota_mode\spragma.\sAdd\stests\sand\sfixes\sfor\sthe\ssame. +D 2014-09-16T20:02:41.756 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -125,6 +125,7 @@ F ext/ota/ota.c d37097e92a005d3915883adefbb93019ea6f8841 F ext/ota/ota1.test 7cbf37a9f6cd29320f47b041cfeb0cc1d7eaa916 F ext/ota/ota2.test 13f76922446c62ed96192e938b8e625ebf0142fa F ext/ota/ota3.test 1c48b7476af1c5920db9a43e7b1476d421a463b5 +F ext/ota/ota4.test ec01b0d69ad2989559a65fde74560c1cfcca8202 F ext/ota/sqlite3ota.c 668ed08dd81ff8ae1e8524b2d4bf0f2609cbf907 F ext/ota/sqlite3ota.h 39ce4dffbfcf4ade9e4526369fe2243709345c8e F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 @@ -220,13 +221,13 @@ F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa F src/os_unix.c addd023b26c623fec4dedc110fc4370a65b4768c F src/os_win.c 0a4042ef35f322e86fa01f6c8884c5e645b911e7 F src/os_win.h 09e751b20bbc107ffbd46e13555dc73576d88e21 -F src/pager.c c1cdf5509386b5c3695082655ce6de6431d920d6 -F src/pager.h 5c13927809e1c35d85e82e14342d817df3019e07 +F src/pager.c 2dafc02d49457084c92472f934fe26a75e6d08f5 +F src/pager.h b62e645e8a19e4f0181253d1663a09f2793d8c94 F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0 F src/pcache.c 4121a0571c18581ee9f82f086d5e2030051ebd6a F src/pcache.h 9b559127b83f84ff76d735c8262f04853be0c59a F src/pcache1.c dab8ab930d4a73b99768d881185994f34b80ecaa -F src/pragma.c c401b5ddbb5c882e2b9d9e16fad2abd8b4a86564 +F src/pragma.c 5b255c09d6e38a37ec07830b92acceec5cab8c85 F src/prepare.c 6ef0cf2f9274982988ed6b7cab1be23147e94196 F src/printf.c e74925089a85e3c9f0e315595f41c139d3d118c2 F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece @@ -304,8 +305,8 @@ F src/vdbemem.c dc36ea9fe26c25550c50085f388167086ef7d73a F src/vdbesort.c a7a40ceca6325b853040ffcc363dcd49a45f201b F src/vdbetrace.c 16d39c1ef7d1f4a3a7464bea3b7b4bdd7849c415 F src/vtab.c 019dbfd0406a7447c990e1f7bd1dfcdb8895697f -F src/wal.c 3c56c85d80a17b51cd8cb0bed5c1ecc4f0771012 -F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 +F src/wal.c 8bd0ced6cf1d3389fd6a73b4f12a1e2bf926e75a +F src/wal.h e25f9d383ffb07986ba20b78dbde2c1d0cb36ab6 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 F src/where.c 839b5e1db2507e221ad1c308f148a8519ed750be F src/whereInt.h 124d970450955a6982e174b07c320ae6d62a595c @@ -1204,7 +1205,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 5efafef51d146bcba3adc425561bfa1ac083c0a7 -R 684c9325f89b1d695bf7f6172a6df69f +P 48d201cd8b68c0377cf8a2cc6439b893f9462fe2 +R de185427497b23c6bf5f7a43a97cdd26 U dan -Z 4568916327f8707f39b56bf66a2404a6 +Z de73fdb4c4401b74adf143cdfee4f7e2 diff --git a/manifest.uuid b/manifest.uuid index 282b6874b8..fa8e1e694f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -48d201cd8b68c0377cf8a2cc6439b893f9462fe2 \ No newline at end of file +decaccc37cbdcd2a663233469efdf4982a810513 \ No newline at end of file diff --git a/src/pager.c b/src/pager.c index 7de6a65550..967f776f09 100644 --- a/src/pager.c +++ b/src/pager.c @@ -615,6 +615,15 @@ struct PagerSavepoint { ** is set to zero in all other states. In PAGER_ERROR state, Pager.errCode ** is always set to SQLITE_FULL, SQLITE_IOERR or one of the SQLITE_IOERR_XXX ** sub-codes. +** +** otaMode +** This variable is normally 0. It is set to 1 by the PagerSetOtaMode() +** function - as a result of a "PRAGMA pager_ota_mode=1" command. Once +** the *-oal file has been opened and it has been determined that the +** database file has not been modified since it was created, this variable +** is set to 2. +** +** */ struct Pager { sqlite3_vfs *pVfs; /* OS functions to use for IO */ @@ -630,7 +639,7 @@ struct Pager { u8 noLock; /* Do not lock (except in WAL mode) */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ - u8 otaMode; /* True if in ota_mode */ + u8 otaMode; /* Non-zero if in ota_mode */ /************************************************************************** ** The following block contains those class members that change during @@ -5183,6 +5192,15 @@ int sqlite3PagerSharedLock(Pager *pPager){ if( pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); + if( rc==SQLITE_OK && pPager->otaMode==1 ){ + rc = sqlite3WalCheckSalt(pPager->pWal, pPager->fd); + if( rc!=SQLITE_OK ){ + sqlite3WalClose(pPager->pWal, 0, 0, 0); + pPager->pWal = 0; + }else{ + pPager->otaMode = 2; + } + } } if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ @@ -7237,6 +7255,20 @@ int sqlite3PagerCloseWal(Pager *pPager){ return rc; } +/* +** This function is called by the wal.c module to obtain the 8 bytes of +** "salt" written into the wal file header. In OTA mode, this is a copy +** of bytes 24-31 of the database file. In non-OTA mode, it is 8 bytes +** of pseudo-random data. +*/ +void sqlite3PagerWalSalt(Pager *pPager, u32 *aSalt){ + if( pPager->otaMode ){ + memcpy(aSalt, pPager->dbFileVers, 8); + }else{ + sqlite3_randomness(8, aSalt); + } +} + #endif /* !SQLITE_OMIT_WAL */ #ifdef SQLITE_ENABLE_ZIPVFS @@ -7260,7 +7292,7 @@ int sqlite3PagerSetOtaMode(Pager *pPager, int bOta){ if( pPager->pWal || pPager->eState!=PAGER_OPEN ){ return SQLITE_ERROR; } - pPager->otaMode = (u8)bOta; + pPager->otaMode = 1; return SQLITE_OK; } diff --git a/src/pager.h b/src/pager.h index 57948079d2..0e928fe64c 100644 --- a/src/pager.h +++ b/src/pager.h @@ -208,5 +208,6 @@ void *sqlite3PagerCodec(DbPage *); #endif int sqlite3PagerSetOtaMode(Pager *pPager, int bOta); +void sqlite3PagerWalSalt(Pager *pPager, u32 *aSalt); #endif /* _PAGER_H_ */ diff --git a/src/pragma.c b/src/pragma.c index 9785a33c26..ee99002274 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -877,13 +877,37 @@ void sqlite3Pragma( /* ** PRAGMA [database.]pager_ota_mode=[01] + ** + ** This pragma sets a flag on the pager associated with the main database + ** only. The flag can only be set when there is no open transaction and + ** the pager does not already have an open WAL file. + ** + ** Once the flag has been set, it is not possible to open a regular WAL + ** file. If, when the next read-transaction is opened, a *-wal file is + ** found or the database header flags indicate that it is a wal-mode + ** database, SQLITE_CANTOPEN is returned. + ** + ** Otherwise, if no WAL file or flags are found, the pager opens the *-oal + ** file and uses it as a write-ahead-log with the *-shm data stored in + ** heap-memory. If the *-oal file already exists but the database file has + ** been modified since it was created, an SQLITE_BUSY_SNAPSHOT error is + ** returned and the read-transaction cannot be opened. + ** + ** Other clients see a rollback-mode database on which the pager_ota_mode + ** client is holding a SHARED lock. */ case PragTyp_PAGER_OTA_MODE: { Btree *pBt = pDb->pBt; assert( pBt!=0 ); if( zRight ){ int iArg = !!sqlite3Atoi(zRight); - rc = sqlite3PagerSetOtaMode(sqlite3BtreePager(pBt), iArg); + if( sqlite3BtreeIsInReadTrans(pBt) ){ + sqlite3ErrorMsg(pParse, + "cannot set pager_ota_mode with open transaction" + ); + }else if( sqlite3PagerSetOtaMode(sqlite3BtreePager(pBt), iArg) ){ + sqlite3ErrorMsg(pParse, "cannot set pager_ota_mode in wal mode"); + } } break; } diff --git a/src/wal.c b/src/wal.c index aa797dfb53..c7ad2bcb2c 100644 --- a/src/wal.c +++ b/src/wal.c @@ -2769,7 +2769,9 @@ int sqlite3WalFrames( sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION); sqlite3Put4byte(&aWalHdr[8], szPage); sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt); - if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt); + if( pWal->nCkpt==0 ){ + sqlite3PagerWalSalt(pList->pPager, pWal->hdr.aSalt); + } memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8); walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum); sqlite3Put4byte(&aWalHdr[24], aCksum[0]); @@ -3084,6 +3086,24 @@ int sqlite3WalHeapMemory(Wal *pWal){ return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ); } +/* +** Unless the wal file is empty, check that the 8 bytes of salt stored in +** the wal header are identical to those in the buffer indicated by the +** second argument. If they are not, return SQLITE_BUSY_SNAPSHOT. Otherwise, +** if the buffers match or the WAL file is empty, return SQLITE_OK. +*/ +int sqlite3WalCheckSalt(Wal *pWal, sqlite3_file *pFd){ + int rc = SQLITE_OK; + if( pWal->hdr.mxFrame>0 ){ + u8 aData[16]; + rc = sqlite3OsRead(pFd, aData, sizeof(aData), 24); + if( rc==SQLITE_OK && memcmp(pWal->hdr.aSalt, aData, 8) ){ + rc = SQLITE_BUSY_SNAPSHOT; + } + } + return rc; +} + #ifdef SQLITE_ENABLE_ZIPVFS /* ** If the argument is not NULL, it points to a Wal object that holds a diff --git a/src/wal.h b/src/wal.h index 092546354b..1c6f27d8f0 100644 --- a/src/wal.h +++ b/src/wal.h @@ -126,6 +126,8 @@ int sqlite3WalExclusiveMode(Wal *pWal, int op); */ int sqlite3WalHeapMemory(Wal *pWal); +int sqlite3WalCheckSalt(Wal *pWal, sqlite3_file*); + #ifdef SQLITE_ENABLE_ZIPVFS /* If the WAL file is not empty, return the number of bytes of content ** stored in each frame (i.e. the db page-size when the WAL was created).