Merge the ota-update-no-pager_ota_mode branch into this one.

FossilOrigin-Name: 71887cd9b38def398d48eaf0ec34eeac3c7c5177
This commit is contained in:
dan 2015-02-11 17:05:17 +00:00
commit 01325e86e6
31 changed files with 1653 additions and 889 deletions

View File

@ -5,21 +5,8 @@ User documentation is in sqlite3ota.h.
SQLite Hacks
------------
1) PRAGMA ota_mode:
This is a new flag pragma. If the flag is set:
* INSERT/DELETE/UPDATE commands are prevented from updating any but the main
b-tree for each table (the PK index for WITHOUT ROWID tables or the
rowid b-tree for others).
* The above statements do not check UNIQUE constraints - except those enforced
by the main b-tree.
* All non-temporary triggers are disabled.
2) PRAGMA pager_ota_mode=1:
1) 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
@ -51,7 +38,7 @@ 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:
2) PRAGMA pager_ota_mode=2:
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
@ -60,14 +47,7 @@ SQLite Hacks
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()
3) sqlite3_ckpt_open/step/close()
API for performing (and resuming) incremental checkpoints.

View File

@ -98,7 +98,7 @@ proc step_ota {target ota} {
#
proc step_ota_uri {target ota} {
while 1 {
sqlite3ota ota file:$target?xyz=123 $ota
sqlite3ota ota file:$target?xyz=&abc=123 $ota
set rc [ota step]
ota close
if {$rc != "SQLITE_OK"} break
@ -106,361 +106,376 @@ proc step_ota_uri {target ota} {
set rc
}
foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_uri} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
}
3 {
CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
}
4 {
CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
5 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
6 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b, a);
}
7 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b, c);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(a, b, c, a, b, c);
}
8 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b, c);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(a, b, c, a, b, c);
}
9 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c));
CREATE INDEX i1 ON t1(b);
}
10 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b DESC);
}
11 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b DESC, a ASC, c DESC);
}
12 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID;
}
13 {
CREATE TABLE t1(a INT, b, c, PRIMARY KEY(a DESC)) WITHOUT ROWID;
}
14 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a DESC, c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
15 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c DESC)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
16 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(c DESC, a)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b DESC, c, a);
}
foreach {tn3 create_vfs destroy_vfs} {
1 {} {}
2 {
sqlite3ota_create_vfs -default myota ""
} {
reset_db
execsql $schema
do_test 1.$tn2.$tn.1 {
create_ota1 ota.db
$cmd test.db ota.db
} {SQLITE_DONE}
do_execsql_test 1.$tn2.$tn.2 { SELECT * FROM t1 ORDER BY a ASC } {
1 2 3
2 two three
3 {} 8.2
}
do_execsql_test 1.$tn2.$tn.3 { SELECT * FROM t1 ORDER BY b ASC } {
3 {} 8.2
1 2 3
2 two three
}
do_execsql_test 1.$tn2.$tn.4 { SELECT * FROM t1 ORDER BY c ASC } {
1 2 3
3 {} 8.2
2 two three
}
do_execsql_test 1.$tn2.$tn.5 { PRAGMA integrity_check } ok
sqlite3ota_destroy_vfs myota
}
}
#-------------------------------------------------------------------------
# Check that an OTA cannot be applied to a table that has no PK.
#
# UPDATE: At one point OTA required that all tables featured either
# explicit IPK columns or were declared WITHOUT ROWID. This has been
# relaxed so that external PRIMARY KEYs on tables with automatic rowids
# are now allowed.
#
# UPDATE 2: Tables without any PRIMARY KEY declaration are now allowed.
# However the input table must feature an "ota_rowid" column.
#
reset_db
create_ota1 ota.db
do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) }
do_test 2.2 {
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_ERROR}
do_test 2.3 {
list [catch { ota close } msg] $msg
} {1 {SQLITE_ERROR - table data_t1 requires ota_rowid column}}
reset_db
do_execsql_test 2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
do_test 2.5 {
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_OK}
do_test 2.6 {
list [catch { ota close } msg] $msg
} {0 SQLITE_OK}
#-------------------------------------------------------------------------
# Check that if a UNIQUE constraint is violated the current and all
# subsequent [ota step] calls return SQLITE_CONSTRAINT. And that the OTA
# transaction is rolled back by the [ota close] that deletes the ota
# handle.
#
foreach {tn errcode errmsg schema} {
1 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(3, 2, 1);
}
2 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE);
INSERT INTO t1 VALUES(4, 2, 'three');
}
3 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
CREATE TABLE t1(a PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(3, 2, 1);
}
4 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
INSERT INTO t1 VALUES(4, 2, 'three');
}
} {
eval $create_vfs
foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_uri} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
}
3 {
CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
}
4 {
CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
5 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
6 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b, a);
}
7 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b, c);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(a, b, c, a, b, c);
}
8 {
CREATE TABLE t1(a PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b, c);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(a, b, c, a, b, c);
}
9 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c));
CREATE INDEX i1 ON t1(b);
}
10 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b DESC);
}
11 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b DESC, a ASC, c DESC);
}
12 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID;
}
13 {
CREATE TABLE t1(a INT, b, c, PRIMARY KEY(a DESC)) WITHOUT ROWID;
}
14 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a DESC, c)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
15 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c DESC)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
}
16 {
CREATE TABLE t1(a, b, c, PRIMARY KEY(c DESC, a)) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b DESC, c, a);
}
} {
reset_db
execsql $schema
do_test $tn3.1.$tn2.$tn.1 {
create_ota1 ota.db
breakpoint
$cmd test.db ota.db
} {SQLITE_DONE}
do_execsql_test $tn3.1.$tn2.$tn.2 { SELECT * FROM t1 ORDER BY a ASC } {
1 2 3
2 two three
3 {} 8.2
}
do_execsql_test $tn3.1.$tn2.$tn.3 { SELECT * FROM t1 ORDER BY b ASC } {
3 {} 8.2
1 2 3
2 two three
}
do_execsql_test $tn3.1.$tn2.$tn.4 { SELECT * FROM t1 ORDER BY c ASC } {
1 2 3
3 {} 8.2
2 two three
}
do_execsql_test $tn3.1.$tn2.$tn.5 { PRAGMA integrity_check } ok
}
}
#-------------------------------------------------------------------------
# Check that an OTA cannot be applied to a table that has no PK.
#
# UPDATE: At one point OTA required that all tables featured either
# explicit IPK columns or were declared WITHOUT ROWID. This has been
# relaxed so that external PRIMARY KEYs on tables with automatic rowids
# are now allowed.
#
# UPDATE 2: Tables without any PRIMARY KEY declaration are now allowed.
# However the input table must feature an "ota_rowid" column.
#
reset_db
execsql $schema
set cksum [dbcksum db main]
do_test 3.$tn.1 {
create_ota1 ota.db
create_ota1 ota.db
do_execsql_test $tn3.2.1 { CREATE TABLE t1(a, b, c) }
do_test $tn3.2.2 {
sqlite3ota ota test.db ota.db
while {[set res [ota step]]=="SQLITE_OK"} {}
set res
} $errcode
do_test 3.$tn.2 { ota step } $errcode
do_test 3.$tn.3 {
ota step
} {SQLITE_ERROR}
do_test $tn3.2.3 {
list [catch { ota close } msg] $msg
} [list 1 "$errcode - $errmsg"]
} {1 {SQLITE_ERROR - table data_t1 requires ota_rowid column}}
reset_db
do_execsql_test $tn3.2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
do_test $tn3.2.5 {
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_OK}
do_test $tn3.2.6 {
list [catch { ota close } msg] $msg
} {0 SQLITE_OK}
do_test 3.$tn.4 { dbcksum db main } $cksum
}
#-------------------------------------------------------------------------
# Check that if a UNIQUE constraint is violated the current and all
# subsequent [ota step] calls return SQLITE_CONSTRAINT. And that the OTA
# transaction is rolled back by the [ota close] that deletes the ota
# handle.
#
foreach {tn errcode errmsg schema} {
1 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(3, 2, 1);
}
#-------------------------------------------------------------------------
#
foreach {tn2 cmd} {1 run_ota 2 step_ota} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
}
3 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
4 {
CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
5 {
CREATE TABLE t1(a INT PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
2 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE);
INSERT INTO t1 VALUES(4, 2, 'three');
}
3 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
CREATE TABLE t1(a PRIMARY KEY, b, c);
INSERT INTO t1 VALUES(3, 2, 1);
}
4 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
INSERT INTO t1 VALUES(4, 2, 'three');
}
6 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c);
CREATE INDEX i1 ON t1(b DESC);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c DESC, b, c);
}
7 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
} {
reset_db
execsql $schema
execsql {
INSERT INTO t1 VALUES(2, 'hello', 'world');
INSERT INTO t1 VALUES(4, 'hello', 'planet');
INSERT INTO t1 VALUES(6, 'hello', 'xyz');
}
do_test 4.$tn2.$tn.1 {
create_ota4 ota.db
$cmd test.db ota.db
} {SQLITE_DONE}
do_execsql_test 4.$tn2.$tn.2 {
SELECT * FROM t1 ORDER BY a ASC;
set cksum [dbcksum db main]
do_test $tn3.3.$tn.1 {
create_ota1 ota.db
sqlite3ota ota test.db ota.db
while {[set res [ota step]]=="SQLITE_OK"} {}
set res
} $errcode
do_test $tn3.3.$tn.2 { ota step } $errcode
do_test $tn3.3.$tn.3 {
list [catch { ota close } msg] $msg
} [list 1 "$errcode - $errmsg"]
do_test $tn3.3.$tn.4 { dbcksum db main } $cksum
}
#-------------------------------------------------------------------------
#
foreach {tn2 cmd} {1 run_ota 2 step_ota} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
}
3 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
4 {
CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
5 {
CREATE TABLE t1(a INT PRIMARY KEY, b, c);
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
6 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c);
CREATE INDEX i1 ON t1(b DESC);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c DESC, b, c);
}
7 {
CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID;
CREATE INDEX i1 ON t1(b);
CREATE INDEX i2 ON t1(c, b);
CREATE INDEX i3 ON t1(c, b, c);
}
} {
1 2 3
3 8 9
6 hello xyz
}
do_execsql_test 4.$tn2.$tn.3 { PRAGMA integrity_check } ok
}
}
#-------------------------------------------------------------------------
#
foreach {tn2 cmd} {1 run_ota 2 step_ota} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
3 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
4 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d);
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
} {
reset_db
execsql $schema
execsql {
INSERT INTO t1 VALUES(1, 2, 3, 4);
INSERT INTO t1 VALUES(2, 5, 6, 7);
INSERT INTO t1 VALUES(3, 8, 9, 10);
}
do_test 5.$tn2.$tn.1 {
create_ota5 ota.db
$cmd test.db ota.db
} {SQLITE_DONE}
reset_db
execsql $schema
execsql {
INSERT INTO t1 VALUES(2, 'hello', 'world');
INSERT INTO t1 VALUES(4, 'hello', 'planet');
INSERT INTO t1 VALUES(6, 'hello', 'xyz');
}
do_execsql_test 5.$tn2.$tn.2 {
SELECT * FROM t1 ORDER BY a ASC;
} {
1 2 3 5
2 5 10 5
3 11 9 10
do_test $tn3.4.$tn2.$tn.1 {
create_ota4 ota.db
$cmd test.db ota.db
} {SQLITE_DONE}
do_execsql_test $tn3.4.$tn2.$tn.2 {
SELECT * FROM t1 ORDER BY a ASC;
} {
1 2 3
3 8 9
6 hello xyz
}
do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok
}
do_execsql_test 5.$tn2.$tn.3 { PRAGMA integrity_check } ok
}
}
#-------------------------------------------------------------------------
# Test some error cases:
#
# * A virtual table with no ota_rowid column.
# * A no-PK table with no ota_rowid column.
# * A PK table with an ota_rowid column.
#
ifcapable fts3 {
foreach {tn schema error} {
1 {
CREATE TABLE t1(a, b);
CREATE TABLE ota.data_t1(a, b, ota_control);
} {SQLITE_ERROR - table data_t1 requires ota_rowid column}
2 {
CREATE VIRTUAL TABLE t1 USING fts4(a, b);
CREATE TABLE ota.data_t1(a, b, ota_control);
} {SQLITE_ERROR - table data_t1 requires ota_rowid column}
3 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
4 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
5 {
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
} {
reset_db
forcedelete ota.db
execsql { ATTACH 'ota.db' AS ota }
execsql $schema
do_test 6.$tn {
list [catch { run_ota test.db ota.db } msg] $msg
} [list 1 $error]
#-------------------------------------------------------------------------
#
foreach {tn2 cmd} {1 run_ota 2 step_ota} {
foreach {tn schema} {
1 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
}
2 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
3 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
4 {
CREATE TABLE t1(a PRIMARY KEY, b, c, d);
CREATE INDEX i1 ON t1(d);
CREATE INDEX i2 ON t1(d, c);
CREATE INDEX i3 ON t1(d, c, b);
CREATE INDEX i4 ON t1(b);
CREATE INDEX i5 ON t1(c);
CREATE INDEX i6 ON t1(c, b);
}
} {
reset_db
execsql $schema
execsql {
INSERT INTO t1 VALUES(1, 2, 3, 4);
INSERT INTO t1 VALUES(2, 5, 6, 7);
INSERT INTO t1 VALUES(3, 8, 9, 10);
}
do_test $tn3.5.$tn2.$tn.1 {
create_ota5 ota.db
$cmd test.db ota.db
} {SQLITE_DONE}
do_execsql_test $tn3.5.$tn2.$tn.2 {
SELECT * FROM t1 ORDER BY a ASC;
} {
1 2 3 5
2 5 10 5
3 11 9 10
}
do_execsql_test $tn3.5.$tn2.$tn.3 { PRAGMA integrity_check } ok
}
}
#-------------------------------------------------------------------------
# Test some error cases:
#
# * A virtual table with no ota_rowid column.
# * A no-PK table with no ota_rowid column.
# * A PK table with an ota_rowid column.
#
ifcapable fts3 {
foreach {tn schema error} {
1 {
CREATE TABLE t1(a, b);
CREATE TABLE ota.data_t1(a, b, ota_control);
} {SQLITE_ERROR - table data_t1 requires ota_rowid column}
2 {
CREATE VIRTUAL TABLE t1 USING fts4(a, b);
CREATE TABLE ota.data_t1(a, b, ota_control);
} {SQLITE_ERROR - table data_t1 requires ota_rowid column}
3 {
CREATE TABLE t1(a PRIMARY KEY, b);
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
4 {
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
5 {
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
CREATE TABLE ota.data_t1(a, b, ota_rowid, ota_control);
} {SQLITE_ERROR - table data_t1 may not have ota_rowid column}
} {
reset_db
forcedelete ota.db
execsql { ATTACH 'ota.db' AS ota }
execsql $schema
do_test $tn3.6.$tn {
list [catch { run_ota test.db ota.db } msg] $msg
} [list 1 $error]
}
}
eval $destroy_vfs
}

View File

@ -1,74 +0,0 @@
# 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.
#
#***********************************************************************
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
set ::testprefix ota2
forcedelete {*}[glob -nocomplain test.db?*]
do_execsql_test 1.0 {
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
} {}
do_test 1.1 { glob test.db* } {test.db}
do_execsql_test 1.2 {
PRAGMA pager_ota_mode = 1;
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 6);
SELECT * FROM t1;
} {1 2 3 4 5 6}
do_test 1.3 { lsort [glob test.db*] } {test.db test.db-oal}
do_test 1.4 {
sqlite3 db2 test.db
db2 eval { SELECT * FROM t1 }
} {1 2}
do_test 1.5 {
catchsql { INSERT INTO t1 VALUES(7, 8) } db2
} {1 {database is locked}}
db2 close
db close
sqlite3 db test.db
do_execsql_test 1.6 {
PRAGMA pager_ota_mode = 1;
SELECT * FROM t1;
} {1 2 3 4 5 6}
do_execsql_test 1.7 {
INSERT INTO t1 VALUES(7,8);
SELECT * FROM t1;
} {1 2 3 4 5 6 7 8}
db close
sqlite3 db2 test.db
do_test 1.8 {
execsql { BEGIN; SELECT * FROM t1 } db2
} {1 2}
do_test 1.9 {
file rename test.db-oal test.db-wal
execsql { SELECT * FROM t1 } db2
} {1 2}
do_test 1.10 {
execsql { COMMIT; SELECT * FROM t1 } db2
} {1 2 3 4 5 6 7 8}
finish_test

View File

@ -1,128 +0,0 @@
# 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 and ota_mode pragmas.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
set ::testprefix ota4
#-------------------------------------------------------------------------
# The following tests aim to verify some properties of the pager_ota_mode
# pragma:
#
# 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.1 {
PRAGMA journal_mode = wal;
SELECT * FROM sqlite_master;
} {wal}
do_catchsql_test 1.1.2 {
PRAGMA pager_ota_mode = 1
} {1 {cannot set pager_ota_mode in wal mode}}
do_execsql_test 1.2.1 {
PRAGMA journal_mode = delete;
BEGIN;
SELECT * FROM sqlite_master;
} {delete}
do_catchsql_test 1.2.2 {
PRAGMA pager_ota_mode = 1
} {1 {cannot set pager_ota_mode with open transaction}}
do_execsql_test 1.2.3 {
COMMIT;
} {}
do_execsql_test 1.3.1 {
PRAGMA journal_mode = wal;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
} {wal}
do_test 1.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 1.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 1.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 1.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 1.5.4 {
PRAGMA pager_ota_mode = 1;
SELECT * FROM t1;
} {1 {database is locked}}
finish_test

View File

@ -50,7 +50,7 @@ for {set nStep 1} {$nStep < 7} {incr nStep} {
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') }
@ -65,7 +65,7 @@ for {set nStep 1} {$nStep < 7} {incr nStep} {
} {1 0}
do_test 1.$nStep.4 {
list [catch { ota close } msg] $msg
} {1 {SQLITE_BUSY - database is locked}}
} {1 {SQLITE_BUSY - database modified during ota update}}
}
for {set nStep 7} {$nStep < 8} {incr nStep} {
@ -122,7 +122,5 @@ for {set nStep 8} {$nStep < 20} {incr nStep} {
}
finish_test

82
ext/ota/otaA.test Normal file
View File

@ -0,0 +1,82 @@
# 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.
#
#***********************************************************************
#
# This file contains tests for the OTA module. More specifically, it
# contains tests to ensure that it is an error to attempt to update
# a wal mode database via OTA.
#
if {![info exists testdir]} {
set testdir [file join [file dirname [info script]] .. .. test]
}
source $testdir/tester.tcl
set ::testprefix otaA
set db_sql {
CREATE TABLE t1(a PRIMARY KEY, b, c);
}
set ota_sql {
CREATE TABLE data_t1(a, b, c, ota_control);
INSERT INTO data_t1 VALUES(1, 2, 3, 0);
INSERT INTO data_t1 VALUES(4, 5, 6, 0);
INSERT INTO data_t1 VALUES(7, 8, 9, 0);
}
do_test 1.0 {
forcedelete test.db ota.db
sqlite3 db test.db
db eval $db_sql
db eval { PRAGMA journal_mode = wal }
db close
sqlite3 db ota.db
db eval $ota_sql
db close
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_ERROR}
do_test 1.1 {
list [catch { ota close } msg] $msg
} {1 {SQLITE_ERROR - cannot update wal mode database}}
do_test 2.0 {
forcedelete test.db ota.db
sqlite3 db test.db
db eval $db_sql
db close
sqlite3 db ota.db
db eval $ota_sql
db close
sqlite3ota ota test.db ota.db
ota step
ota close
} {SQLITE_OK}
do_test 2.1 {
sqlite3 db test.db
db eval {PRAGMA journal_mode = wal}
db close
sqlite3ota ota test.db ota.db
ota step
} {SQLITE_ERROR}
do_test 2.2 {
list [catch { ota close } msg] $msg
} {1 {SQLITE_ERROR - cannot update wal mode database}}
finish_test

View File

@ -62,6 +62,9 @@
** Valid if STAGE==3. The blob to pass to sqlite3ckpt_start() to resume
** the incremental checkpoint.
**
** OTA_STATE_COOKIE:
** Valid if STAGE==1. The current change-counter cookie value in the
** target db file.
*/
#define OTA_STATE_STAGE 1
#define OTA_STATE_TBL 2
@ -69,9 +72,9 @@
#define OTA_STATE_ROW 4
#define OTA_STATE_PROGRESS 5
#define OTA_STATE_CKPT 6
#define OTA_STATE_COOKIE 7
#define OTA_STAGE_OAL 1
#define OTA_STAGE_COPY 2
#define OTA_STAGE_CKPT 3
#define OTA_STAGE_DONE 4
@ -81,6 +84,8 @@
typedef struct OtaState OtaState;
typedef struct OtaObjIter OtaObjIter;
typedef struct ota_vfs ota_vfs;
typedef struct ota_file ota_file;
/*
** A structure to store values read from the ota_state table in memory.
@ -152,6 +157,7 @@ struct OtaObjIter {
#define OTA_PK_WITHOUT_ROWID 4
#define OTA_PK_VTAB 5
/*
** OTA handle.
*/
@ -166,8 +172,37 @@ struct sqlite3ota {
int nProgress; /* Rows processed for all objects */
OtaObjIter objiter; /* Iterator for skipping through tbl/idx */
sqlite3_ckpt *pCkpt; /* Incr-checkpoint handle */
ota_file *pTargetFd; /* File handle open on target db */
const char *zVfsName; /* Name of automatically created ota vfs */
};
struct ota_vfs {
sqlite3_vfs base; /* ota VFS shim methods */
sqlite3_vfs *pRealVfs; /* Underlying VFS */
sqlite3_mutex *mutex;
const char *zOtaWal;
};
struct ota_file {
sqlite3_file base; /* sqlite3_file methods */
sqlite3_file *pReal; /* Underlying file handle */
ota_vfs *pOtaVfs; /* Pointer to the ota_vfs object */
sqlite3ota *pOta; /* Pointer to ota object (ota target only) */
int openFlags; /* Flags this file was opened with */
unsigned int iCookie; /* Cookie value for main db files */
unsigned char iWriteVer; /* "write-version" value for main db files */
int nShm; /* Number of entries in apShm[] array */
char **apShm; /* Array of mmap'd *-shm regions */
const char *zWal; /* Wal filename for this db file */
char *zDel; /* Delete this when closing file */
};
static void otaCreateVfs(sqlite3ota*, const char*);
static void otaDeleteVfs(sqlite3ota*);
/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return
@ -413,6 +448,20 @@ static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
return p->rc;
}
static void *otaMalloc(sqlite3ota *p, int nByte){
void *pRet = 0;
if( p->rc==SQLITE_OK ){
pRet = sqlite3_malloc(nByte);
if( pRet==0 ){
p->rc = SQLITE_NOMEM;
}else{
memset(pRet, 0, nByte);
}
}
return pRet;
}
/*
** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
** there is room for at least nCol elements. If an OOM occurs, store an
@ -422,17 +471,13 @@ static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
int nByte = (2*sizeof(char*) + sizeof(int) + 2*sizeof(unsigned char)) * nCol;
char **azNew;
assert( p->rc==SQLITE_OK );
azNew = (char**)sqlite3_malloc(nByte);
azNew = (char**)otaMalloc(p, nByte);
if( azNew ){
memset(azNew, 0, nByte);
pIter->azTblCol = azNew;
pIter->azTblType = &azNew[nCol];
pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
pIter->abTblPk = (unsigned char*)&pIter->aiSrcOrder[nCol];
pIter->abNotNull = (unsigned char*)&pIter->abTblPk[nCol];
}else{
p->rc = SQLITE_NOMEM;
}
}
@ -969,17 +1014,14 @@ static char *otaObjIterGetSetlist(
static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
char *zRet = 0;
if( p->rc==SQLITE_OK ){
int nByte = nBind*2 + 1;
zRet = sqlite3_malloc(nByte);
if( zRet==0 ){
p->rc = SQLITE_NOMEM;
}else{
int i;
for(i=0; i<nBind; i++){
zRet[i*2] = '?';
zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
}
int nByte = nBind*2 + 1;
zRet = (char*)otaMalloc(p, nByte);
if( zRet ){
int i;
for(i=0; i<nBind; i++){
zRet[i*2] = '?';
zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
}
}
return zRet;
@ -1434,15 +1476,27 @@ static int otaGetUpdateStmt(
** error occurs, leave an error code and message in the OTA handle.
*/
static void otaOpenDatabase(sqlite3ota *p){
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
assert( p->rc==SQLITE_OK );
sqlite3_close(p->db);
p->db = 0;
assert( p->db==0 );
p->rc = sqlite3_open(p->zTarget, &p->db);
p->rc = sqlite3_open_v2(p->zTarget, &p->db, flags, p->zVfsName);
if( p->rc ){
p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
}else{
otaMPrintfExec(p, "ATTACH %Q AS ota", p->zOta);
/* Mark the database file just opened as an OTA target database. If
** this call returns SQLITE_NOTFOUND, then the OTA vfs is not in use.
** This is an error. */
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_OTA, (void*)p);
if( p->rc==SQLITE_NOTFOUND ){
p->rc = SQLITE_ERROR;
p->zErrmsg = sqlite3_mprintf("ota vfs not found");
}
}
}
otaMPrintfExec(p, "ATTACH %Q AS ota", p->zOta);
}
/*
@ -1506,8 +1560,10 @@ static void otaMoveOalFile(sqlite3ota *p){
/* Re-open the databases. */
otaObjIterFinalize(&p->objiter);
otaOpenDatabase(p);
sqlite3_close(p->db);
p->db = 0;
p->eStage = OTA_STAGE_CKPT;
otaOpenDatabase(p);
}
sqlite3_free(zWal);
@ -1795,13 +1851,15 @@ static void otaSaveTransactionState(sqlite3ota *p){
"(%d, %Q), "
"(%d, %d), "
"(%d, %lld), "
"(%d, ?) ",
"(%d, ?), "
"(%d, %lld) ",
OTA_STATE_STAGE, p->eStage,
OTA_STATE_TBL, p->objiter.zTbl,
OTA_STATE_IDX, p->objiter.zIdx,
OTA_STATE_ROW, p->nStep,
OTA_STATE_PROGRESS, p->nProgress,
OTA_STATE_CKPT
OTA_STATE_CKPT,
OTA_STATE_COOKIE, (sqlite3_int64)p->pTargetFd->iCookie
)
);
assert( pInsert==0 || rc==SQLITE_OK );
@ -1848,7 +1906,7 @@ static void otaFreeState(OtaState *p){
static OtaState *otaLoadState(sqlite3ota *p){
const char *zSelect = "SELECT k, v FROM ota.ota_state";
OtaState *pRet = 0;
sqlite3_stmt *pStmt;
sqlite3_stmt *pStmt = 0;
int rc;
int rc2;
@ -1866,7 +1924,6 @@ static OtaState *otaLoadState(sqlite3ota *p){
case OTA_STATE_STAGE:
pRet->eStage = sqlite3_column_int(pStmt, 1);
if( pRet->eStage!=OTA_STAGE_OAL
&& pRet->eStage!=OTA_STAGE_COPY
&& pRet->eStage!=OTA_STAGE_CKPT
){
p->rc = SQLITE_CORRUPT;
@ -1896,6 +1953,19 @@ static OtaState *otaLoadState(sqlite3ota *p){
);
break;
case OTA_STATE_COOKIE:
/* At this point (p->iCookie) contains the value of the change-counter
** cookie (the thing that gets incremented when a transaction is
** committed in rollback mode) currently stored on page 1 of the
** database file. */
if( pRet->eStage==OTA_STAGE_OAL
&& p->pTargetFd->iCookie!=(unsigned int)sqlite3_column_int64(pStmt, 1)
){
rc = SQLITE_BUSY;
p->zErrmsg = sqlite3_mprintf("database modified during ota update");
}
break;
default:
rc = SQLITE_CORRUPT;
break;
@ -1965,19 +2035,29 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){
if( p ){
OtaState *pState = 0;
/* Open the target database */
/* Create the custom VFS */
memset(p, 0, sizeof(sqlite3ota));
p->zTarget = (char*)&p[1];
memcpy(p->zTarget, zTarget, nTarget+1);
p->zOta = &p->zTarget[nTarget+1];
memcpy(p->zOta, zOta, nOta+1);
otaOpenDatabase(p);
otaCreateVfs(p, 0);
/* Open the target database */
if( p->rc==SQLITE_OK ){
p->zTarget = (char*)&p[1];
memcpy(p->zTarget, zTarget, nTarget+1);
p->zOta = &p->zTarget[nTarget+1];
memcpy(p->zOta, zOta, nOta+1);
otaOpenDatabase(p);
}
/* If it has not already been created, create the ota_state table */
if( p->rc==SQLITE_OK ){
p->rc = sqlite3_exec(p->db, OTA_CREATE_STATE, 0, 0, &p->zErrmsg);
}
if( p->rc==SQLITE_OK && p->pTargetFd->iWriteVer>1 ){
p->rc = SQLITE_ERROR;
p->zErrmsg = sqlite3_mprintf("cannot update wal mode database");
}
if( p->rc==SQLITE_OK ){
pState = otaLoadState(p);
assert( pState || p->rc!=SQLITE_OK );
@ -1995,12 +2075,14 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){
if( p->rc==SQLITE_OK ){
if( p->eStage==OTA_STAGE_OAL ){
const char *zScript =
"PRAGMA journal_mode=off;"
"PRAGMA pager_ota_mode=1;"
"BEGIN IMMEDIATE;"
;
p->rc = sqlite3_exec(p->db, zScript, 0, 0, &p->zErrmsg);
ota_vfs *pOtaVfs = p->pTargetFd->pOtaVfs;
sqlite3_mutex_enter(pOtaVfs->mutex);
assert( pOtaVfs->zOtaWal==0 );
pOtaVfs->zOtaWal = p->pTargetFd->zWal;
p->rc = sqlite3_exec(p->db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
pOtaVfs->zOtaWal = 0;
sqlite3_mutex_leave(pOtaVfs->mutex);
/* Point the object iterator at the first object */
if( p->rc==SQLITE_OK ){
@ -2081,13 +2163,10 @@ int sqlite3ota_close(sqlite3ota *p, char **pzErrmsg){
p->rc = sqlite3_exec(p->db, "COMMIT", 0, 0, &p->zErrmsg);
}
if( p->rc==SQLITE_OK && p->eStage==OTA_STAGE_CKPT ){
p->rc = sqlite3_exec(p->db, "PRAGMA pager_ota_mode=2", 0, 0, &p->zErrmsg);
}
/* Close the open database handle */
/* Close the open database handle and VFS object. */
if( p->pCkpt ) sqlite3_ckpt_close(p->pCkpt, 0, 0);
sqlite3_close(p->db);
otaDeleteVfs(p);
otaEditErrmsg(p);
rc = p->rc;
@ -2109,6 +2188,624 @@ sqlite3_int64 sqlite3ota_progress(sqlite3ota *pOta){
return pOta->nProgress;
}
/**************************************************************************
** Beginning of OTA VFS shim methods. The VFS shim modifies the behaviour
** of a standard VFS in the following ways:
**
** 1. Whenever the first page of a main database file is read or
** written, the value of the change-counter cookie is stored in
** ota_file.iCookie. Similarly, the value of the "write-version"
** database header field is stored in ota_file.iWriteVer. This ensures
** that the values are always trustworthy within an open transaction.
**
** 2. When the ota handle is in OTA_STAGE_OAL or OTA_STAGE_CKPT state, all
** EXCLUSIVE lock attempts on the target database fail. This prevents
** sqlite3_close() from running an automatic checkpoint. Until the
** ota handle reaches OTA_STAGE_DONE - at that point the automatic
** checkpoint may be required to delete the *-wal file.
**
** 3. In OTA_STAGE_OAL, the *-shm file is stored in memory. All xShmLock()
** calls are noops. This is just an optimization.
**
** 4. In OTA_STAGE_OAL mode, when SQLite calls xAccess() to check if a
** *-wal file associated with the target database exists, the following
** special handling applies:
**
** a) if the *-wal file does exist, return SQLITE_CANTOPEN. An OTA
** target database may not be in wal mode already.
**
** b) if the *-wal file does not exist, set the output parameter to
** non-zero (to tell SQLite that it does exist) anyway.
**
** 5. In OTA_STAGE_OAL mode, if SQLite tries to open a *-wal file
** associated with a target database, open the corresponding *-oal file
** instead.
*/
/*
** Close an ota file.
*/
static int otaVfsClose(sqlite3_file *pFile){
ota_file *p = (ota_file*)pFile;
int rc;
int i;
/* Free the contents of the apShm[] array. And the array itself. */
for(i=0; i<p->nShm; i++){
sqlite3_free(p->apShm[i]);
}
sqlite3_free(p->apShm);
p->apShm = 0;
sqlite3_free(p->zDel);
/* Close the underlying file handle */
rc = p->pReal->pMethods->xClose(p->pReal);
return rc;
}
/*
** Read and return an unsigned 32-bit big-endian integer from the buffer
** passed as the only argument.
*/
static unsigned int otaGetU32(unsigned char *aBuf){
return ((unsigned int)aBuf[0] << 24)
+ ((unsigned int)aBuf[1] << 16)
+ ((unsigned int)aBuf[2] << 8)
+ ((unsigned int)aBuf[3]);
}
/*
** Read data from an otaVfs-file.
*/
static int otaVfsRead(
sqlite3_file *pFile,
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
ota_file *p = (ota_file*)pFile;
int rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
/* These look like magic numbers. But they are stable, as they are part
** of the definition of the SQLite file format, which may not change. */
unsigned char *pBuf = (unsigned char*)zBuf;
p->iCookie = otaGetU32(&pBuf[24]);
p->iWriteVer = pBuf[19];
}
return rc;
}
/*
** Write data to an otaVfs-file.
*/
static int otaVfsWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
ota_file *p = (ota_file*)pFile;
int rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
/* These look like magic numbers. But they are stable, as they are part
** of the definition of the SQLite file format, which may not change. */
unsigned char *pBuf = (unsigned char*)zBuf;
p->iCookie = otaGetU32(&pBuf[24]);
p->iWriteVer = pBuf[19];
}
return rc;
}
/*
** Truncate an otaVfs-file.
*/
static int otaVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
ota_file *p = (ota_file*)pFile;
return p->pReal->pMethods->xTruncate(p->pReal, size);
}
/*
** Sync an otaVfs-file.
*/
static int otaVfsSync(sqlite3_file *pFile, int flags){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xSync(p->pReal, flags);
}
/*
** Return the current file-size of an otaVfs-file.
*/
static int otaVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xFileSize(p->pReal, pSize);
}
/*
** Lock an otaVfs-file.
*/
static int otaVfsLock(sqlite3_file *pFile, int eLock){
ota_file *p = (ota_file*)pFile;
sqlite3ota *pOta = p->pOta;
int rc = SQLITE_OK;
if( pOta && eLock==SQLITE_LOCK_EXCLUSIVE
&& (pOta->eStage==OTA_STAGE_OAL || pOta->eStage==OTA_STAGE_CKPT)
){
/* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
** prevents it from checkpointing the database from sqlite3_close(). */
rc = SQLITE_BUSY;
}else{
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
}
return rc;
}
/*
** Unlock an otaVfs-file.
*/
static int otaVfsUnlock(sqlite3_file *pFile, int eLock){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xUnlock(p->pReal, eLock);
}
/*
** Check if another file-handle holds a RESERVED lock on an otaVfs-file.
*/
static int otaVfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
}
/*
** File control method. For custom operations on an otaVfs-file.
*/
static int otaVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
ota_file *p = (ota_file *)pFile;
int (*xControl)(sqlite3_file*,int,void*) = p->pReal->pMethods->xFileControl;
if( op==SQLITE_FCNTL_OTA ){
int rc;
sqlite3ota *pOta = (sqlite3ota*)pArg;
/* First try to find another OTA vfs lower down in the vfs stack. If
** one is found, this vfs will operate in pass-through mode. The lower
** level vfs will do the special OTA handling. */
rc = xControl(p->pReal, op, pArg);
if( rc==SQLITE_NOTFOUND ){
/* Now search for a zipvfs instance lower down in the VFS stack. If
** one is found, this is an error. */
void *dummy = 0;
rc = xControl(p->pReal, SQLITE_FCNTL_ZIPVFS_PAGER, &dummy);
if( rc==SQLITE_OK ){
rc = SQLITE_ERROR;
pOta->zErrmsg = sqlite3_mprintf("ota/zipvfs setup error");
}else if( rc==SQLITE_NOTFOUND ){
pOta->pTargetFd = p;
p->pOta = pOta;
rc = SQLITE_OK;
}
}
return rc;
}
return xControl(p->pReal, op, pArg);
}
/*
** Return the sector-size in bytes for an otaVfs-file.
*/
static int otaVfsSectorSize(sqlite3_file *pFile){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xSectorSize(p->pReal);
}
/*
** Return the device characteristic flags supported by an otaVfs-file.
*/
static int otaVfsDeviceCharacteristics(sqlite3_file *pFile){
ota_file *p = (ota_file *)pFile;
return p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
}
/*
** Shared-memory methods are all pass-thrus.
*/
static int otaVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
ota_file *p = (ota_file*)pFile;
int rc = SQLITE_OK;
#ifdef SQLITE_AMALGAMATION
assert( WAL_CKPT_LOCK==1 );
#endif
if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
/* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
** taking this lock also prevents any checkpoints from occurring.
** todo: really, it's not clear why this might occur, as
** wal_autocheckpoint ought to be turned off. */
if( ofst==1 && n==1 ) rc = SQLITE_BUSY;
}else{
assert( p->nShm==0 );
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
}
return rc;
}
static int otaVfsShmMap(
sqlite3_file *pFile,
int iRegion,
int szRegion,
int isWrite,
void volatile **pp
){
ota_file *p = (ota_file*)pFile;
int rc = SQLITE_OK;
/* If not in OTA_STAGE_OAL, allow this call to pass through. Or, if this
** ota is in the OTA_STAGE_OAL state, use heap memory for *-shm space
** instead of a file on disk. */
if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
if( iRegion<=p->nShm ){
int nByte = (iRegion+1) * sizeof(char*);
char **apNew = (char**)sqlite3_realloc(p->apShm, nByte);
if( apNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
p->apShm = apNew;
p->nShm = iRegion+1;
}
}
if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
char *pNew = (char*)sqlite3_malloc(szRegion);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, szRegion);
p->apShm[iRegion] = pNew;
}
}
if( rc==SQLITE_OK ){
*pp = p->apShm[iRegion];
}else{
*pp = 0;
}
}else{
assert( p->apShm==0 );
rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
}
return rc;
}
/*
** Memory barrier.
*/
static void otaVfsShmBarrier(sqlite3_file *pFile){
ota_file *p = (ota_file *)pFile;
p->pReal->pMethods->xShmBarrier(p->pReal);
}
static int otaVfsShmUnmap(sqlite3_file *pFile, int delFlag){
ota_file *p = (ota_file*)pFile;
int rc = SQLITE_OK;
if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
/* no-op */
}else{
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
}
return rc;
}
/*
** Open an ota file handle.
*/
static int otaVfsOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
int flags,
int *pOutFlags
){
static sqlite3_io_methods otavfs_io_methods = {
2, /* iVersion */
otaVfsClose, /* xClose */
otaVfsRead, /* xRead */
otaVfsWrite, /* xWrite */
otaVfsTruncate, /* xTruncate */
otaVfsSync, /* xSync */
otaVfsFileSize, /* xFileSize */
otaVfsLock, /* xLock */
otaVfsUnlock, /* xUnlock */
otaVfsCheckReservedLock, /* xCheckReservedLock */
otaVfsFileControl, /* xFileControl */
otaVfsSectorSize, /* xSectorSize */
otaVfsDeviceCharacteristics, /* xDeviceCharacteristics */
otaVfsShmMap, /* xShmMap */
otaVfsShmLock, /* xShmLock */
otaVfsShmBarrier, /* xShmBarrier */
otaVfsShmUnmap /* xShmUnmap */
};
ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;
ota_file *pFd = (ota_file *)pFile;
int rc = SQLITE_OK;
const char *zOpen = zName;
memset(pFd, 0, sizeof(ota_file));
pFd->pReal = (sqlite3_file*)&pFd[1];
pFd->pOtaVfs = pOtaVfs;
pFd->openFlags = flags;
if( zName ){
if( flags & SQLITE_OPEN_MAIN_DB ){
/* A main database has just been opened. The following block sets
** (pFd->zWal) to point to a buffer owned by SQLite that contains
** the name of the *-wal file this db connection will use. SQLite
** happens to pass a pointer to this buffer when using xAccess()
** or xOpen() to operate on the *-wal file. */
int n = strlen(zName);
const char *z = &zName[n];
if( flags & SQLITE_OPEN_URI ){
int odd = 0;
while( 1 ){
if( z[0]==0 ){
odd = 1 - odd;
if( odd && z[1]==0 ) break;
}
z++;
}
z += 2;
}else{
while( *z==0 ) z++;
}
z += (n + 8 + 1);
pFd->zWal = z;
}
else if( (flags & SQLITE_OPEN_WAL) && zName==pOtaVfs->zOtaWal ){
char *zCopy = otaStrndup(zName, -1, &rc);
if( zCopy ){
int nCopy = strlen(zCopy);
zCopy[nCopy-3] = 'o';
zOpen = (const char*)(pFd->zDel = zCopy);
}
}
}
if( rc==SQLITE_OK ){
rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, flags, pOutFlags);
}
if( pFd->pReal->pMethods ){
pFile->pMethods = &otavfs_io_methods;
}
return rc;
}
/*
** Delete the file located at zPath.
*/
static int otaVfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xDelete(pRealVfs, zPath, dirSync);
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int otaVfsAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;
int rc;
rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);
if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS && pOtaVfs->zOtaWal==zPath ){
if( *pResOut ){
rc = SQLITE_CANTOPEN;
}else{
*pResOut = 1;
}
}
return rc;
}
/*
** Populate buffer zOut with the full canonical pathname corresponding
** to the pathname in zPath. zOut is guaranteed to point to a buffer
** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
*/
static int otaVfsFullPathname(
sqlite3_vfs *pVfs,
const char *zPath,
int nOut,
char *zOut
){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xFullPathname(pRealVfs, zPath, nOut, zOut);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *otaVfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xDlOpen(pRealVfs, zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void otaVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
pRealVfs->xDlError(pRealVfs, nByte, zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
*/
static void (*otaVfsDlSym(
sqlite3_vfs *pVfs,
void *pArg,
const char *zSym
))(void){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xDlSym(pRealVfs, pArg, zSym);
}
/*
** Close the dynamic library handle pHandle.
*/
static void otaVfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xDlClose(pRealVfs, pHandle);
}
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int otaVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xRandomness(pRealVfs, nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int otaVfsSleep(sqlite3_vfs *pVfs, int nMicro){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xSleep(pRealVfs, nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int otaVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
sqlite3_vfs *pRealVfs = ((ota_vfs*)pVfs)->pRealVfs;
return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
}
static int otaVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
return 0;
}
void sqlite3ota_destroy_vfs(const char *zName){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
if( pVfs ){
sqlite3_vfs_unregister(pVfs);
sqlite3_free(pVfs);
}
}
int sqlite3ota_create_vfs(const char *zName, const char *zParent){
/* Template for VFS */
static sqlite3_vfs vfs_template = {
1, /* iVersion */
0, /* szOsFile */
0, /* mxPathname */
0, /* pNext */
0, /* zName */
0, /* pAppData */
otaVfsOpen, /* xOpen */
otaVfsDelete, /* xDelete */
otaVfsAccess, /* xAccess */
otaVfsFullPathname, /* xFullPathname */
otaVfsDlOpen, /* xDlOpen */
otaVfsDlError, /* xDlError */
otaVfsDlSym, /* xDlSym */
otaVfsDlClose, /* xDlClose */
otaVfsRandomness, /* xRandomness */
otaVfsSleep, /* xSleep */
otaVfsCurrentTime, /* xCurrentTime */
otaVfsGetLastError, /* xGetLastError */
0, /* xCurrentTimeInt64 (version 2) */
0, 0, 0 /* Unimplemented version 3 methods */
};
sqlite3_vfs *pParent; /* Parent VFS */
ota_vfs *pNew = 0; /* Newly allocated VFS */
int nName;
int rc = SQLITE_OK;
nName = strlen(zName);
pParent = sqlite3_vfs_find(zParent);
if( pParent==0 ){
rc = SQLITE_NOTFOUND;
}else{
int nByte = sizeof(ota_vfs) + nName + 1;
pNew = (ota_vfs*)sqlite3_malloc(nByte);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, nByte);
}
}
if( rc==SQLITE_OK ){
char *zSpace;
memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
pNew->base.mxPathname = pParent->mxPathname;
pNew->base.szOsFile = sizeof(ota_file) + pParent->szOsFile;
pNew->pRealVfs = pParent;
pNew->base.zName = (const char*)(zSpace = (char*)&pNew[1]);
memcpy(zSpace, zName, nName);
/* Register the new VFS (not as the default) */
rc = sqlite3_vfs_register(&pNew->base, 0);
if( rc ){
sqlite3_free(pNew);
}
}
return rc;
}
static void otaCreateVfs(sqlite3ota *p, const char *zParent){
int rnd;
char zRnd[64];
assert( p->rc==SQLITE_OK );
sqlite3_randomness(sizeof(int), (void*)&rnd);
sprintf(zRnd, "ota_vfs_%d", rnd);
p->rc = sqlite3ota_create_vfs(zRnd, zParent);
if( p->rc==SQLITE_NOTFOUND ){
p->zErrmsg = sqlite3_mprintf("no such vfs: %s", zParent);
}else if( p->rc==SQLITE_OK ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
assert( pVfs );
p->zVfsName = pVfs->zName;
}
}
static void otaDeleteVfs(sqlite3ota *p){
if( p->zVfsName ){
sqlite3ota_destroy_vfs(p->zVfsName);
p->zVfsName = 0;
}
}
/**************************************************************************/
@ -2235,9 +2932,76 @@ static int test_sqlite3ota(
return TCL_OK;
}
/*
** Tclcmd: sqlite3ota_create_vfs ?-default? NAME PARENT
*/
static int test_sqlite3ota_create_vfs(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zName;
const char *zParent;
int rc;
if( objc!=3 && objc!=4 ){
Tcl_WrongNumArgs(interp, 1, objv, "?-default? NAME PARENT");
return TCL_ERROR;
}
zName = Tcl_GetString(objv[objc-2]);
zParent = Tcl_GetString(objv[objc-1]);
if( zParent[0]=='\0' ) zParent = 0;
rc = sqlite3ota_create_vfs(zName, zParent);
if( rc!=SQLITE_OK ){
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
return TCL_ERROR;
}else if( objc==4 ){
sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
sqlite3_vfs_register(pVfs, 1);
}
Tcl_ResetResult(interp);
return TCL_OK;
}
/*
** Tclcmd: sqlite3ota_destroy_vfs NAME
*/
static int test_sqlite3ota_destroy_vfs(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
const char *zName;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "NAME");
return TCL_ERROR;
}
zName = Tcl_GetString(objv[1]);
sqlite3ota_destroy_vfs(zName);
return TCL_OK;
}
int SqliteOta_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "sqlite3ota", test_sqlite3ota, 0, 0);
static struct {
char *zName;
Tcl_ObjCmdProc *xProc;
} aObjCmd[] = {
{ "sqlite3ota", test_sqlite3ota },
{ "sqlite3ota_create_vfs", test_sqlite3ota_create_vfs },
{ "sqlite3ota_destroy_vfs", test_sqlite3ota_destroy_vfs },
};
int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);
}
return TCL_OK;
}
#endif /* ifdef SQLITE_TEST */

