1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add tests for another application writing the database while an ota update is ongoing.

FossilOrigin-Name: 2402baa0027ca65e9a5106b8b9c4e10f40d2cbc3
This commit is contained in:
dan
2014-10-21 18:09:58 +00:00
parent ae0151c5a7
commit 5e3fc6e99f
5 changed files with 180 additions and 33 deletions

View File

@ -19,11 +19,11 @@ SQLite Hacks
* All non-temporary triggers are disabled.
2) PRAGMA pager_ota_mode:
2) PRAGMA pager_ota_mode=1:
This pragma sets a flag on the pager associated with the main database only. In
a zipvfs system, this pragma is intercepted by zipvfs and the flag is set on
the lower level pager only.
This pragma sets a flag on the pager associated with the main database only.
In a zipvfs system, this pragma is intercepted by zipvfs and the flag is set
on the lower level pager only.
The flag can only be set when there is no open transaction and the pager does
not already have an open WAL file. Attempting to do so is an error.
@ -36,13 +36,13 @@ SQLite Hacks
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.
The 8-bytes of "salt" at teh start of an *-oal file is a copy of the 8 bytes
The 8-bytes of "salt" at the start of an *-oal file is a copy of the 8 bytes
starting at offset 24 of the database file header (the change counter and the
number of pages in the file). If the *-oal file already exists when it is
opened, SQLite checks that the salt still matches the database header fields.
If not, it concludes that the database file has been written by a rollback-mode
client since the *-oal wa created and an SQLITE_BUSY_SNAPSHOT error is
returned. No read-transaction can be opened in this case.
If not, it concludes that the database file has been written by a
rollback-mode client since the *-oal wa created and an SQLITE_BUSY_SNAPSHOT
error is returned. No read-transaction can be opened in this case.
A pager with the pager_ota_mode flag set never runs a checkpoint.
@ -51,14 +51,26 @@ SQLite Hacks
pager_ota_mode connections. If two or more such connections attempt to write
simultaneously, the results are undefined.
3) PRAGMA pager_ota_mode=2:
3) sqlite3_index_writer()
The pager_ota_mode pragma may also be set to 2 if the main database is open
in WAL mode. This prevents SQLite from checkpointing the wal file as part
of sqlite3_close().
The effects of setting pager_ota_mode=2 if the db is not in WAL mode are
undefined.
4) sqlite3_index_writer()
This new API function is used to create VMs that can insert or delete entries
from individual index b-trees within the database. The VMs apply affinities
and check that UNIQUE constraints are not violated before updating index
b-trees.
5) sqlite3_ckpt_open/step/close()
API for performing (and resuming) incremental checkpoints.
The OTA extension
-----------------

128
ext/ota/ota6.test Normal file
View File

