--- /dev/null
+# 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
-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
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
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
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
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
-48d201cd8b68c0377cf8a2cc6439b893f9462fe2
\ No newline at end of file
+decaccc37cbdcd2a663233469efdf4982a810513
\ No newline at end of file
** 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 */
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
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 ){
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
if( pPager->pWal || pPager->eState!=PAGER_OPEN ){
return SQLITE_ERROR;
}
- pPager->otaMode = (u8)bOta;
+ pPager->otaMode = 1;
return SQLITE_OK;
}
#endif
int sqlite3PagerSetOtaMode(Pager *pPager, int bOta);
+void sqlite3PagerWalSalt(Pager *pPager, u32 *aSalt);
#endif /* _PAGER_H_ */
/*
** 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;
}
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]);
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
*/
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).