View File

@ -237,6 +237,15 @@ typedef struct sqlite3ota sqlite3ota;
** Argument zTarget is the path to the target database. Argument zOta is
** the path to the OTA database. Each call to this function must be matched
** by a call to sqlite3ota_close().
**
** By default, OTA uses the default VFS to access the files on disk. To
** use a VFS other than the default, an SQLite "file:" URI containing a
** "vfs=..." option may be passed as the zTarget option.
**
** IMPORTANT NOTE FOR ZIPVFS USERS: The OTA extension works with all of
** SQLite's built-in VFSs, including the multiplexor VFS. However it does
** not work out of the box with zipvfs. Refer to the comment describing
** the zipvfs_create_vfs() API below for details on using OTA with zipvfs.
*/
sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta);
@ -276,8 +285,8 @@ int sqlite3ota_step(sqlite3ota *pOta);
/*
** Close an OTA handle.
**
** If the OTA update has been completely applied, commit it to the target
** database. Otherwise, assuming no error has occurred, save the current
** If the OTA update has been completely applied, commit it to the target
** database. Otherwise, assuming no error has occurred, save the current
** state of the OTA update appliation to the OTA database.
**
** If an error has already occurred as part of an sqlite3ota_step()
@ -300,5 +309,50 @@ int sqlite3ota_close(sqlite3ota *pOta, char **pzErrmsg);
*/
sqlite3_int64 sqlite3ota_progress(sqlite3ota *pOta);
/*
** Part of the OTA implementation uses a custom VFS object. Usually, this
** object is created and deleted automatically by OTA.
**
** The exception is for applications that also use zipvfs. In this case,
** the custom VFS must be explicitly created by the user before the OTA
** handle is opened. The OTA VFS should be installed so that the zipvfs
** VFS uses the OTA VFS, which in turn uses any other VFS layers in use
** (for example multiplexor) to access the file-system. For example,
** to assemble an OTA enabled VFS stack that uses both zipvfs and
** multiplexor (error checking omitted):
**
** // Create a VFS named "multiplexor" (not the default).
** sqlite3_multiplex_initialize(zVfsName, 0);
**
** // Create an ota VFS named "ota" that uses multiplexor.
** sqlite3ota_create_vfs("ota", "multiplexor");
**
** // Create a zipvfs VFS named "zipvfs" that uses ota.
** zipvfs_create_vfs_v3("zipvfs", "ota", 0, xCompressorAlgorithmDetector);
**
** // Make zipvfs the default VFS.
** sqlite3_vfs_register(sqlite3_vfs_find("zipvfs"), 1);
**
** Because the default VFS created above includes a OTA functionality, it
** may be used by OTA clients. Attempting to use OTA with a zipvfs VFS stack
** that does not include the OTA layer results in an error.
**
** The overhead of adding the "ota" VFS to the system is negligible for
** non-OTA users. There is no harm in an application accessing the
** file-system via "ota" all the time, even if it only uses OTA functionality
** occasionally.
*/
int sqlite3ota_create_vfs(const char *zName, const char *zParent);
/*
** Deregister and destroy an OTA vfs created by an earlier call to
** sqlite3ota_create_vfs().
**
** VFS objects are not reference counted. If a VFS object is destroyed
** before all database handles that use it have been closed, the results
** are undefined.
*/
void sqlite3ota_destroy_vfs(const char *zName);
#endif /* _SQLITE3OTA_H */