@ -0,0 +1,128 @@
# 2014 October 21
#
# 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.
#
#***********************************************************************
#
# This file contains tests for the OTA module. Specifically, it tests the
# outcome of some other client writing to the database while an OTA update
# is being applied.
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
set ::testprefix ota6
proc setup_test {} {
reset_db
execsql {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
CREATE TABLE t2(a INTEGER PRIMARY KEY, b UNIQUE);
CREATE TABLE t3(a INTEGER PRIMARY KEY, b UNIQUE);
}
db close
forcedelete ota.db
sqlite3 ota ota.db
ota eval {
CREATE TABLE data_t1(a, b, ota_control);
CREATE TABLE data_t2(a, b, ota_control);
CREATE TABLE data_t3(a, b, ota_control);
INSERT INTO data_t1 VALUES(1, 't1', 0);
INSERT INTO data_t2 VALUES(2, 't2', 0);
INSERT INTO data_t3 VALUES(3, 't3', 0);
}
ota close
}
# Test the outcome of some other client writing the db while the *-oal
# file is being generated. Once this has happened, the update cannot be
# progressed.
#
for {set nStep 1} {$nStep < 7} {incr nStep} {
do_test 1.$nStep.1 {
setup_test
sqlite3ota ota test.db ota.db
for {set i 0} {$i<$nStep} {incr i} {ota step}
ota close
sqlite3 db test.db
execsql { INSERT INTO t1 VALUES(5, 'hello') }
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_BUSY}
do_test 1.$nStep.2 {
ota step
} {SQLITE_BUSY}
do_test 1.$nStep.3 {
list [file exists test.db-oal] [file exists test.db-wal]
} {1 0}
do_test 1.$nStep.4 {
list [catch { ota close } msg] $msg
} {1 {SQLITE_BUSY - database is locked}}
}
for {set nStep 7} {$nStep < 8} {incr nStep} {
do_test 1.$nStep.1 {
setup_test
sqlite3ota ota test.db ota.db
for {set i 0} {$i<$nStep} {incr i} {ota step}
ota close
sqlite3 db test.db
execsql { INSERT INTO t1 VALUES(5, 'hello') }
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_OK}
do_test 1.$nStep.2 {
ota step
} {SQLITE_OK}
do_test 1.$nStep.3 {
list [file exists test.db-oal] [file exists test.db-wal]
} {0 1}
do_test 1.$nStep.4 {
list [catch { ota close } msg] $msg
} {0 SQLITE_OK}
}
# Test the outcome of some other client writing the db after the *-oal
# file has been copied to the *-wal path. Once this has happened, any
# other client writing to the db causes OTA to consider its job finished.
#
for {set nStep 8} {$nStep < 20} {incr nStep} {
do_test 1.$nStep.1 {
setup_test
sqlite3ota ota test.db ota.db
for {set i 0} {$i<$nStep} {incr i} {ota step}
ota close
sqlite3 db test.db
execsql { INSERT INTO t1 VALUES(5, 'hello') }
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_DONE}
do_test 1.$nStep.2 {
ota step
} {SQLITE_DONE}
do_test 1.$nStep.3 {
file exists test.db-oal
} {0}
do_test 1.$nStep.4 {
list [catch { ota close } msg] $msg
} {0 SQLITE_DONE}
do_execsql_test 1.$nStep.5 {
SELECT * FROM t1;
} {1 t1 5 hello}
}
finish_test

View File

@ -1303,29 +1303,35 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){
}
assert( p->rc!=SQLITE_OK || p->eStage!=0 );
if( p->eStage==OTA_STAGE_OAL ){
if( p->rc==SQLITE_OK ){
if( p->rc==SQLITE_OK ){
if( p->eStage==OTA_STAGE_OAL ){
const char *zScript =
"PRAGMA journal_mode=off;"
"PRAGMA pager_ota_mode=1;"
"PRAGMA ota_mode=1;"
"BEGIN IMMEDIATE;"
;
;
p->rc = sqlite3_exec(p->db, zScript, 0, 0, &p->zErrmsg);
/* Point the object iterator at the first object */
if( p->rc==SQLITE_OK ){
p->rc = otaObjIterFirst(p, &p->objiter);
}
if( p->rc==SQLITE_OK ){
otaLoadTransactionState(p, pState);
}
}else if( p->eStage==OTA_STAGE_CKPT ){
p->rc = sqlite3_ckpt_open(
p->db, pState->pCkptState, pState->nCkptState, &p->pCkpt
);
if( p->rc==SQLITE_MISMATCH ){
p->eStage = OTA_STAGE_DONE;
p->rc = SQLITE_DONE;
}
}else if( p->eStage==OTA_STAGE_DONE ){
p->rc = SQLITE_DONE;
}
/* Point the object iterator at the first object */
if( p->rc==SQLITE_OK ){
p->rc = otaObjIterFirst(p, &p->objiter);
}
if( p->rc==SQLITE_OK ){
otaLoadTransactionState(p, pState);
}
}else if( p->rc==SQLITE_OK && p->eStage==OTA_STAGE_CKPT ){
p->rc = sqlite3_ckpt_open(
p->db, pState->pCkptState, pState->nCkptState, &p->pCkpt
);
}
otaFreeState(pState);