Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -3422,10 +3422,18 @@ if( rc==SQLITE_OK ){ put4byte(&pPage1->aData[28], pBt->nPage); } } } + }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){ + /* Even if there was no transaction opened when this function was + ** called, a race condition may cause an SQLITE_BUSY_SNAPSHOT error + ** in wal mode (since the code above opens a read-transaction and then + ** upgrades it to a write-transaction - it does not take the write lock + ** atomically). In this case change the error code to SQLITE_BUSY. */ + assert( wrflag ); + rc = SQLITE_BUSY; } trans_begun: if( rc==SQLITE_OK ){ Index: test/walprotocol2.test ================================================================== --- test/walprotocol2.test +++ test/walprotocol2.test @@ -70,11 +70,11 @@ do_catchsql_test 2.2 { BEGIN EXCLUSIVE; } {1 {database is locked}} do_test 2.3 { sqlite3_extended_errcode db -} {SQLITE_BUSY_SNAPSHOT} +} {SQLITE_BUSY} #--------------------------------------------------------------- # Same again, but with a busy-handler. This time, following the # SQLITE_BUSY_SNAPSHOT error the busy-handler is invoked and then the # whole thing retried from the beginning. This time it succeeds.