Clarify the effects of the pager_ota_mode pragma. Add tests and fixes for the same.

FossilOrigin-Name: decaccc37cbdcd2a663233469efdf4982a810513
This commit is contained in:
dan 2014-09-16 20:02:41 +00:00
parent 15e1ec4c5b
commit 1b95de09bc
8 changed files with 216 additions and 15 deletions

121
ext/ota/ota4.test Normal file
View File

@ -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

View File

@ -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

View File

@ -1 +1 @@
48d201cd8b68c0377cf8a2cc6439b893f9462fe2
decaccc37cbdcd2a663233469efdf4982a810513

View File

@ -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;
}

View File

@ -208,5 +208,6 @@ void *sqlite3PagerCodec(DbPage *);
#endif
int sqlite3PagerSetOtaMode(Pager *pPager, int bOta);
void sqlite3PagerWalSalt(Pager *pPager, u32 *aSalt);
#endif /* _PAGER_H_ */

View File

@ -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;
}

View File

@ -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

View File

@ -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).