-C Fix\sa\stypo\sin\sLICENSE.md
-D 2024-10-29T14:22:12.268
+C Allow\sread-only\sconnections\sto\signore\shot\sjournals\screated\sby\s"PRAGMA\sjournal_mode\s=\swal".
+D 2024-10-29T19:34:58.292
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md c5b4009dca54d127d2d6033c22fd9cc34f53bedb6ef12c7cbaa468381c74ab28
F src/os_unix.c 0ad4e0885294b3a0e135a18533590ec9ad91ffe82f6a08e55b40babd51772928
F src/os_win.c 69fa1aaff68270423c85cff4327ba17ef99a1eb017e1a2bfb97416d9b8398b05
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
-F src/pager.c 9656ad4e8331efb8a4f94f7a0c6440b98caea073950a367ea0c728a53b8e62c9
+F src/pager.c 170558eb359dd374159141c1f8325480d2d3181439738e7fbb938462c5c9c2c4
F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
F src/parse.y a7a8d42eeff01d267444ddb476029b0b1726fb70ae3d77984140f17ad02e2d61
F src/pcache.c 588cc3c5ccaaadde689ed35ce5c5c891a1f7b1f4d1f56f6cf0143b74d8ee6484
F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
F test/nan.test 73ea63ab43668313e2f8cc9ef9e9a966672c7934f3ce76926fbe991235d07d91
F test/nockpt.test 8c43b25af63b0bd620cf1b003529e37b6f1dc53bd22690e96a1bd73f78dde53a
-F test/nolock.test f196cf8b8fbea4e2ca345140a2b3f3b0da45c76e
+F test/nolock.test 284c72c2b752cf3ffbfd224dfa868bd524e624d3ab5c5ef181fc49ad9df0b444
F test/normalize.test f23b6c5926c59548635fcf39678ac613e726121e073dd902a3062fbb83903b72
F test/notify1.test 669b2b743618efdc18ca4b02f45423d5d2304abf
F test/notify2.test 2ecabaa1305083856b7c39cf32816b612740c161
F test/rbu.test 168573d353cd0fd10196b87b0caa322c144ef736
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/readonly.test 69a7ccec846cad2e000b3539d56360d02f327061dc5e41f7f9a3e01f19719952
+F test/readonly2.test df7b572df01a0f773bb7b6e64bd8c98a55cb3099b4ffd054a44319c5f7e87f14
F test/recover.test a163a156ea9f2beea63fa83c4dcd8dea6e57b8a569fc647155e3d2754eaac1b5
F test/regexp1.test 8f2a8bc1569666e29a4cee6c1a666cd224eb6d50e2470d1dc1df995170f3e0f1
F test/regexp2.test 55ed41da802b0e284ac7e2fe944be3948f93ff25abbca0361a609acfed1368b5
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 1d24a29c6ef05185950ba5c45f2a60a92f12a8e5c57026b599f716c9f2f6cf84
-R 39666de7648cff00f94d62d8ecd9d6d5
-U drh
-Z 98e5f326f4e37c32b0833374f38b52df
+P decc60034849c232a05c8eb93ff0c6a5d6a48336d960771ed096d89633a9d0e2
+R b458c57cdc073a8cbc08997978169801
+T *branch * readonly-ignore-wal-jrnl
+T *sym-readonly-ignore-wal-jrnl *
+T -sym-trunk *
+U dan
+Z 5e2548d07e7a74989e8031dcc6402807
# Remove this line to create a well-formed Fossil manifest.
-decc60034849c232a05c8eb93ff0c6a5d6a48336d960771ed096d89633a9d0e2
+d003480db7443025cfd2227096fd929454e9243d5f393f296b74f9bdad15d396
}
+/*
+** This is used by a read-only database connection in cases where there
+** is a hot journal which cannot be rolled back, but the hot journal was
+** created by an aborted "PRAGMA journal_mode = wal" statement. In this
+** case we can proceed, but must set bytes 18 and 19 of page 1 of the
+** db file to indicate that this is not a wal mode database.
+*/
+static int getPageOneNoWal(
+ Pager *pPager, /* The pager open on the database file */
+ Pgno pgno, /* Page number to fetch */
+ DbPage **ppPage, /* Write a pointer to the page here */
+ int flags /* PAGER_GET_XXX flags */
+){
+ int rc = SQLITE_OK;
+
+ assert( pgno==1 );
+ rc = getPageNormal(pPager, pgno, ppPage, flags);
+ if( rc==SQLITE_OK ){
+ u8 *aPg = (u8*)(*ppPage)->pData;
+ aPg[18] = 1;
+ aPg[19] = 1;
+ }
+ setGetterMethod(pPager);
+ return rc;
+}
+
+/*
+** This function is called after a potentially hot journal has been opened
+** to inspect its contents and determine whether or not it really is a
+** hot journal. The journal is not a hot journal in two cases:
+**
+** * The first byte of the journal is 0x00, or,
+**
+** * The connection is opened read-only, the journal consist of page 1
+** of the db only, and the contents of page 1 differs from the contents
+** of the db only in bytes 18 and 19 (the wal mode setting).
+**
+** If the journal is not hot, then output variable (*pbHot) is set to 0
+** before this function returns. Otherwise, if the journal is hot, (*pbHot)
+** is set to 1.
+*/
+static int checkHotJournal(Pager *pPager, int *pbHot){
+ char aHdr[28];
+ int rc = SQLITE_OK;
+
+ *pbHot = 1;
+ rc = sqlite3OsRead(pPager->jfd, (void *)aHdr, sizeof(aHdr), 0);
+ if( rc==SQLITE_OK ){
+ if( aHdr[0]==0 ){
+ *pbHot = 0;
+ }else if( pPager->readOnly ){
+ u32 nPg = sqlite3Get4byte(&aHdr[8]);
+ u32 off = sqlite3Get4byte(&aHdr[20]);
+ u32 pgsz = sqlite3Get4byte(&aHdr[24]);
+
+ if( nPg==0xFFFFFFFF ){
+ i64 sz = 0;
+ rc = sqlite3OsFileSize(pPager->jfd, &sz);
+ nPg = ((sz-off) / pgsz);
+ }
+ if( nPg==1 ){
+ u8 *a1 = 0;
+ a1 = sqlite3_malloc(pgsz*2);
+ if( a1==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ u8 *a2 = &a1[pgsz];
+ rc = sqlite3OsRead(pPager->jfd, a1, pgsz, off+4);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsRead(pPager->fd, a2, pgsz, 0);
+ memcpy(&a2[18], &a1[18], 2);
+ memcpy(&a2[24], &a1[24], 4);
+ memcpy(&a2[92], &a1[92], 8);
+ }
+ if( rc==SQLITE_OK && memcmp(a1, a2, pgsz)==0 ){
+ *pbHot = 0;
+ pPager->xGet = getPageOneNoWal;
+ }
+ sqlite3_free(a1);
+ }
+ }
+ }
+ }
+ if( rc==SQLITE_IOERR_SHORT_READ ){
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
/*
** This function is called after transitioning from PAGER_UNLOCK to
** PAGER_SHARED state. It tests if there is a hot journal present in
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
}
if( rc==SQLITE_OK ){
- u8 first = 0;
- rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
- if( rc==SQLITE_IOERR_SHORT_READ ){
- rc = SQLITE_OK;
- }
+ rc = checkHotJournal(pPager, pExists);
if( !jrnlOpen ){
sqlite3OsClose(pPager->jfd);
}
- *pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if
** it has a zero header, that might be due to an I/O error, or
assert( !MEMDB );
assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
+ assert( pPager->xGet!=getPageOneNoWal );
rc = pager_wait_on_lock(pPager, SHARED_LOCK);
if( rc!=SQLITE_OK ){
assert( !MEMDB );
pager_unlock(pPager);
assert( pPager->eState==PAGER_OPEN );
+ testcase( pPager->xGet==getPageOneNoWal );
+ setGetterMethod(pPager);
}else{
pPager->eState = PAGER_READER;
pPager->hasHeldSharedLock = 1;
return pPager->errCode;
}
-
/* Dispatch all page fetch requests to the appropriate getter method.
*/
int sqlite3PagerGet(
set testdir [file dirname $argv0]
source $testdir/tester.tcl
+set testprefix nolock
unset -nocomplain tvfs_calls
proc tvfs_reset {} {
} {1 {unable to open database file}}
}
+#-------------------------------------------------------------------------
+reset_db
+do_execsql_test 5.0 {
+ CREATE TABLE t1(x, y);
+ CREATE TABLE t2(x, y);
+ INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6);
+ INSERT INTO t2 VALUES(1, 2), (3, 4), (5, 6);
+ PRAGMA cache_size = 0;
+ BEGIN;
+ INSERT INTO t1 VALUES(7, 8);
+ INSERT INTO t2 VALUES(7, 8);
+}
+
+do_test 5.1 {
+ forcecopy test.db test.db2
+ forcecopy test.db-journal test.db2-journal
+ sqlite3 db2 file:test.db2?immutable=0 -uri 1 -readonly 1
+
+ list [catch {
+ db2 eval {
+ SELECT * FROM t1;
+ SELECT * FROM t2;
+ }
+ } msg] $msg
+} {1 {attempt to write a readonly database}}
+
finish_test
+
--- /dev/null
+# 2024 October 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 that readonly database connections may ignore a hot journal
+# created by an aborted "PRAGMA journal_mode = wal" statement.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+set ::testprefix readonly2
+
+testvfs tvfs -default 1
+tvfs script at_vfs_callback
+tvfs filter {xDelete}
+
+set ::delete_shall_fail 0
+proc at_vfs_callback {method file z args} {
+ if {$::delete_shall_fail} {
+ return "SQLITE_IOERR"
+ }
+ return "SQLITE_OK"
+}
+
+reset_db
+do_execsql_test 1.0 {
+ BEGIN;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6);
+ COMMIT;
+}
+
+set ::delete_shall_fail 1
+do_catchsql_test 1.1 {
+ PRAGMA journal_mode = wal;
+} {1 {disk I/O error}}
+
+do_test 1.2 {
+ file exists test.db-journal
+} 1
+
+do_test 1.3 {
+ sqlite3 db2 test.db -readonly 1
+ db2 eval {
+ SELECT * FROM t1
+ }
+} {1 2 3 4 5 6}
+
+do_test 1.4 {
+ file exists test.db-journal
+} 1
+
+set ::delete_shall_fail 0
+do_execsql_test 1.5 {
+ SELECT * FROM t1
+} {1 2 3 4 5 6}
+
+set ::delete_shall_fail 1
+do_catchsql_test 1.6 {
+ PRAGMA user_version = 444;
+} {1 {disk I/O error}}
+
+do_test 1.7 {
+ file exists test.db-journal
+} 1
+
+do_test 1.8 {
+ list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
+} {1 {attempt to write a readonly database}}
+
+do_test 1.9 {
+ file exists test.db-journal
+} 1
+
+set ::delete_shall_fail 0
+do_execsql_test 1.10 {
+ SELECT * FROM t1
+} {1 2 3 4 5 6}
+
+
+finish_test