View File

@ -219,6 +219,9 @@ SRC += \
SRC += \
$(TOP)/ext/userauth/userauth.c \
$(TOP)/ext/userauth/sqlite3userauth.h
SRC += \
$(TOP)/ext/ota/sqlite3ota.c \
$(TOP)/ext/ota/sqlite3ota.h
# Generated source code files
#

View File

@ -1,5 +1,5 @@
C Merge\sthe\scommand-line\sshell\senhancements\sfrom\strunk.
D 2015-02-06T15:03:45.342
C Merge\sthe\sota-update-no-pager_ota_mode\sbranch\sinto\sthis\sone.
D 2015-02-11T17:05:17.871
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 6b9e7677829aa94b9f30949656e27312aefb9a46
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -123,21 +123,20 @@ F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
F ext/ota/README.txt 78d4a9f78f567d4bf826cf0f02df6254902562ca
F ext/ota/README.txt 2ce4ffbb0aaa6731b041c27a7359f9a5f1c69152
F ext/ota/ota.c c11a85af71dccc45976622fe7a51169a481caa91
F ext/ota/ota1.test 719854e444dff2ead58ff6b62d8315954bd7762a
F ext/ota/ota1.test d50ba4ded2edeba99740bc7dd0b7284c1894127c
F ext/ota/ota10.test 85e0f6e7964db5007590c1b299e75211ed4240d4
F ext/ota/ota2.test 2829bc08ffbb71b605392a68fedfd554763356a7
F ext/ota/ota3.test a77efbce7723332eb688d2b28bf18204fc9614d7
F ext/ota/ota4.test 82434aa39c9acca6cd6317f6b0ab07b0ec6c2e7d
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
F ext/ota/ota6.test 1fbba5fd46e3e0bfa5ae1d0caf9da27d15cb7cdf
F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
F ext/ota/otaA.test 95566a8d193113867b960eadf85b310937f2fe03
F ext/ota/otafault.test 508ba87c83d632670ac0f94371a465d4bb4d49dd
F ext/ota/sqlite3ota.c bf417242a191617841cc1ab0815071b49444c9c8
F ext/ota/sqlite3ota.h b4c54c7df5d223f2ee40efa5ba363188daa3ad37
F ext/ota/sqlite3ota.c 466546d41d9b09136216349db98647d0afd77816
F ext/ota/sqlite3ota.h 1cc7201086fe65a36957740381485a24738c4077
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 14e6239434d4e3f65d3e90320713f26aa24e167f
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
@ -167,7 +166,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 1de9f345052b7cf631e3323b42bd35064cdfcf0a
F main.mk 57c115aba023c1988564edb80ac87c3e07472b05
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk c6b3fa301db6ef7ac916b14c60868aeaec1337b5
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -196,8 +195,8 @@ F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c 198a0066ba60ab06fc00fba1998d870a4d575463
F src/ctime.c 98f89724adc891a1a4c655bee04e33e716e05887
F src/date.c e4d50b3283696836ec1036b695ead9a19e37a5ac
F src/delete.c bd1a91ddd247ce13004075251e0b7fe2bf9925ef
F src/expr.c abe930897ccafae3819fd2855cbc1b00c262fd12
F src/delete.c 37964e6c1d73ff49cbea9ff690c9605fb15f600e
F src/expr.c 3ef111b88ae2941b84b6b6ea4be8d501ba1af0cb
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e0444b61bed271a76840cbe6182df93a9baa3f12
F src/func.c 6d3c4ebd72aa7923ce9b110a7dc15f9b8c548430
@ -232,30 +231,30 @@ F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
F src/os_unix.c aefeaf915aaef9f81aa2645e0d5d06fa1bd83beb
F src/os_win.c 8223e7db5b7c4a81d8b161098ac3959400434cdb
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 90b164ac8fefed940cd50fad6938cd18b55af8f3
F src/pager.h 19d83e2782fe978976cb1acf474d09d9a6124ac3
F src/parse.y c5d0d964f9ac023e8154cad512e54b0b6058e086
F src/pager.c 9d29fb3dfd99d16896d839a511b467784d72f4da
F src/pager.h 20954a3fa1bbf05d39063d94e789ad9efd15e5d1
F src/parse.y 0f8e7d60f0ab3cb53d270adef69259ac307d83a8
F src/pcache.c d210cf90d04365a74f85d21374dded65af67b0cb
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
F src/pcache1.c 1e77432b40b7d3288327d9cdf399dcdfd2b6d3bf
F src/pragma.c 8042d2b202140c49ffccb267aaa2012b50e337e4
F src/pragma.h d2f776d719d156544638fe3f87f9627d8e16222f
F src/pragma.c ea0be138a99784b14e87bd4522fea40e7b979e9c
F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
F src/prepare.c 173a5a499138451b2561614ecb87d78f9f4644b9
F src/printf.c 05edc41450d0eb2c05ef7db113bf32742ae65325
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c f6c46d3434439ab2084618d603e6d6dbeb0d6ada
F src/resolve.c f4d79e31ffa5820c2e3d1740baa5e9b190425f2b
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 1f2087523007c42900ffcbdeaef06a23ad9329fc
F src/select.c e46cef4c224549b439384c88fc7f57ba064dad54
F src/shell.c 82c25508dac802b32198af6f5256ca1597c6a1af
F src/sqlite.h.in 4807b024e8d257af774cde0cf178f721ff2406ec
F src/sqlite.h.in c49acd2daa6e54110ab0cc607eb73ff32720a269
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 57f8f45028598cc2877fc08ac03b402242242c68
F src/sqliteInt.h 57a405ae6d2ed10fff52de376d18f21e04d96609
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 81712116e826b0089bb221b018929536b2b5406f
F src/table.c e7a09215315a978057fb42c640f890160dbcc45e
F src/tclsqlite.c b321464aba1fff1ed9317ebc82a1a94887f97af8
F src/test1.c ce8ea168800d129acb2c0afdf2831ddf8667e082
F src/test1.c 90fbedce75330d48d99eadb7d5f4223e86969585
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
F src/test4.c d168f83cc78d02e8d35567bb5630e40dcd85ac1e
@ -313,12 +312,12 @@ F src/vdbe.h 6fc69d9c5e146302c56e163cb4b31d1ee64a18c3
F src/vdbeInt.h 9bb69ff2447c34b6ccc58b34ec35b615f86ead78
F src/vdbeapi.c 4bc511a46b9839392ae0e90844a71dc96d9dbd71
F src/vdbeaux.c 97911edb61074b871ec4aa2d6bb779071643dee5
F src/vdbeblob.c 4af4bfb71f6df7778397b4a0ebc1879793276778
F src/vdbeblob.c 4f2e8e075d238392df98c5e03a64342465b03f90
F src/vdbemem.c 31d8eabb0cd78bfeab4e5124c7363c3e9e54db9f
F src/vdbesort.c 6d64c5448b64851b99931ede980addc3af70d5e2
F src/vdbetrace.c 7e4222955e07dd707a2f360c0eb73452be1cb010
F src/vtab.c c08ec66f45919eaa726bf88aa53eb08379d607f9
F src/wal.c 735d081f736fd7fecbf8f2aa213484e641ba35ff
F src/wal.c 7a8a4e7a40d693d44dbfc4d1f2bcb7e2b620f530
F src/wal.h 0d3ba0c3f1b4c25796cb213568a84b9f9063f465
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c d46de821bc604a4fd36fa3928c086950e91aafb1
@ -418,7 +417,7 @@ F test/collate4.test f04d5168685f2eef637ecfa2d4ddf8ec0d600177
F test/collate5.test 65d928034d30d2d263a80f6359f7549ee1598ec6
F test/collate6.test 8be65a182abaac8011a622131486dafb8076e907
F test/collate7.test 8ec29d98f3ee4ccebce6e16ce3863fb6b8c7b868
F test/collate8.test df26649cfcbddf109c04122b340301616d3a88f6
F test/collate8.test cd9b3d3f999b8520ffaa7cc1647061fc5bab1334
F test/collate9.test 3adcc799229545940df2f25308dd1ad65869145a
F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6
F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1
@ -653,7 +652,7 @@ F test/in3.test 3cbf58c87f4052cee3a58b37b6389777505aa0c0
F test/in4.test d2b38cba404bc4320f4fe1b595b3d163f212c068
F test/in5.test 1de657472fa9ac2924be25c2c959ac5ca1aae554
F test/incrblob.test e81846d214f3637622620fbde7cd526781cfe328
F test/incrblob2.test bf4d549aa4a466d7fbe3e3a3693d3861263d5600
F test/incrblob2.test 0d8821730a84f90af78a9dd547fe7a2480a06240
F test/incrblob3.test d8d036fde015d4a159cd3cbae9d29003b37227a4
F test/incrblob4.test f26502a5697893e5acea268c910f16478c2f0fab
F test/incrblob_err.test af1f12ba60d220c9752073ff2bda2ad59e88960d
@ -850,7 +849,7 @@ F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
F test/select4.test 8c5a60d439e2df824aed56223566877a883c5c84
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
F test/select6.test e76bd10a56988f15726c097a5d5a7966fe82d3b2
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
F test/select8.test 391de11bdd52339c30580dabbbbe97e3e9a3c79d
F test/select9.test aebc2bb0c3bc44606125033cbcaac2c8d1f33a95
@ -1219,10 +1218,10 @@ F tool/logest.c eef612f8adf4d0993dafed0416064cf50d5d33c6
F tool/mkautoconfamal.sh d1a2da0e15b2ed33d60af35c7e9d483f13a8eb9f
F tool/mkkeywordhash.c dfff09dbbfaf950e89af294f48f902181b144670
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl a5cb9b20ad7abb2ffd519c85f1f8f99bcbfa6823
F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 9ef48e1748dce7b844f67e2450ff9dfeb0fb4ab5
F tool/mksqlite3c.tcl 6b8e572a90eb4e0086e3ba90d88b76c085919863
F tool/mksqlite3c.tcl d8b0b0cc5f0e912058c9300f052769c62404d2d9
F tool/mksqlite3h.tcl ba24038056f51fde07c0079c41885ab85e2cff12
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl 52a4c613707ac34ae9c226e5ccc69cb948556105
@ -1238,7 +1237,7 @@ F tool/showstat4.c 9515faa8ec176599d4a8288293ba8ec61f7b728a
F tool/showwal.c 85cb36d4fe3e93e2fbd63e786e0d1ce42d0c4fad
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
F tool/space_used.tcl f714c41a59e326b8b9042f415b628b561bafa06b
F tool/spaceanal.tcl 8e50b217c56a6a086a1b47eac9d09c5cd65b996f
F tool/spaceanal.tcl d5a09620c66a6c144576cb9d2bdfa9a6fbe362a5
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
@ -1255,7 +1254,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 7f10a0eaf1fedfa020cbd7019ec9342ffdc3b9b0 0f65a7e2e09f801b66897479d501607caeae4abf
R 1ef390b8e8775fda79ba88277a4a2044
U drh
Z f716caa3559c7f56ae8a7035846f96fb
P c3931db560ab4a2601c7f7318fb02c8d5e6862b1 0b63e8dcbaec5043e353734e684c2a46552a3409
R f20bdc7a7c21af7b4cb60f763acf418a
U dan
Z 0a34d9bc8960589f0668f7402b31b0ce

View File

@ -1 +1 @@
c3931db560ab4a2601c7f7318fb02c8d5e6862b1
71887cd9b38def398d48eaf0ec34eeac3c7c5177

View File

@ -189,7 +189,7 @@ Expr *sqlite3LimitWhere(
pInClause->x.pSelect = pSelect;
pInClause->flags |= EP_xIsSelect;
sqlite3ExprSetHeight(pParse, pInClause);
sqlite3ExprSetHeightAndFlags(pParse, pInClause);
return pInClause;
/* something went wrong. clean up anything allocated. */

View File

@ -146,10 +146,25 @@ CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
break;
}
if( p->flags & EP_Collate ){
if( ALWAYS(p->pLeft) && (p->pLeft->flags & EP_Collate)!=0 ){
if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){
p = p->pLeft;
}else{
p = p->pRight;
Expr *pNext = p->pRight;
/* The Expr.x union is never used at the same time as Expr.pRight */
assert( p->x.pList==0 || p->pRight==0 );
/* p->flags holds EP_Collate and p->pLeft->flags does not. And
** p->x.pSelect cannot. So if p->x.pLeft exists, it must hold at
** least one EP_Collate. Thus the following two ALWAYS. */
if( p->x.pList!=0 && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) ){
int i;
for(i=0; ALWAYS(i<p->x.pList->nExpr); i++){
if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
pNext = p->x.pList->a[i].pExpr;
break;
}
}
}
p = pNext;
}
}else{
break;
@ -355,6 +370,9 @@ static void heightOfSelect(Select *p, int *pnHeight){
** Expr.pSelect member has a height of 1. Any other expression
** has a height equal to the maximum height of any other
** referenced Expr plus one.
**
** Also propagate EP_Propagate flags up from Expr.x.pList to Expr.flags,
** if appropriate.
*/
static void exprSetHeight(Expr *p){
int nHeight = 0;
@ -362,8 +380,9 @@ static void exprSetHeight(Expr *p){
heightOfExpr(p->pRight, &nHeight);
if( ExprHasProperty(p, EP_xIsSelect) ){
heightOfSelect(p->x.pSelect, &nHeight);
}else{
}else if( p->x.pList ){
heightOfExprList(p->x.pList, &nHeight);
p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
}
p->nHeight = nHeight + 1;
}
@ -372,8 +391,11 @@ static void exprSetHeight(Expr *p){
** Set the Expr.nHeight variable using the exprSetHeight() function. If
** the height is greater than the maximum allowed expression depth,
** leave an error in pParse.
**
** Also propagate all EP_Propagate flags from the Expr.x.pList into
** Expr.flags.
*/
void sqlite3ExprSetHeight(Parse *pParse, Expr *p){
void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
exprSetHeight(p);
sqlite3ExprCheckHeight(pParse, p->nHeight);
}
@ -387,8 +409,17 @@ int sqlite3SelectExprHeight(Select *p){
heightOfSelect(p, &nHeight);
return nHeight;
}
#else
#define exprSetHeight(y)
#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */
/*
** Propagate all EP_Propagate flags from the Expr.x.pList into
** Expr.flags.
*/
void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){
p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
}
}
#define exprSetHeight(y)
#endif /* SQLITE_MAX_EXPR_DEPTH>0 */
/*
@ -490,11 +521,11 @@ void sqlite3ExprAttachSubtrees(
}else{
if( pRight ){
pRoot->pRight = pRight;
pRoot->flags |= EP_Collate & pRight->flags;
pRoot->flags |= EP_Propagate & pRight->flags;
}
if( pLeft ){
pRoot->pLeft = pLeft;
pRoot->flags |= EP_Collate & pLeft->flags;
pRoot->flags |= EP_Propagate & pLeft->flags;
}
exprSetHeight(pRoot);
}
@ -594,7 +625,7 @@ Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *pToken){
}
pNew->x.pList = pList;
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
sqlite3ExprSetHeight(pParse, pNew);
sqlite3ExprSetHeightAndFlags(pParse, pNew);
return pNew;
}
@ -1209,6 +1240,21 @@ void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
sqlite3DbFree(db, pList);
}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
** ExprList.
*/
u32 sqlite3ExprListFlags(const ExprList *pList){
int i;
u32 m = 0;
if( pList ){
for(i=0; i<pList->nExpr; i++){
m |= pList->a[i].pExpr->flags;
}
}
return m;
}
/*
** These routines are Walker callbacks used to check expressions to
** see if they are "constant" for some definition of constant. The
@ -1249,7 +1295,7 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
** and either pWalker->eCode==4 or 5 or the function has the
** SQLITE_FUNC_CONST flag. */
case TK_FUNCTION:
if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_Constant) ){
if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc) ){
return WRC_Continue;
}else{
pWalker->eCode = 0;

View File

@ -615,18 +615,6 @@ 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.
**
** It is also possible to use PagerSetOtaMode() to 2 if the database is
** already in WAL mode. In this case the only effect is to prevent the
** connection from checkpointing the db as part of an sqlite3PagerClose()
** call.
*/
struct Pager {
sqlite3_vfs *pVfs; /* OS functions to use for IO */
@ -642,9 +630,6 @@ 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 */
#ifdef SQLITE_ENABLE_OTA
u8 otaMode; /* Non-zero if in ota_mode */
#endif
/**************************************************************************
** The following block contains those class members that change during
@ -720,16 +705,6 @@ struct Pager {
#endif
};
/*
** Return the value of the pager otaMode flag (0, 1 or 2). Or, if
** SQLITE_ENABLE_OTA is not defined, return constant value 0.
*/
#ifdef SQLITE_ENABLE_OTA
# define PagerOtaMode(pPager) ((pPager)->otaMode)
#else
# define PagerOtaMode(pPager) 0
#endif
/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
@ -851,8 +826,6 @@ static int pagerUseWal(Pager *pPager){
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
static int pagerOpenWalInternal(Pager*, int*);
#ifndef NDEBUG
/*
** Usage:
@ -2052,7 +2025,7 @@ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
}
if( !pPager->exclusiveMode && !PagerOtaMode(pPager)
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
@ -4007,9 +3980,7 @@ int sqlite3PagerClose(Pager *pPager){
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
sqlite3WalClose(pPager->pWal,
pPager->ckptSyncFlags, pPager->pageSize, (PagerOtaMode(pPager)?0:pTmp)
);
sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags, pPager->pageSize, pTmp);
pPager->pWal = 0;
#endif
pager_reset(pPager);
@ -5210,11 +5181,6 @@ int sqlite3PagerSharedLock(Pager *pPager){
** mode. Otherwise, the following function call is a no-op.
*/
rc = pagerOpenWalIfPresent(pPager);
if( rc==SQLITE_OK && PagerOtaMode(pPager) ){
int nWal = sqlite3Strlen30(pPager->zWal);
pPager->zWal[nWal-3] = 'o';
rc = pagerOpenWalInternal(pPager, 0);
}
#ifndef SQLITE_OMIT_WAL
assert( pPager->pWal==0 || rc==SQLITE_OK );
@ -5224,17 +5190,6 @@ int sqlite3PagerSharedLock(Pager *pPager){
if( pagerUseWal(pPager) ){
assert( rc==SQLITE_OK );
rc = pagerBeginReadTransaction(pPager);
if( rc==SQLITE_OK && PagerOtaMode(pPager)==1 ){
rc = sqlite3WalCheckSalt(pPager->pWal, pPager->fd);
if( rc!=SQLITE_OK ){
sqlite3WalClose(pPager->pWal, 0, 0, 0);
pPager->pWal = 0;
}else{
#ifdef SQLITE_ENABLE_OTA
pPager->otaMode = 2;
#endif
}
}
}
if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
@ -7129,7 +7084,7 @@ void sqlite3PagerClearCache(Pager *pPager){
*/
int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK;
if( pPager->pWal && PagerOtaMode(pPager)==0 ){
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
pPager->pBusyHandlerArg,
@ -7197,7 +7152,7 @@ static int pagerOpenWal(Pager *pPager){
*/
if( rc==SQLITE_OK ){
rc = sqlite3WalOpen(pPager->pVfs,
pPager->fd, pPager->zWal, pPager->exclusiveMode || PagerOtaMode(pPager),
pPager->fd, pPager->zWal, pPager->exclusiveMode,
pPager->journalSizeLimit, &pPager->pWal
);
}
@ -7206,15 +7161,23 @@ static int pagerOpenWal(Pager *pPager){
return rc;
}
/*
** Open the WAL file if it is not open. If it is already open, set *pbOpen
** to 1 before returning. Return SQLITE_OK if successful, or an SQLite error
** code otherwise.
** The caller must be holding a SHARED lock on the database file to call
** this function.
**
** The difference between this function and sqlite3PagerOpenWal() is that
** PagerOpenWal() does not open the WAL file if the pager is in OTA mode.
** If the pager passed as the first argument is open on a real database
** file (not a temp file or an in-memory database), and the WAL file
** is not already open, make an attempt to open it now. If successful,
** return SQLITE_OK. If an error occurs or the VFS used by the pager does
** not support the xShmXXX() methods, return an error code. *pbOpen is
** not modified in either case.
**
** If the pager is open on a temp-file (or in-memory database), or if
** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK
** without doing anything.
*/
static int pagerOpenWalInternal(
int sqlite3PagerOpenWal(
Pager *pPager, /* Pager object */
int *pbOpen /* OUT: Set to true if call is a no-op */
){
@ -7244,29 +7207,6 @@ static int pagerOpenWalInternal(
return rc;
}
/*
** The caller must be holding a SHARED lock on the database file to call
** this function.
**
** If the pager passed as the first argument is open on a real database
** file (not a temp file or an in-memory database), and the WAL file
** is not already open, make an attempt to open it now. If successful,
** return SQLITE_OK. If an error occurs or the VFS used by the pager does
** not support the xShmXXX() methods, return an error code. *pbOpen is
** not modified in either case.
**
** If the pager is open on a temp-file (or in-memory database), or if
** the WAL file is already open, set *pbOpen to 1 and return SQLITE_OK
** without doing anything.
*/
int sqlite3PagerOpenWal(
Pager *pPager, /* Pager object */
int *pbOpen /* OUT: Set to true if call is a no-op */
){
if( PagerOtaMode(pPager) ) return SQLITE_CANTOPEN_BKPT;
return pagerOpenWalInternal(pPager, pbOpen);
}
/*
** This function is called to close the connection to the log file prior
** to switching from WAL to rollback mode.
@ -7313,20 +7253,6 @@ 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( PagerOtaMode(pPager) ){
memcpy(aSalt, pPager->dbFileVers, 8);
}else{
sqlite3_randomness(8, aSalt);
}
}
#endif /* !SQLITE_OMIT_WAL */
#ifdef SQLITE_ENABLE_ZIPVFS
@ -7344,17 +7270,6 @@ int sqlite3PagerWalFramesize(Pager *pPager){
#endif
#ifdef SQLITE_ENABLE_OTA
/*
** Set or clear the "OTA mode" flag.
*/
int sqlite3PagerSetOtaMode(Pager *pPager, int iOta){
assert( iOta==1 || iOta==2 );
if( iOta==1 && (pPager->pWal || pPager->eState!=PAGER_OPEN) ){
return SQLITE_ERROR;
}
pPager->otaMode = iOta;
return SQLITE_OK;
}
/*
** Open an incremental checkpoint handle.

View File

@ -210,8 +210,6 @@ void *sqlite3PagerCodec(DbPage *);
# define enable_simulated_io_errors()
#endif
int sqlite3PagerSetOtaMode(Pager *pPager, int bOta);
void sqlite3PagerWalSalt(Pager *pPager, u32 *aSalt);
int sqlite3PagerWalCheckpointStart(sqlite3*, Pager*, u8*, int, sqlite3_ckpt**);
#endif /* _PAGER_H_ */

View File

@ -1078,7 +1078,7 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0);
if( A.pExpr ){
A.pExpr->x.pList = Y;
sqlite3ExprSetHeight(pParse, A.pExpr);
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
}else{
sqlite3ExprListDelete(pParse->db, Y);
}
@ -1091,8 +1091,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
A.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
if( A.pExpr ){
A.pExpr->x.pSelect = X;
ExprSetProperty(A.pExpr, EP_xIsSelect);
sqlite3ExprSetHeight(pParse, A.pExpr);
ExprSetProperty(A.pExpr, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
}else{
sqlite3SelectDelete(pParse->db, X);
}
@ -1103,8 +1103,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0);
if( A.pExpr ){
A.pExpr->x.pSelect = Y;
ExprSetProperty(A.pExpr, EP_xIsSelect);
sqlite3ExprSetHeight(pParse, A.pExpr);
ExprSetProperty(A.pExpr, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
}else{
sqlite3SelectDelete(pParse->db, Y);
}
@ -1117,8 +1117,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
A.pExpr = sqlite3PExpr(pParse, TK_IN, X.pExpr, 0, 0);
if( A.pExpr ){
A.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
ExprSetProperty(A.pExpr, EP_xIsSelect);
sqlite3ExprSetHeight(pParse, A.pExpr);
ExprSetProperty(A.pExpr, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
}else{
sqlite3SrcListDelete(pParse->db, pSrc);
}
@ -1130,8 +1130,8 @@ expr(A) ::= expr(W) between_op(N) expr(X) AND expr(Y). [BETWEEN] {
Expr *p = A.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
if( p ){
p->x.pSelect = Y;
ExprSetProperty(p, EP_xIsSelect);
sqlite3ExprSetHeight(pParse, p);
ExprSetProperty(p, EP_xIsSelect|EP_Subquery);
sqlite3ExprSetHeightAndFlags(pParse, p);
}else{
sqlite3SelectDelete(pParse->db, Y);
}
@ -1145,7 +1145,7 @@ expr(A) ::= CASE(C) case_operand(X) case_exprlist(Y) case_else(Z) END(E). {
A.pExpr = sqlite3PExpr(pParse, TK_CASE, X, 0, 0);
if( A.pExpr ){
A.pExpr->x.pList = Z ? sqlite3ExprListAppend(pParse,Y,Z) : Y;
sqlite3ExprSetHeight(pParse, A.pExpr);
sqlite3ExprSetHeightAndFlags(pParse, A.pExpr);
}else{
sqlite3ExprListDelete(pParse->db, Y);
sqlite3ExprDelete(pParse->db, Z);

View File

@ -422,50 +422,6 @@ void sqlite3Pragma(
}
#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */
/*
** 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.
*/
#ifdef SQLITE_ENABLE_OTA
case PragTyp_PAGER_OTA_MODE: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
if( zRight ){
int iArg = sqlite3Atoi(zRight);
Pager *pPager = sqlite3BtreePager(pBt);
if( sqlite3BtreeIsInReadTrans(pBt) ){
sqlite3ErrorMsg(pParse,
"cannot set pager_ota_mode with open transaction"
);
}else if( sqlite3PagerWalSupported(pPager)==0 ){
sqlite3ErrorMsg(pParse,
"cannot set pager_ota_mode without wal support"
);
}else if( sqlite3PagerSetOtaMode(sqlite3BtreePager(pBt), iArg) ){
sqlite3ErrorMsg(pParse, "cannot set pager_ota_mode in wal mode");
}
}
break;
}
#endif /* SQLITE_ENABLE_OTA */
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
/*
** PRAGMA [database.]page_size

View File

@ -28,24 +28,23 @@
#define PragTyp_PAGE_COUNT 22
#define PragTyp_MMAP_SIZE 23
#define PragTyp_PAGE_SIZE 24
#define PragTyp_PAGER_OTA_MODE 25
#define PragTyp_SECURE_DELETE 26
#define PragTyp_SHRINK_MEMORY 27
#define PragTyp_SOFT_HEAP_LIMIT 28
#define PragTyp_STATS 29
#define PragTyp_SYNCHRONOUS 30
#define PragTyp_TABLE_INFO 31
#define PragTyp_TEMP_STORE 32
#define PragTyp_TEMP_STORE_DIRECTORY 33
#define PragTyp_THREADS 34
#define PragTyp_WAL_AUTOCHECKPOINT 35
#define PragTyp_WAL_CHECKPOINT 36
#define PragTyp_ACTIVATE_EXTENSIONS 37
#define PragTyp_HEXKEY 38
#define PragTyp_KEY 39
#define PragTyp_REKEY 40
#define PragTyp_LOCK_STATUS 41
#define PragTyp_PARSER_TRACE 42
#define PragTyp_SECURE_DELETE 25
#define PragTyp_SHRINK_MEMORY 26
#define PragTyp_SOFT_HEAP_LIMIT 27
#define PragTyp_STATS 28
#define PragTyp_SYNCHRONOUS 29
#define PragTyp_TABLE_INFO 30
#define PragTyp_TEMP_STORE 31
#define PragTyp_TEMP_STORE_DIRECTORY 32
#define PragTyp_THREADS 33
#define PragTyp_WAL_AUTOCHECKPOINT 34
#define PragTyp_WAL_CHECKPOINT 35
#define PragTyp_ACTIVATE_EXTENSIONS 36
#define PragTyp_HEXKEY 37
#define PragTyp_KEY 38
#define PragTyp_REKEY 39
#define PragTyp_LOCK_STATUS 40
#define PragTyp_PARSER_TRACE 41
#define PragFlag_NeedSchema 0x01
#define PragFlag_ReadOnly 0x02
static const struct sPragmaNames {
@ -304,12 +303,6 @@ static const struct sPragmaNames {
/* ePragFlag: */ 0,
/* iArg: */ 0 },
#endif
#if defined(SQLITE_ENABLE_OTA)
{ /* zName: */ "pager_ota_mode",
/* ePragTyp: */ PragTyp_PAGER_OTA_MODE,
/* ePragFlag: */ 0,
/* iArg: */ 0 },
#endif
#if defined(SQLITE_DEBUG)
{ /* zName: */ "parser_trace",
/* ePragTyp: */ PragTyp_PARSER_TRACE,
@ -463,4 +456,4 @@ static const struct sPragmaNames {
/* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
#endif
};
/* Number of pragmas: 59 on by default, 73 total. */
/* Number of pragmas: 59 on by default, 72 total. */

View File

@ -247,9 +247,10 @@ static int lookupName(
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IsCheck );
if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){
/* Silently ignore database qualifiers inside CHECK constraints and partial
** indices. Do not raise errors because that might break legacy and
** because it does not hurt anything to just ignore the database name. */
/* Silently ignore database qualifiers inside CHECK constraints and
** partial indices. Do not raise errors because that might break
** legacy and because it does not hurt anything to just ignore the
** database name. */
zDb = 0;
}else{
for(i=0; i<db->nDb; i++){
@ -320,7 +321,8 @@ static int lookupName(
if( pMatch ){
pExpr->iTable = pMatch->iCursor;
pExpr->pTab = pMatch->pTab;
assert( (pMatch->jointype & JT_RIGHT)==0 ); /* RIGHT JOIN not (yet) supported */
/* RIGHT JOIN not (yet) supported */
assert( (pMatch->jointype & JT_RIGHT)==0 );
if( (pMatch->jointype & JT_LEFT)!=0 ){
ExprSetProperty(pExpr, EP_CanBeNull);
}
@ -641,7 +643,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->affinity = SQLITE_AFF_INTEGER;
break;
}
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
&& !defined(SQLITE_OMIT_SUBQUERY) */
/* A lone identifier is the name of a column.
*/
@ -706,19 +709,20 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( n==2 ){
pExpr->iTable = exprProbability(pList->a[1].pExpr);
if( pExpr->iTable<0 ){
sqlite3ErrorMsg(pParse, "second argument to likelihood() must be a "
"constant between 0.0 and 1.0");
sqlite3ErrorMsg(pParse,
"second argument to likelihood() must be a "
"constant between 0.0 and 1.0");
pNC->nErr++;
}
}else{
/* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is equivalent to
** likelihood(X, 0.0625).
** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is short-hand for
** likelihood(X,0.0625).
** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand for
** likelihood(X,0.9375).
** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent to
** likelihood(X,0.9375). */
/* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is
** equivalent to likelihood(X, 0.0625).
** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is
** short-hand for likelihood(X,0.0625).
** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand
** for likelihood(X,0.9375).
** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent
** to likelihood(X,0.9375). */
/* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
pExpr->iTable = pDef->zName[0]=='u' ? 8388608 : 125829120;
}
@ -735,7 +739,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
return WRC_Prune;
}
#endif
if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ) ExprSetProperty(pExpr,EP_Constant);
if( pDef->funcFlags & SQLITE_FUNC_CONSTANT ){
ExprSetProperty(pExpr,EP_ConstFunc);
}
}
if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
@ -1046,7 +1052,8 @@ int sqlite3ResolveOrderGroupBy(
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr, zType,0);
resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
zType,0);
}
}
return 0;

View File

@ -3194,7 +3194,10 @@ static void substSelect(
**
** (1) The subquery and the outer query do not both use aggregates.
**
** (2) The subquery is not an aggregate or the outer query is not a join.
** (2) The subquery is not an aggregate or (2a) the outer query is not a join
** and (2b) the outer query does not use subqueries other than the one
** FROM-clause subquery that is a candidate for flattening. (2b is
** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
**
** (3) The subquery is not the right operand of a left outer join
** (Originally ticket #306. Strengthened by ticket #3300)
@ -3331,8 +3334,17 @@ static int flattenSubquery(
iParent = pSubitem->iCursor;
pSub = pSubitem->pSelect;
assert( pSub!=0 );
if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */
if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */
if( subqueryIsAgg ){
if( isAgg ) return 0; /* Restriction (1) */
if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */
if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery))
|| (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0
|| (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0
){
return 0; /* Restriction (2b) */
}
}
pSubSrc = pSub->pSrc;
assert( pSubSrc );
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
@ -4752,6 +4764,13 @@ int sqlite3Select(
}
isAgg = (p->selFlags & SF_Aggregate)!=0;
assert( pEList!=0 );
#if SELECTTRACE_ENABLED
if( sqlite3SelectTrace & 0x100 ){
SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
/* Begin generating code.
*/
@ -5497,9 +5516,9 @@ select_end:
void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
int n = 0;
pView = sqlite3TreeViewPush(pView, moreToFollow);
sqlite3TreeViewLine(pView, "SELECT%s%s",
sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p)",
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
((p->selFlags & SF_Aggregate) ? " agg_flag" : "")
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p
);
if( p->pSrc && p->pSrc->nSrc ) n++;
if( p->pWhere ) n++;

View File

@ -970,6 +970,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
#define SQLITE_FCNTL_ZIPVFS_PAGER 24
#define SQLITE_FCNTL_OTA 25
/*
** CAPI3REF: Mutex Handle

View File

@ -1207,6 +1207,7 @@ struct sqlite3 {
#define SQLITE_QueryOnly 0x02000000 /* Disable database changes */
#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */
/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
@ -2043,8 +2044,14 @@ struct Expr {
#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
#define EP_Constant 0x080000 /* Node is a constant */
#define EP_ConstFunc 0x080000 /* Node is a SQLITE_FUNC_CONSTANT function */
#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
/*
** Combinations of two or more EP_* flags
*/
#define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */
/*
** These macros can be used to test, set, or clear bits in the
@ -3152,6 +3159,7 @@ ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
void sqlite3ExprListDelete(sqlite3*, ExprList*);
u32 sqlite3ExprListFlags(const ExprList*);
int sqlite3Init(sqlite3*, char**);
int sqlite3InitCallback(void*, int, char**, char**);
void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
@ -3735,12 +3743,11 @@ void sqlite3MemJournalOpen(sqlite3_file *);
int sqlite3MemJournalSize(void);
int sqlite3IsMemJournal(sqlite3_file *);
void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p);
#if SQLITE_MAX_EXPR_DEPTH>0
void sqlite3ExprSetHeight(Parse *pParse, Expr *p);
int sqlite3SelectExprHeight(Select *);
int sqlite3ExprCheckHeight(Parse*, int);
#else
#define sqlite3ExprSetHeight(x,y)
#define sqlite3SelectExprHeight(x) 0
#define sqlite3ExprCheckHeight(x,y)
#endif
@ -3818,7 +3825,6 @@ SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
/*
** Threading interface
*/

View File

@ -6928,7 +6928,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#endif
};
static int bitmask_size = sizeof(Bitmask)*8;
int i;
extern int sqlite3_sync_count, sqlite3_fullsync_count;

View File

@ -154,12 +154,17 @@ int sqlite3_blob_open(
Incrblob *pBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || ppBlob==0 || zTable==0 ){
if( ppBlob==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
*ppBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
flags = !!flags; /* flags = (flags ? 1 : 0); */
*ppBlob = 0;
sqlite3_mutex_enter(db->mutex);
@ -373,7 +378,7 @@ static int blobReadWrite(
sqlite3_mutex_enter(db->mutex);
v = (Vdbe*)p->pStmt;
if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){
if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){
/* Request is out of range. Return a transient error. */
rc = SQLITE_ERROR;
}else if( v==0 ){

View File

@ -2920,9 +2920,7 @@ int sqlite3WalFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
if( pWal->nCkpt==0 ){
sqlite3PagerWalSalt(pList->pPager, pWal->hdr.aSalt);
}
if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);

View File

@ -13,7 +13,9 @@
# focus of this script is making sure collations pass through the
# unary + operator.
#
# $Id: collate8.test,v 1.2 2008/08/25 12:14:09 drh Exp $
# 2015-02-09: Added tests to make sure COLLATE passes through function
# calls. Ticket [ca0d20b6cdddec5e81b8d66f89c46a5583b5f6f6].
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -122,4 +124,34 @@ do_test collate8-2.8 {
}
} {abc}
# Make sure the COLLATE operator perculates up through function calls
# and other Expr structures that use the Expr.x.pList field.
#
do_execsql_test collate8-3.1 {
SELECT 'abc'==('ABC'||'') COLLATE nocase;
SELECT 'abc'==('ABC'||'' COLLATE nocase);
SELECT 'abc'==('ABC'||('' COLLATE nocase));
SELECT 'abc'==('ABC'||upper('' COLLATE nocase));
} {1 1 1 1}
do_execsql_test collate8-3.2 {
SELECT 'abc'==('ABC'||max('' COLLATE nocase,'' COLLATE binary));
} {1}
# The COLLATE binary is on the left and so takes precedence
do_execsql_test collate8-3.3 {
SELECT 'abc'==('ABC'||max('' COLLATE binary,'' COLLATE nocase));
} {0}
do_execsql_test collate8-3.4 {
SELECT 'abc'==('ABC'||CASE WHEN 1-1=2 THEN '' COLLATE nocase
ELSE '' COLLATE binary END);
SELECT 'abc'==('ABC'||CASE WHEN 1+1=2 THEN '' COLLATE nocase
ELSE '' COLLATE binary END);
} {1 1}
do_execsql_test collate8-3.5 {
SELECT 'abc'==('ABC'||CASE WHEN 1=2 THEN '' COLLATE binary
ELSE '' COLLATE nocase END);
} {0}
finish_test

View File

@ -324,12 +324,34 @@ do_test incrblob2-6.2 {
sqlite3_blob_read $rdHandle 0 2
} {AB}
do_test incrblob2-6.2b {
set rc [catch {
# Prior to 2015-02-07, the following caused a segfault due to
# integer overflow.
sqlite3_blob_read $rdHandle 2147483647 2147483647
} errmsg]
lappend rc $errmsg
} {1 SQLITE_ERROR}
do_test incrblob2-6.3 {
set wrHandle [db incrblob t1 data 1]
sqlite3_blob_write $wrHandle 0 ZZZZZZZZZZ
sqlite3_blob_read $rdHandle 2 4
} {ZZZZ}
do_test incrblob2-6.3b {
set rc [catch {
# Prior to 2015-02-07, the following caused a segfault due to
# integer overflow.
sqlite3_blob_write $wrHandle 2147483647 YYYYYYYYYYYYYYYYYY
} errmsg]
lappend rc $errmsg
} {1 SQLITE_ERROR}
do_test incrblob2-6.3c {
sqlite3_blob_read $rdHandle 2 4
} {ZZZZ}
do_test incrblob2-6.4 {
close $wrHandle
close $rdHandle

View File

@ -557,5 +557,61 @@ do_catchsql_test 10.8 {
)
} $err
# 2015-02-09 Ticket [2f7170d73bf9abf80339187aa3677dce3dbcd5ca]
# "misuse of aggregate" error if aggregate column from FROM
# subquery is used in correlated subquery
#
do_execsql_test 11.1 {
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(w INT, x INT);
INSERT INTO t1(w,x)
VALUES(1,10),(2,20),(3,30),
(2,21),(3,31),
(3,32);
CREATE INDEX t1wx ON t1(w,x);
DROP TABLE IF EXISTS t2;
CREATE TABLE t2(w INT, y VARCHAR(8));
INSERT INTO t2(w,y) VALUES(1,'one'),(2,'two'),(3,'three'),(4,'four');
CREATE INDEX t2wy ON t2(w,y);
SELECT cnt, xyz, (SELECT y FROM t2 WHERE w=cnt), '|'
FROM (SELECT count(*) AS cnt, w AS xyz FROM t1 GROUP BY 2)
ORDER BY cnt, xyz;
} {1 1 one | 2 2 two | 3 3 three |}
do_execsql_test 11.2 {
SELECT cnt, xyz, lower((SELECT y FROM t2 WHERE w=cnt)), '|'
FROM (SELECT count(*) AS cnt, w AS xyz FROM t1 GROUP BY 2)
ORDER BY cnt, xyz;
} {1 1 one | 2 2 two | 3 3 three |}
do_execsql_test 11.3 {
SELECT cnt, xyz, '|'
FROM (SELECT count(*) AS cnt, w AS xyz FROM t1 GROUP BY 2)
WHERE (SELECT y FROM t2 WHERE w=cnt)!='two'
ORDER BY cnt, xyz;
} {1 1 | 3 3 |}
do_execsql_test 11.4 {
SELECT cnt, xyz, '|'
FROM (SELECT count(*) AS cnt, w AS xyz FROM t1 GROUP BY 2)
ORDER BY lower((SELECT y FROM t2 WHERE w=cnt));
} {1 1 | 3 3 | 2 2 |}
do_execsql_test 11.5 {
SELECT cnt, xyz,
CASE WHEN (SELECT y FROM t2 WHERE w=cnt)=='two'
THEN 'aaa' ELSE 'bbb'
END, '|'
FROM (SELECT count(*) AS cnt, w AS xyz FROM t1 GROUP BY 2)
ORDER BY +cnt;
} {1 1 bbb | 2 2 aaa | 3 3 bbb |}
do_execsql_test 11.100 {
DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1(x);
CREATE TABLE t2(y, z);
SELECT ( SELECT y FROM t2 WHERE z = cnt )
FROM ( SELECT count(*) AS cnt FROM t1 );
} {{}}
finish_test

View File

@ -315,9 +315,6 @@ set pragma_def {
NAME: soft_heap_limit
NAME: threads
NAME: pager_ota_mode
IF: defined(SQLITE_ENABLE_OTA)
}
# Open the output file

View File

@ -112,8 +112,9 @@ foreach hdr {
pcache.h
pragma.h
rtree.h
sqlite3ext.h
sqlite3.h
sqlite3ext.h
sqlite3ota.h
sqliteicu.h
sqliteInt.h
sqliteLimit.h
@ -334,6 +335,7 @@ foreach file {
rtree.c
icu.c
fts3_icu.c
sqlite3ota.c
} {
copy_file tsrc/$file
}

View File

@ -4,6 +4,24 @@
#
if {[catch {
# Argument $tname is the name of a table within the database opened by
# database handle [db]. Return true if it is a WITHOUT ROWID table, or
# false otherwise.
#
proc is_without_rowid {tname} {
set t [string map {' ''} $tname]
db eval "PRAGMA index_list = '$t'" o {
if {$o(origin) == "pk"} {
set n $o(name)
if {0==[db one { SELECT count(*) FROM sqlite_master WHERE name=$n }]} {
return 1
}
}
}
return 0
}
# Get the name of the database to analyze
#
proc usage {} {
@ -167,20 +185,21 @@ set sql { SELECT name, tbl_name FROM sqlite_master WHERE rootpage>0 }
foreach {name tblname} [concat sqlite_master sqlite_master [db eval $sql]] {
set is_index [expr {$name!=$tblname}]
set idx_btree [expr {$is_index || [is_without_rowid $name]}]
db eval {
SELECT
sum(ncell) AS nentry,
sum(isleaf(pagetype, $is_index) * ncell) AS leaf_entries,
sum(isleaf(pagetype, $idx_btree) * ncell) AS leaf_entries,
sum(payload) AS payload,
sum(isoverflow(pagetype, $is_index) * payload) AS ovfl_payload,
sum(isoverflow(pagetype, $idx_btree) * payload) AS ovfl_payload,
sum(path LIKE '%+000000') AS ovfl_cnt,
max(mx_payload) AS mx_payload,
sum(isinternal(pagetype, $is_index)) AS int_pages,
sum(isleaf(pagetype, $is_index)) AS leaf_pages,
sum(isoverflow(pagetype, $is_index)) AS ovfl_pages,
sum(isinternal(pagetype, $is_index) * unused) AS int_unused,
sum(isleaf(pagetype, $is_index) * unused) AS leaf_unused,
sum(isoverflow(pagetype, $is_index) * unused) AS ovfl_unused,
sum(isinternal(pagetype, $idx_btree)) AS int_pages,
sum(isleaf(pagetype, $idx_btree)) AS leaf_pages,
sum(isoverflow(pagetype, $idx_btree)) AS ovfl_pages,
sum(isinternal(pagetype, $idx_btree) * unused) AS int_unused,
sum(isleaf(pagetype, $idx_btree) * unused) AS leaf_unused,
sum(isoverflow(pagetype, $idx_btree) * unused) AS ovfl_unused,
sum(pgsize) AS compressed_size
FROM temp.dbstat WHERE name = $name
} break