Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f058b9c472 | |||
| 9263220487 | |||
| 9df25c4736 | |||
| 00d5ab7405 | |||
| 7f0cd09cdd | |||
| 7ac07bda73 | |||
| 856d446efe | |||
| dde548cb16 | |||
| 815bd625c2 | |||
| c5aca218aa | |||
| 504ab3b6e5 | |||
| a6fb464878 | |||
| b387e63707 | |||
| a48bae8819 | |||
| df553659ad | |||
| 2bbb62ce0b | |||
| 8255079611 | |||
| 6758959ec3 | |||
| e11afed562 | |||
| 55d46f01c5 | |||
| 1cdd523a50 | |||
| 39b7ff7987 | |||
| 903dd7d5d1 | |||
| 1c29c27bb0 | |||
| 43030868e6 | |||
| ec16d98489 | |||
| 5c06dd831f | |||
| 28b22a0159 | |||
| 4141a81b30 | |||
| fdb031891c | |||
| 57d0d9f33c | |||
| 32c9068adb | |||
| 6da7a0a93d | |||
| f37120a416 | |||
| 5e4eeb3c00 | |||
| c3b328da6e | |||
| 9340de52e2 | |||
| a9106ca83d | |||
| 54fc21438e | |||
| 2bd7ace54e | |||
| 67d4b20c46 | |||
| 5b828f8c33 | |||
| bbd0c46e0e | |||
| 04f121c187 | |||
| 5ebc83b1d2 | |||
| 882b8e0974 | |||
| ee9fa4af15 | |||
| 92f55ceb70 | |||
| 942b0a68ea | |||
| 138bf3bc7f | |||
| 71405b27c9 | |||
| 20ab217f93 | |||
| 7551c5c3e6 | |||
| e3e031ce85 | |||
| d092df51de | |||
| eadc772beb | |||
| f5fe23909d | |||
| 5902352fca | |||
| c76c64f931 | |||
| 52800df9fc | |||
| 01325e86e6 | |||
| a3cf4f7ccb | |||
| 2e24971f2a | |||
| cd73244144 | |||
| 07427d0350 | |||
| faa8c4fdcb | |||
| de1f6a270b | |||
| dd0ad3785b | |||
| 00dbc89824 | |||
| 2f8109cef1 | |||
| 8800b53bc5 | |||
| a8ccf3ed74 | |||
| d4ef7c50bb | |||
| fe9752d934 | |||
| 06c9722564 | |||
| 738a3b2fcb | |||
| 1e35850229 | |||
| e2863b6074 | |||
| 75fda9b395 | |||
| 3b660d7da3 | |||
| 161a6845f9 | |||
| 0a3105c25f | |||
| 3102aa01fd | |||
| 3e6bc3977f | |||
| 5c08eb143f | |||
| 25876016a3 | |||
| 120b3a4f61 | |||
| 0a99e9fb01 | |||
| fdf3a8970d | |||
| 61826cdb6c | |||
| 7ca1d07aff | |||
| ca3d648f9c | |||
| 7bf9ec1c26 | |||
| 841ed2b0f2 | |||
| 54f0dec3e6 | |||
| ca5ceb4229 | |||
| f8bd49ee22 | |||
| 268c0f8844 | |||
| 54ef517012 | |||
| 8a1e107811 | |||
| 0dc3ac5f3d | |||
| d8260fd1e3 | |||
| 08e06b0b21 | |||
| 9f0da72b09 | |||
| d6446a0abe | |||
| 5e3fc6e99f | |||
| ae0151c5a7 | |||
| d2f5ee20f0 | |||
| 93fdbfa64f | |||
| d1193a9893 | |||
| be758ce3ae | |||
| 6c5984a950 | |||
| 318e63c60b | |||
| ed18c96107 | |||
| 4caa8fbc1e | |||
| bb03ae7c8a | |||
| c6c3d00f33 | |||
| 44ed6bc5c1 | |||
| abc0788663 | |||
| 1b95de09bc | |||
| 15e1ec4c5b | |||
| 1af32e83ca | |||
| d4a80e6742 | |||
| ee8d0b4111 | |||
| 02cf6e1681 | |||
| 13ce7931f7 | |||
| d54e06ca5e | |||
| a690402b4c | |||
| 9295d21bfd | |||
| 4e9246e9db | |||
| 0c4ba2662e | |||
| 55b40e0ec5 | |||
| 19f913f268 | |||
| 8fa7f590cc | |||
| 19b465a0ae | |||
| 98231c054b | |||
| b0083756f2 |
+5
-1
@@ -339,6 +339,9 @@ SRC += \
|
||||
SRC += \
|
||||
$(TOP)/ext/rtree/rtree.h \
|
||||
$(TOP)/ext/rtree/rtree.c
|
||||
SRC += \
|
||||
$(TOP)/ext/ota/sqlite3ota.h \
|
||||
$(TOP)/ext/ota/sqlite3ota.c
|
||||
|
||||
|
||||
# Generated source code files
|
||||
@@ -395,7 +398,8 @@ TESTSRC = \
|
||||
$(TOP)/src/test_vfs.c \
|
||||
$(TOP)/src/test_wsd.c \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_test.c
|
||||
$(TOP)/ext/fts3/fts3_test.c \
|
||||
$(TOP)/ext/ota/test_ota.c
|
||||
|
||||
# Statically linked extensions
|
||||
#
|
||||
|
||||
+5
-2
@@ -1001,7 +1001,9 @@ SRC4 = \
|
||||
$(TOP)\ext\icu\sqliteicu.h \
|
||||
$(TOP)\ext\icu\icu.c \
|
||||
$(TOP)\ext\rtree\rtree.h \
|
||||
$(TOP)\ext\rtree\rtree.c
|
||||
$(TOP)\ext\rtree\rtree.c \
|
||||
$(TOP)\ext\ota\sqlite3ota.h \
|
||||
$(TOP)\ext\ota\sqlite3ota.c
|
||||
|
||||
|
||||
# Generated source code files
|
||||
@@ -1061,7 +1063,8 @@ TESTSRC = \
|
||||
$(TOP)\src\test_vfs.c \
|
||||
$(TOP)\src\test_wsd.c \
|
||||
$(TOP)\ext\fts3\fts3_term.c \
|
||||
$(TOP)\ext\fts3\fts3_test.c
|
||||
$(TOP)\ext\fts3\fts3_test.c \
|
||||
$(TOP)\ext\ota\test_ota.c
|
||||
|
||||
# Statically linked extensions
|
||||
#
|
||||
|
||||
@@ -131,7 +131,7 @@ struct StrBuffer {
|
||||
static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
|
||||
MatchinfoBuffer *pRet;
|
||||
int nByte = sizeof(u32) * (2*nElem + 2) + sizeof(MatchinfoBuffer);
|
||||
int nStr = strlen(zMatchinfo);
|
||||
int nStr = (int)strlen(zMatchinfo);
|
||||
|
||||
pRet = sqlite3_malloc(nByte + nStr+1);
|
||||
if( pRet ){
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
** 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 a command-line application that uses the OTA
|
||||
** extension. See the usage() function below for an explanation.
|
||||
*/
|
||||
|
||||
#include "sqlite3ota.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Print a usage message and exit.
|
||||
*/
|
||||
void usage(const char *zArgv0){
|
||||
fprintf(stderr,
|
||||
"Usage: %s [-step NSTEP] TARGET-DB OTA-DB\n"
|
||||
"\n"
|
||||
" Argument OTA-DB must be an OTA database containing an update suitable for\n"
|
||||
" target database TARGET-DB. If NSTEP is set to less than or equal to zero\n"
|
||||
" (the default value), this program attempts to apply the entire update to\n"
|
||||
" the target database.\n"
|
||||
"\n"
|
||||
" If NSTEP is greater than zero, then a maximum of NSTEP calls are made\n"
|
||||
" to sqlite3ota_step(). If the OTA update has not been completely applied\n"
|
||||
" after the NSTEP'th call is made, the state is saved in the database OTA-DB\n"
|
||||
" and the program exits. Subsequent invocations of this (or any other OTA)\n"
|
||||
" application will use this state to resume applying the OTA update to the\n"
|
||||
" target db.\n"
|
||||
"\n"
|
||||
, zArgv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void report_default_vfs(){
|
||||
sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
|
||||
fprintf(stdout, "default vfs is \"%s\"\n", pVfs->zName);
|
||||
}
|
||||
|
||||
void report_ota_vfs(sqlite3ota *pOta){
|
||||
sqlite3 *db = sqlite3ota_db(pOta, 0);
|
||||
if( db ){
|
||||
char *zName = 0;
|
||||
sqlite3_file_control(db, "main", SQLITE_FCNTL_VFSNAME, &zName);
|
||||
if( zName ){
|
||||
fprintf(stdout, "using vfs \"%s\"\n", zName);
|
||||
}else{
|
||||
fprintf(stdout, "vfs name not available\n");
|
||||
}
|
||||
sqlite3_free(zName);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
int i;
|
||||
const char *zTarget; /* Target database to apply OTA to */
|
||||
const char *zOta; /* Database containing OTA */
|
||||
char *zErrmsg; /* Error message, if any */
|
||||
sqlite3ota *pOta; /* OTA handle */
|
||||
int nStep = 0; /* Maximum number of step() calls */
|
||||
int rc;
|
||||
sqlite3_int64 nProgress = 0;
|
||||
|
||||
/* Process command line arguments. Following this block local variables
|
||||
** zTarget, zOta and nStep are all set. */
|
||||
if( argc==5 ){
|
||||
int nArg1 = strlen(argv[1]);
|
||||
if( nArg1>5 || nArg1<2 || memcmp("-step", argv[1], nArg1) ) usage(argv[0]);
|
||||
nStep = atoi(argv[2]);
|
||||
}else if( argc!=3 ){
|
||||
usage(argv[0]);
|
||||
}
|
||||
zTarget = argv[argc-2];
|
||||
zOta = argv[argc-1];
|
||||
|
||||
report_default_vfs();
|
||||
|
||||
/* Open an OTA handle. If nStep is less than or equal to zero, call
|
||||
** sqlite3ota_step() until either the OTA has been completely applied
|
||||
** or an error occurs. Or, if nStep is greater than zero, call
|
||||
** sqlite3ota_step() a maximum of nStep times. */
|
||||
pOta = sqlite3ota_open(zTarget, zOta);
|
||||
report_ota_vfs(pOta);
|
||||
for(i=0; (nStep<=0 || i<nStep) && sqlite3ota_step(pOta)==SQLITE_OK; i++);
|
||||
nProgress = sqlite3ota_progress(pOta);
|
||||
rc = sqlite3ota_close(pOta, &zErrmsg);
|
||||
|
||||
/* Let the user know what happened. */
|
||||
switch( rc ){
|
||||
case SQLITE_OK:
|
||||
fprintf(stdout,
|
||||
"SQLITE_OK: ota update incomplete (%lld operations so far)\n",
|
||||
nProgress
|
||||
);
|
||||
break;
|
||||
|
||||
case SQLITE_DONE:
|
||||
fprintf(stdout,
|
||||
"SQLITE_DONE: ota update completed (%lld operations)\n",
|
||||
nProgress
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "error=%d: %s\n", rc, zErrmsg);
|
||||
break;
|
||||
}
|
||||
|
||||
sqlite3_free(zErrmsg);
|
||||
return (rc==SQLITE_OK || rc==SQLITE_DONE) ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,680 @@
|
||||
# 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 ota1
|
||||
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
|
||||
# Create a simple OTA database. That expects to write to a table:
|
||||
#
|
||||
# CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
#
|
||||
proc create_ota1 {filename} {
|
||||
forcedelete $filename
|
||||
sqlite3 ota1 $filename
|
||||
ota1 eval {
|
||||
CREATE TABLE data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, 2, 3, 0);
|
||||
INSERT INTO data_t1 VALUES(2, 'two', 'three', 0);
|
||||
INSERT INTO data_t1 VALUES(3, NULL, 8.2, 0);
|
||||
}
|
||||
ota1 close
|
||||
return $filename
|
||||
}
|
||||
|
||||
# Create a simple OTA database. That expects to write to a table:
|
||||
#
|
||||
# CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
#
|
||||
# This OTA includes both insert and delete operations.
|
||||
#
|
||||
proc create_ota4 {filename} {
|
||||
forcedelete $filename
|
||||
sqlite3 ota1 $filename
|
||||
ota1 eval {
|
||||
CREATE TABLE data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, 2, 3, 0);
|
||||
INSERT INTO data_t1 VALUES(2, NULL, 5, 1);
|
||||
INSERT INTO data_t1 VALUES(3, 8, 9, 0);
|
||||
INSERT INTO data_t1 VALUES(4, NULL, 11, 1);
|
||||
}
|
||||
ota1 close
|
||||
return $filename
|
||||
}
|
||||
#
|
||||
# Create a simple OTA database. That expects to write to a table:
|
||||
#
|
||||
# CREATE TABLE t1(c, b, '(a)' INTEGER PRIMARY KEY);
|
||||
#
|
||||
# This OTA includes both insert and delete operations.
|
||||
#
|
||||
proc create_ota4b {filename} {
|
||||
forcedelete $filename
|
||||
sqlite3 ota1 $filename
|
||||
ota1 eval {
|
||||
CREATE TABLE data_t1(c, b, '(a)', ota_control);
|
||||
INSERT INTO data_t1 VALUES(3, 2, 1, 0);
|
||||
INSERT INTO data_t1 VALUES(5, NULL, 2, 1);
|
||||
INSERT INTO data_t1 VALUES(9, 8, 3, 0);
|
||||
INSERT INTO data_t1 VALUES(11, NULL, 4, 1);
|
||||
}
|
||||
ota1 close
|
||||
return $filename
|
||||
}
|
||||
|
||||
# Create a simple OTA database. That expects to write to a table:
|
||||
#
|
||||
# CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
|
||||
#
|
||||
# This OTA includes update statements.
|
||||
#
|
||||
proc create_ota5 {filename} {
|
||||
forcedelete $filename
|
||||
sqlite3 ota5 $filename
|
||||
ota5 eval {
|
||||
CREATE TABLE data_t1(a, b, c, d, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, NULL, NULL, 5, '...x'); -- SET d = 5
|
||||
INSERT INTO data_t1 VALUES(2, NULL, 10, 5, '..xx'); -- SET c=10, d = 5
|
||||
INSERT INTO data_t1 VALUES(3, 11, NULL, NULL, '.x..'); -- SET b=11
|
||||
}
|
||||
ota5 close
|
||||
return $filename
|
||||
}
|
||||
|
||||
# Run the OTA in file $ota on target database $target until completion.
|
||||
#
|
||||
proc run_ota {target ota} {
|
||||
sqlite3ota ota $target $ota
|
||||
while 1 {
|
||||
set rc [ota step]
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
}
|
||||
ota close
|
||||
}
|
||||
|
||||
proc step_ota {target ota} {
|
||||
while 1 {
|
||||
sqlite3ota ota $target $ota
|
||||
set rc [ota step]
|
||||
ota close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
# Same as [step_ota], except using a URI to open the target db.
|
||||
#
|
||||
proc step_ota_uri {target ota} {
|
||||
while 1 {
|
||||
sqlite3ota ota file:$target?xyz=&abc=123 $ota
|
||||
set rc [ota step]
|
||||
ota close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
# Same as [step_ota], except using an external state database - "state.db"
|
||||
#
|
||||
proc step_ota_state {target ota} {
|
||||
while 1 {
|
||||
sqlite3ota ota $target $ota state.db
|
||||
set rc [ota step]
|
||||
ota close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
proc dbfilecksum {file} {
|
||||
sqlite3 ck $file
|
||||
set cksum [dbcksum ck main]
|
||||
ck close
|
||||
set cksum
|
||||
}
|
||||
|
||||
foreach {tn3 create_vfs destroy_vfs} {
|
||||
1 {} {}
|
||||
2 {
|
||||
sqlite3ota_create_vfs -default myota ""
|
||||
} {
|
||||
sqlite3ota_destroy_vfs myota
|
||||
}
|
||||
} {
|
||||
|
||||
eval $create_vfs
|
||||
|
||||
foreach {tn2 cmd} {
|
||||
1 run_ota
|
||||
2 step_ota 3 step_ota_uri 4 step_ota_state
|
||||
} {
|
||||
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
|
||||
create_ota1 ota.db
|
||||
set check [dbfilecksum ota.db]
|
||||
forcedelete state.db
|
||||
|
||||
do_test $tn3.1.$tn2.$tn.1 {
|
||||
$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
|
||||
|
||||
if {$cmd=="step_ota_state"} {
|
||||
do_test $tn3.1.$tn2.$tn.6 { file exists state.db } 1
|
||||
do_test $tn3.1.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 1
|
||||
} else {
|
||||
do_test $tn3.1.$tn2.$tn.8 { file exists state.db } 0
|
||||
do_test $tn3.1.$tn2.$tn.9 { expr {$check == [dbfilecksum ota.db]} } 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 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 $tn3.2.1 { CREATE TABLE t1(a, b, c) }
|
||||
do_test $tn3.2.2 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_ERROR}
|
||||
do_test $tn3.2.3 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {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}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 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');
|
||||
}
|
||||
|
||||
} {
|
||||
reset_db
|
||||
execsql $schema
|
||||
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 3 step_ota_state } {
|
||||
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);
|
||||
}
|
||||
} {
|
||||
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');
|
||||
}
|
||||
|
||||
create_ota4 ota.db
|
||||
set check [dbfilecksum ota.db]
|
||||
forcedelete state.db
|
||||
|
||||
do_test $tn3.4.$tn2.$tn.1 {
|
||||
$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
|
||||
|
||||
if {$cmd=="step_ota_state"} {
|
||||
do_test $tn3.4.$tn2.$tn.4 { file exists state.db } 1
|
||||
do_test $tn3.4.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1
|
||||
} else {
|
||||
do_test $tn3.4.$tn2.$tn.6 { file exists state.db } 0
|
||||
do_test $tn3.4.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_state} {
|
||||
foreach {tn schema} {
|
||||
1 {
|
||||
CREATE TABLE t1(c, b, '(a)' INTEGER PRIMARY KEY);
|
||||
CREATE INDEX i1 ON t1(c, b);
|
||||
}
|
||||
2 {
|
||||
CREATE TABLE t1(c, b, '(a)' PRIMARY KEY);
|
||||
}
|
||||
3 {
|
||||
CREATE TABLE t1(c, b, '(a)' PRIMARY KEY) WITHOUT ROWID;
|
||||
}
|
||||
} {
|
||||
reset_db
|
||||
execsql $schema
|
||||
execsql {
|
||||
INSERT INTO t1('(a)', b, c) VALUES(2, 'hello', 'world');
|
||||
INSERT INTO t1('(a)', b, c) VALUES(4, 'hello', 'planet');
|
||||
INSERT INTO t1('(a)', b, c) VALUES(6, 'hello', 'xyz');
|
||||
}
|
||||
|
||||
create_ota4b ota.db
|
||||
set check [dbfilecksum ota.db]
|
||||
forcedelete state.db
|
||||
|
||||
do_test $tn3.5.$tn2.$tn.1 {
|
||||
$cmd test.db ota.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test $tn3.5.$tn2.$tn.2 {
|
||||
SELECT * FROM t1 ORDER BY "(a)" ASC;
|
||||
} {
|
||||
3 2 1
|
||||
9 8 3
|
||||
xyz hello 6
|
||||
}
|
||||
|
||||
do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok
|
||||
|
||||
if {$cmd=="step_ota_state"} {
|
||||
do_test $tn3.5.$tn2.$tn.4 { file exists state.db } 1
|
||||
do_test $tn3.5.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1
|
||||
} else {
|
||||
do_test $tn3.5.$tn2.$tn.6 { file exists state.db } 0
|
||||
do_test $tn3.5.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_state} {
|
||||
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);
|
||||
}
|
||||
|
||||
create_ota5 ota.db
|
||||
set check [dbfilecksum ota.db]
|
||||
forcedelete state.db
|
||||
|
||||
do_test $tn3.5.$tn2.$tn.1 {
|
||||
$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.6.$tn2.$tn.3 { PRAGMA integrity_check } ok
|
||||
|
||||
if {$cmd=="step_ota_state"} {
|
||||
do_test $tn3.6.$tn2.$tn.4 { file exists state.db } 1
|
||||
do_test $tn3.6.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1
|
||||
} else {
|
||||
do_test $tn3.6.$tn2.$tn.6 { file exists state.db } 0
|
||||
do_test $tn3.6.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 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.
|
||||
#
|
||||
# 6: An update string of the wrong length
|
||||
#
|
||||
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}
|
||||
|
||||
6 {
|
||||
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE TABLE ota.data_t1(a, b, ota_control);
|
||||
INSERT INTO ota.data_t1 VALUES(1, 2, 'x.x');
|
||||
} {SQLITE_ERROR - invalid ota_control value}
|
||||
|
||||
7 {
|
||||
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE TABLE ota.data_t1(a, b, ota_control);
|
||||
INSERT INTO ota.data_t1 VALUES(1, 2, NULL);
|
||||
} {SQLITE_ERROR - invalid ota_control value}
|
||||
|
||||
8 {
|
||||
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE TABLE ota.data_t1(a, b, ota_control);
|
||||
INSERT INTO ota.data_t1 VALUES(1, 2, 4);
|
||||
} {SQLITE_ERROR - invalid ota_control value}
|
||||
|
||||
9 {
|
||||
CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
|
||||
CREATE TABLE ota.data_t1(a, b, ota_control);
|
||||
INSERT INTO ota.data_t1 VALUES(1, 2, 2);
|
||||
} {SQLITE_ERROR - invalid ota_control value}
|
||||
|
||||
10 {
|
||||
CREATE TABLE t2(a, b);
|
||||
CREATE TABLE ota.data_t1(a, b, ota_control);
|
||||
INSERT INTO ota.data_t1 VALUES(1, 2, 2);
|
||||
} {SQLITE_ERROR - no such table: t1}
|
||||
|
||||
11 {
|
||||
CREATE TABLE ota.data_t2(a, b, ota_control);
|
||||
INSERT INTO ota.data_t2 VALUES(1, 2, 2);
|
||||
} {SQLITE_ERROR - no such table: t2}
|
||||
|
||||
} {
|
||||
reset_db
|
||||
forcedelete ota.db
|
||||
execsql { ATTACH 'ota.db' AS ota }
|
||||
execsql $schema
|
||||
|
||||
do_test $tn3.7.$tn {
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} [list 1 $error]
|
||||
}
|
||||
}
|
||||
|
||||
# Test that an OTA database containing no input tables is handled
|
||||
# correctly.
|
||||
reset_db
|
||||
forcedelete ota.db
|
||||
do_test $tn3.8 {
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
# Test that OTA can update indexes containing NULL values.
|
||||
#
|
||||
reset_db
|
||||
forcedelete ota.db
|
||||
do_execsql_test $tn3.9.1 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 ON t1(b, c);
|
||||
INSERT INTO t1 VALUES(1, 1, NULL);
|
||||
INSERT INTO t1 VALUES(2, NULL, 2);
|
||||
INSERT INTO t1 VALUES(3, NULL, NULL);
|
||||
|
||||
ATTACH 'ota.db' AS ota;
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(3, NULL, NULL, 1);
|
||||
} {}
|
||||
|
||||
do_test $tn3.9.2 {
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
do_execsql_test $tn3.9.3 {
|
||||
SELECT * FROM t1
|
||||
} {2 {} 2}
|
||||
do_execsql_test $tn3.9.4 { PRAGMA integrity_check } {ok}
|
||||
|
||||
catch { db close }
|
||||
eval $destroy_vfs
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
# 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 ota10
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that UPDATE commands work even if the input columns are in a
|
||||
# different order to the output columns.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 'b', 'c');
|
||||
}
|
||||
|
||||
proc apply_ota {sql} {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval $sql
|
||||
db2 close
|
||||
sqlite3ota ota test.db ota.db
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
apply_ota {
|
||||
CREATE TABLE data_t1(a, c, b, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, 'xxx', NULL, '.x.');
|
||||
}
|
||||
db eval { SELECT * FROM t1 }
|
||||
} {1 b xxx}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that the hidden languageid column of an fts4 table can be
|
||||
# written.
|
||||
#
|
||||
ifcapable fts3 {
|
||||
do_execsql_test 2.0 {
|
||||
CREATE VIRTUAL TABLE ft USING fts4(a, b, languageid='langid');
|
||||
}
|
||||
do_test 2.1 {
|
||||
apply_ota {
|
||||
CREATE TABLE data_ft(a, b, ota_rowid, langid, ota_control);
|
||||
INSERT INTO data_ft VALUES('a', 'b', 22, 1, 0); -- insert
|
||||
INSERT INTO data_ft VALUES('a', 'b', 23, 10, 0); -- insert
|
||||
INSERT INTO data_ft VALUES('a', 'b', 24, 100, 0); -- insert
|
||||
}
|
||||
db eval { SELECT a, b, rowid, langid FROM ft }
|
||||
} [list {*}{
|
||||
a b 22 1
|
||||
a b 23 10
|
||||
a b 24 100
|
||||
}]
|
||||
|
||||
# Or not - this data_xxx table has no langid column, so langid
|
||||
# defaults to 0.
|
||||
#
|
||||
do_test 2.2 {
|
||||
apply_ota {
|
||||
CREATE TABLE data_ft(a, b, ota_rowid, ota_control);
|
||||
INSERT INTO data_ft VALUES('a', 'b', 25, 0); -- insert
|
||||
}
|
||||
db eval { SELECT a, b, rowid, langid FROM ft }
|
||||
} [list {*}{
|
||||
a b 22 1
|
||||
a b 23 10
|
||||
a b 24 100
|
||||
a b 25 0
|
||||
}]
|
||||
|
||||
# Update langid.
|
||||
#
|
||||
do_test 2.3 {
|
||||
apply_ota {
|
||||
CREATE TABLE data_ft(a, b, ota_rowid, langid, ota_control);
|
||||
INSERT INTO data_ft VALUES(NULL, NULL, 23, 50, '..x');
|
||||
INSERT INTO data_ft VALUES(NULL, NULL, 25, 500, '..x');
|
||||
}
|
||||
db eval { SELECT a, b, rowid, langid FROM ft }
|
||||
} [list {*}{
|
||||
a b 22 1
|
||||
a b 23 50
|
||||
a b 24 100
|
||||
a b 25 500
|
||||
}]
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that if writing a hidden virtual table column is an error,
|
||||
# attempting to do so via ota is also an error.
|
||||
#
|
||||
ifcapable fts3 {
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE xt USING fts4(a);
|
||||
}
|
||||
do_test 3.1 {
|
||||
list [catch {
|
||||
apply_ota {
|
||||
CREATE TABLE data_xt(a, xt, ota_rowid, ota_control);
|
||||
INSERT INTO data_xt VALUES('a', 'b', 1, 0);
|
||||
}
|
||||
} msg] $msg
|
||||
} {1 {SQLITE_ERROR - SQL logic error or missing database}}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that it is not possible to violate a NOT NULL constraint by
|
||||
# applying an OTA update.
|
||||
#
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE t2(a INTEGER NOT NULL, b TEXT NOT NULL, c PRIMARY KEY);
|
||||
CREATE TABLE t3(a INTEGER NOT NULL, b TEXT NOT NULL, c INTEGER PRIMARY KEY);
|
||||
CREATE TABLE t4(a, b, PRIMARY KEY(a, b)) WITHOUT ROWID;
|
||||
|
||||
INSERT INTO t2 VALUES(10, 10, 10);
|
||||
INSERT INTO t3 VALUES(10, 10, 10);
|
||||
INSERT INTO t4 VALUES(10, 10);
|
||||
}
|
||||
|
||||
foreach {tn error ota} {
|
||||
2 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t2.a} {
|
||||
INSERT INTO data_t2 VALUES(NULL, 'abc', 1, 0);
|
||||
}
|
||||
3 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t2.b} {
|
||||
INSERT INTO data_t2 VALUES(2, NULL, 1, 0);
|
||||
}
|
||||
4 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t2.c} {
|
||||
INSERT INTO data_t2 VALUES(1, 'abc', NULL, 0);
|
||||
}
|
||||
|
||||
5 {SQLITE_MISMATCH - datatype mismatch} {
|
||||
INSERT INTO data_t3 VALUES(1, 'abc', NULL, 0);
|
||||
}
|
||||
|
||||
6 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t4.b} {
|
||||
INSERT INTO data_t4 VALUES('a', NULL, 0);
|
||||
}
|
||||
7 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t4.a} {
|
||||
INSERT INTO data_t4 VALUES(NULL, 'a', 0);
|
||||
}
|
||||
8 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t2.a} {
|
||||
INSERT INTO data_t2 VALUES(NULL, 0, 10, 'x..');
|
||||
}
|
||||
9 {SQLITE_CONSTRAINT - NOT NULL constraint failed: t3.b} {
|
||||
INSERT INTO data_t3 VALUES(10, NULL, 10, '.x.');
|
||||
}
|
||||
|
||||
10 {SQLITE_MISMATCH - datatype mismatch} {
|
||||
INSERT INTO data_t3 VALUES(1, 'abc', 'text', 0);
|
||||
}
|
||||
} {
|
||||
set ota "
|
||||
CREATE TABLE data_t2(a, b, c, ota_control);
|
||||
CREATE TABLE data_t3(a, b, c, ota_control);
|
||||
CREATE TABLE data_t4(a, b, ota_control);
|
||||
$ota
|
||||
"
|
||||
do_test 4.2.$tn {
|
||||
list [catch { apply_ota $ota } msg] $msg
|
||||
} [list 1 $error]
|
||||
}
|
||||
|
||||
do_test 4.3 {
|
||||
set ota {
|
||||
CREATE TABLE data_t3(a, b, c, ota_control);
|
||||
INSERT INTO data_t3 VALUES(1, 'abc', '5', 0);
|
||||
INSERT INTO data_t3 VALUES(1, 'abc', '-6.0', 0);
|
||||
}
|
||||
list [catch { apply_ota $ota } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# 2015 February 16
|
||||
#
|
||||
# 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 ota11
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that the xAccess() method of an ota vfs handles queries other
|
||||
# than SQLITE_ACCESS_EXISTS correctly. The test code below causes
|
||||
# SQLite to call xAccess(SQLITE_ACCESS_READWRITE) on the directory
|
||||
# path argument passed to "PRAGMA temp_store_directory".
|
||||
#
|
||||
do_test 1.1 {
|
||||
sqlite3ota_create_vfs -default ota ""
|
||||
reset_db
|
||||
catchsql { PRAGMA temp_store_directory = '/no/such/directory' }
|
||||
} {1 {not a writable directory}}
|
||||
|
||||
do_test 1.2 {
|
||||
catchsql " PRAGMA temp_store_directory = '[pwd]' "
|
||||
} {0 {}}
|
||||
|
||||
do_test 1.3 {
|
||||
catchsql " PRAGMA temp_store_directory = '' "
|
||||
} {0 {}}
|
||||
|
||||
do_test 1.4 {
|
||||
db close
|
||||
sqlite3ota_destroy_vfs ota
|
||||
} {}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Try to trick ota into operating on a database opened in wal mode.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
PRAGMA journal_mode = 'wal';
|
||||
CREATE TABLE t2(d PRIMARY KEY, e, f);
|
||||
} {wal}
|
||||
|
||||
do_test 2.2 {
|
||||
db_save
|
||||
db close
|
||||
|
||||
forcedelete ota.db
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval {
|
||||
CREATE TABLE data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(4, 5, 6, 0);
|
||||
INSERT INTO data_t1 VALUES(7, 8, 9, 0);
|
||||
}
|
||||
dbo close
|
||||
|
||||
db_restore
|
||||
hexio_write test.db 18 0101
|
||||
file exists test.db-wal
|
||||
} {1}
|
||||
|
||||
do_test 2.3 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_ERROR}
|
||||
|
||||
do_test 2.4 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - cannot update wal mode database}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test a constraint violation message with an unusual table name.
|
||||
# Specifically, one for which the first character is a codepoint
|
||||
# smaller than 30 (character '0').
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 3.1 {
|
||||
CREATE TABLE "(t1)"(a PRIMARY KEY, b, c);
|
||||
INSERT INTO "(t1)" VALUES(1, 2, 3);
|
||||
INSERT INTO "(t1)" VALUES(4, 5, 6);
|
||||
}
|
||||
db close
|
||||
|
||||
do_test 3.2 {
|
||||
forcedelete ota.db
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval {
|
||||
CREATE TABLE "data_(t1)"(a, b, c, ota_control);
|
||||
INSERT INTO "data_(t1)" VALUES(4, 8, 9, 0);
|
||||
}
|
||||
dbo close
|
||||
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
ota step
|
||||
} {SQLITE_CONSTRAINT}
|
||||
|
||||
do_test 3.3 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 {SQLITE_CONSTRAINT - UNIQUE constraint failed: (t1).a}}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Check that once an OTA update has been applied, attempting to apply
|
||||
# it a second time is a no-op (as the state stored in the OTA database is
|
||||
# "all steps completed").
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1 {
|
||||
CREATE TABLE "(t1)"(a, b, c, PRIMARY KEY(c, b, a));
|
||||
INSERT INTO "(t1)" VALUES(1, 2, 3);
|
||||
INSERT INTO "(t1)" VALUES(4, 5, 6);
|
||||
}
|
||||
db close
|
||||
|
||||
do_test 4.2 {
|
||||
forcedelete ota.db
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval {
|
||||
CREATE TABLE "data_(t1)"(a, b, c, ota_control);
|
||||
INSERT INTO "data_(t1)" VALUES(7, 8, 9, 0);
|
||||
INSERT INTO "data_(t1)" VALUES(1, 2, 3, 1);
|
||||
}
|
||||
dbo close
|
||||
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[ota step]=="SQLITE_OK"} { }
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 4.3 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 4.4 {
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 4.5.1 {
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval { INSERT INTO ota_state VALUES(100, 100) }
|
||||
dbo close
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_CORRUPT}
|
||||
do_test 4.5.2 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 SQLITE_CORRUPT}
|
||||
do_test 4.5.3 {
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval { DELETE FROM ota_state WHERE k = 100 }
|
||||
dbo close
|
||||
} {}
|
||||
|
||||
# Also, check that an invalid state value in the ota_state table is
|
||||
# detected and reported as corruption.
|
||||
do_test 4.6.1 {
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval { UPDATE ota_state SET v = v*-1 WHERE k = 1 }
|
||||
dbo close
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_CORRUPT}
|
||||
do_test 4.6.2 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 SQLITE_CORRUPT}
|
||||
do_test 4.6.3 {
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval { UPDATE ota_state SET v = v*-1 WHERE k = 1 }
|
||||
dbo close
|
||||
} {}
|
||||
|
||||
do_test 4.7.1 {
|
||||
sqlite3 dbo ota.db
|
||||
dbo eval { UPDATE ota_state SET v = 1 WHERE k = 1 }
|
||||
dbo eval { UPDATE ota_state SET v = 'nosuchtable' WHERE k = 2 }
|
||||
dbo close
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_ERROR}
|
||||
do_test 4.7.2 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - ota_state mismatch error}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
# 2015 February 16
|
||||
#
|
||||
# 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
|
||||
source $testdir/lock_common.tcl
|
||||
set ::testprefix ota12
|
||||
|
||||
set setup_sql {
|
||||
DROP TABLE IF EXISTS xx;
|
||||
DROP TABLE IF EXISTS xy;
|
||||
CREATE TABLE xx(a, b, c PRIMARY KEY);
|
||||
INSERT INTO xx VALUES(1, 2, 3);
|
||||
CREATE TABLE xy(a, b, c PRIMARY KEY);
|
||||
|
||||
ATTACH 'ota.db' AS ota;
|
||||
DROP TABLE IF EXISTS data_xx;
|
||||
CREATE TABLE ota.data_xx(a, b, c, ota_control);
|
||||
INSERT INTO data_xx VALUES(4, 5, 6, 0);
|
||||
INSERT INTO data_xx VALUES(7, 8, 9, 0);
|
||||
CREATE TABLE ota.data_xy(a, b, c, ota_control);
|
||||
INSERT INTO data_xy VALUES(10, 11, 12, 0);
|
||||
DETACH ota;
|
||||
}
|
||||
|
||||
do_multiclient_test tn {
|
||||
|
||||
# Initialize a target (test.db) and ota (ota.db) database.
|
||||
#
|
||||
forcedelete ota.db
|
||||
sql1 $setup_sql
|
||||
|
||||
# Using connection 2, open a read transaction on the target database.
|
||||
# OTA will still be able to generate "test.db-oal", but it will not be
|
||||
# able to rename it to "test.db-wal".
|
||||
#
|
||||
do_test 1.$tn.1 {
|
||||
sql2 { BEGIN; SELECT * FROM xx; }
|
||||
} {1 2 3}
|
||||
do_test 1.$tn.2 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while 1 {
|
||||
set res [ota step]
|
||||
if {$res!="SQLITE_OK"} break
|
||||
}
|
||||
set res
|
||||
} {SQLITE_BUSY}
|
||||
|
||||
do_test 1.$tn.3 { sql2 { SELECT * FROM xx; } } {1 2 3}
|
||||
do_test 1.$tn.4 { sql2 { SELECT * FROM xy; } } {}
|
||||
do_test 1.$tn.5 {
|
||||
list [file exists test.db-wal] [file exists test.db-oal]
|
||||
} {0 1}
|
||||
do_test 1.$tn.6 { sql2 COMMIT } {}
|
||||
|
||||
# The ota object that hit the SQLITE_BUSY error above cannot be reused.
|
||||
# It is stuck in a permanent SQLITE_BUSY state at this point.
|
||||
#
|
||||
do_test 1.$tn.7 { ota step } {SQLITE_BUSY}
|
||||
do_test 1.$tn.8 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 SQLITE_BUSY}
|
||||
|
||||
do_test 1.$tn.9.1 { sql2 { BEGIN EXCLUSIVE } } {}
|
||||
do_test 1.$tn.9.2 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.$tn.9.3 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 {SQLITE_BUSY - database is locked}}
|
||||
do_test 1.$tn.9.4 { sql2 COMMIT } {}
|
||||
|
||||
sqlite3ota ota test.db ota.db
|
||||
do_test 1.$tn.10.1 { sql2 { BEGIN EXCLUSIVE } } {}
|
||||
do_test 1.$tn.10.2 {
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.$tn.10.3 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 SQLITE_BUSY}
|
||||
do_test 1.$tn.10.4 { sql2 COMMIT } {}
|
||||
|
||||
# A new ota object can finish the work though.
|
||||
#
|
||||
do_test 1.$tn.11 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_OK}
|
||||
do_test 1.$tn.12 {
|
||||
list [file exists test.db-wal] [file exists test.db-oal]
|
||||
} {1 0}
|
||||
do_test 1.$tn.13 {
|
||||
while 1 {
|
||||
set res [ota step]
|
||||
if {$res!="SQLITE_OK"} break
|
||||
}
|
||||
set res
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 1.$tn.14 {
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
}
|
||||
|
||||
do_multiclient_test tn {
|
||||
|
||||
# Initialize a target (test.db) and ota (ota.db) database.
|
||||
#
|
||||
forcedelete ota.db
|
||||
sql1 $setup_sql
|
||||
|
||||
do_test 2.$tn.1 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[file exists test.db-wal]==0} {
|
||||
if {[ota step]!="SQLITE_OK"} {error "problem here...."}
|
||||
}
|
||||
ota close
|
||||
} {SQLITE_OK}
|
||||
|
||||
|
||||
do_test 2.$tn.2 { sql2 { BEGIN IMMEDIATE } } {}
|
||||
|
||||
do_test 2.$tn.3 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
|
||||
do_test 2.$tn.4 { list [catch { ota close } msg] $msg } {1 SQLITE_BUSY}
|
||||
|
||||
do_test 2.$tn.5 {
|
||||
sql2 { SELECT * FROM xx ; COMMIT }
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
do_test 2.$tn.6 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
ota close
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 2.$tn.7 { sql2 { BEGIN EXCLUSIVE } } {}
|
||||
|
||||
do_test 2.$tn.8 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 2.$tn.9 { list [catch { ota close } msg] $msg } {1 SQLITE_BUSY}
|
||||
do_test 2.$tn.10 {
|
||||
sql2 { SELECT * FROM xx ; COMMIT }
|
||||
} {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
do_test 2.$tn.11 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[ota step]=="SQLITE_OK"} {}
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# 2015 February 16
|
||||
#
|
||||
# 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 an OTA update that features lots of different ota_control strings
|
||||
# for UPDATE statements. This tests OTA's internal UPDATE statement cache.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/lock_common.tcl
|
||||
set ::testprefix ota13
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c, d, e, f, g, h);
|
||||
WITH ii(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM ii WHERE i<127)
|
||||
INSERT INTO t1 SELECT i, 0, 0, 0, 0, 0, 0, 0 FROM ii;
|
||||
}
|
||||
|
||||
forcedelete ota.db
|
||||
do_execsql_test 1.1 {
|
||||
ATTACH 'ota.db' AS ota;
|
||||
CREATE TABLE ota.data_t1(a, b, c, d, e, f, g, h, ota_control);
|
||||
}
|
||||
|
||||
do_test 1.2 {
|
||||
for {set i 0} {$i<128} {incr i} {
|
||||
set control "."
|
||||
for {set bit 6} {$bit>=0} {incr bit -1} {
|
||||
if { $i & (1<<$bit) } {
|
||||
append control "x"
|
||||
} else {
|
||||
append control "."
|
||||
}
|
||||
}
|
||||
execsql { INSERT INTO data_t1 VALUES($i, 1, 1, 1, 1, 1, 1, 1, $control) }
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.3 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while 1 {
|
||||
set rc [ota step]
|
||||
if {$rc!="SQLITE_OK"} break
|
||||
}
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.4 {
|
||||
SELECT count(*) FROM t1 WHERE
|
||||
a == ( (b<<6) + (c<<5) + (d<<4) + (e<<3) + (f<<2) + (g<<1) + (h<<0) )
|
||||
} {128}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
# 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 ota3
|
||||
|
||||
|
||||
# Run the OTA in file $ota on target database $target until completion.
|
||||
#
|
||||
proc run_ota {target ota} {
|
||||
sqlite3ota ota $target $ota
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
}
|
||||
|
||||
forcedelete test.db-oal ota.db
|
||||
db close
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
reset_db
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that for an OTA to be applied, no corruption results if the
|
||||
# affinities on the source and target table do not match.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b TEXT, c REAL);
|
||||
CREATE INDEX i1 ON x1(b, c);
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_x1(a, b, c, ota_control);
|
||||
INSERT INTO data_x1 VALUES(1, '123', '123', 0);
|
||||
INSERT INTO data_x1 VALUES(2, 123, 123, 0);
|
||||
}
|
||||
db2 close
|
||||
run_ota test.db ota.db
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.2 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that NULL values may not be inserted into INTEGER PRIMARY KEY
|
||||
# columns.
|
||||
#
|
||||
forcedelete ota.db
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b TEXT, c REAL);
|
||||
CREATE INDEX i1 ON x1(b, c);
|
||||
} {}
|
||||
|
||||
foreach {tn otadb} {
|
||||
1 {
|
||||
CREATE TABLE data_x1(a, b, c, ota_control);
|
||||
INSERT INTO data_x1 VALUES(NULL, 'a', 'b', 0);
|
||||
}
|
||||
|
||||
2 {
|
||||
CREATE TABLE data_x1(c, b, a, ota_control);
|
||||
INSERT INTO data_x1 VALUES('b', 'a', NULL, 0);
|
||||
}
|
||||
} {
|
||||
do_test 2.$tn.1 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval $otadb
|
||||
db2 close
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} {1 {SQLITE_MISMATCH - datatype mismatch}}
|
||||
|
||||
do_execsql_test 2.1.2 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Test that missing columns are detected.
|
||||
#
|
||||
forcedelete ota.db
|
||||
reset_db
|
||||
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1 ON x1(b, c);
|
||||
} {}
|
||||
|
||||
do_test 2.1 {
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_x1(a, b, ota_control);
|
||||
INSERT INTO data_x1 VALUES(1, 'a', 0);
|
||||
}
|
||||
db2 close
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} {1 {SQLITE_ERROR - column missing from data_x1: c}}
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
# Also extra columns.
|
||||
#
|
||||
do_execsql_test 2.3 {
|
||||
CREATE TABLE x2(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i2 ON x2(b, c);
|
||||
} {}
|
||||
|
||||
do_test 2.4 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_x2(a, b, c, d, ota_control);
|
||||
INSERT INTO data_x2 VALUES(1, 'a', 2, 3, 0);
|
||||
}
|
||||
db2 close
|
||||
list [catch { run_ota test.db ota.db } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_execsql_test 2.5 {
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3ota_create_vfs() returns an error if the requested
|
||||
# parent VFS is unknown.
|
||||
#
|
||||
# And that nothing disasterous happens if a VFS name passed to
|
||||
# sqlite3ota_destroy_vfs() is unknown or not an OTA vfs.
|
||||
#
|
||||
do_test 3.1 {
|
||||
list [catch {sqlite3ota_create_vfs xyz nosuchparent} msg] $msg
|
||||
} {1 SQLITE_NOTFOUND}
|
||||
|
||||
do_test 3.2 {
|
||||
sqlite3ota_destroy_vfs nosuchvfs
|
||||
sqlite3ota_destroy_vfs unix
|
||||
sqlite3ota_destroy_vfs win32
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that it is an error to specify an explicit VFS that does not
|
||||
# include ota VFS functionality.
|
||||
#
|
||||
do_test 4.1 {
|
||||
testvfs tvfs
|
||||
sqlite3ota ota file:test.db?vfs=tvfs ota.db
|
||||
list [catch { ota step } msg] $msg
|
||||
} {0 SQLITE_ERROR}
|
||||
do_test 4.2 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - ota vfs not found}}
|
||||
tvfs delete
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test a large ota update to ensure that wal_autocheckpoint does not get
|
||||
# in the way.
|
||||
#
|
||||
forcedelete ota.db
|
||||
reset_db
|
||||
do_execsql_test 5.1 {
|
||||
CREATE TABLE x1(a, b, c, PRIMARY KEY(a)) WITHOUT ROWID;
|
||||
CREATE INDEX i1 ON x1(a);
|
||||
|
||||
ATTACH 'ota.db' AS ota;
|
||||
CREATE TABLE ota.data_x1(a, b, c, ota_control);
|
||||
WITH s(a, b, c) AS (
|
||||
SELECT randomblob(300), randomblob(300), 1
|
||||
UNION ALL
|
||||
SELECT randomblob(300), randomblob(300), c+1 FROM s WHERE c<2000
|
||||
)
|
||||
INSERT INTO data_x1 SELECT a, b, c, 0 FROM s;
|
||||
}
|
||||
|
||||
do_test 5.2 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[ota step]=="SQLITE_OK" && [file exists test.db-wal]==0} {}
|
||||
ota close
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_test 5.3 {
|
||||
expr {[file size test.db-wal] > (1024 * 1200)}
|
||||
} 1
|
||||
|
||||
do_test 6.1 { sqlite3ota_internal_test } {}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
# 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 ota5
|
||||
|
||||
|
||||
# Run the OTA in file $ota on target database $target until completion.
|
||||
#
|
||||
proc run_ota {target ota} {
|
||||
sqlite3ota ota $target $ota
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
}
|
||||
|
||||
|
||||
# Run the OTA in file $ota on target database $target one step at a
|
||||
# time until completion.
|
||||
#
|
||||
proc step_ota {target ota} {
|
||||
while 1 {
|
||||
sqlite3ota ota $target $ota
|
||||
set rc [ota step]
|
||||
ota close
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
}
|
||||
set rc
|
||||
}
|
||||
|
||||
# Return a list of the primary key columns for table $tbl in the database
|
||||
# opened by database handle $db.
|
||||
#
|
||||
proc pkcols {db tbl} {
|
||||
set ret [list]
|
||||
$db eval "PRAGMA table_info = '$tbl'" {
|
||||
if {$pk} { lappend ret $name }
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Return a list of all columns for table $tbl in the database opened by
|
||||
# database handle $db.
|
||||
#
|
||||
proc allcols {db tbl} {
|
||||
set ret [list]
|
||||
$db eval "PRAGMA table_info = '$tbl'" {
|
||||
lappend ret $name
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Return a checksum on all tables and data in the main database attached
|
||||
# to database handle $db. It is possible to add indexes without changing
|
||||
# the checksum.
|
||||
#
|
||||
proc datacksum {db} {
|
||||
|
||||
$db eval { SELECT name FROM sqlite_master WHERE type='table' } {
|
||||
append txt $name
|
||||
set cols [list]
|
||||
set order [list]
|
||||
set cnt 0
|
||||
$db eval "PRAGMA table_info = $name" x {
|
||||
lappend cols "quote($x(name))"
|
||||
lappend order [incr cnt]
|
||||
}
|
||||
set cols [join $cols ,]
|
||||
set order [join $order ,]
|
||||
append txt [$db eval "SELECT $cols FROM $name ORDER BY $order"]
|
||||
}
|
||||
return "[string length $txt]-[md5 $txt]"
|
||||
}
|
||||
|
||||
proc ucontrol {args} {
|
||||
set ret ""
|
||||
foreach a $args {
|
||||
if {$a} {
|
||||
append ret .
|
||||
} else {
|
||||
append ret x
|
||||
}
|
||||
}
|
||||
return $ret
|
||||
}
|
||||
|
||||
# Argument $target is the name of an SQLite database file. $sql is an SQL
|
||||
# script containing INSERT, UPDATE and DELETE statements to execute against
|
||||
# it. This command creates an OTA update database in file $ota that has
|
||||
# the same effect as the script. The target database is not modified by
|
||||
# this command.
|
||||
#
|
||||
proc generate_ota_db {target ota sql} {
|
||||
|
||||
forcedelete $ota
|
||||
forcecopy $target copy.db
|
||||
|
||||
# Evaluate the SQL script to modify the contents of copy.db.
|
||||
#
|
||||
sqlite3 dbOta copy.db
|
||||
dbOta eval $sql
|
||||
|
||||
dbOta function ucontrol ucontrol
|
||||
|
||||
# Evaluate the SQL script to modify the contents of copy.db.
|
||||
set ret [datacksum dbOta]
|
||||
|
||||
dbOta eval { ATTACH $ota AS ota }
|
||||
dbOta eval { ATTACH $target AS orig }
|
||||
|
||||
dbOta eval { SELECT name AS tbl FROM sqlite_master WHERE type = 'table' } {
|
||||
set pk [pkcols dbOta $tbl]
|
||||
set cols [allcols dbOta $tbl]
|
||||
|
||||
# A WHERE clause to test that the PK columns match.
|
||||
#
|
||||
set where [list]
|
||||
foreach c $pk { lappend where "main.$tbl.$c IS orig.$tbl.$c" }
|
||||
set where [join $where " AND "]
|
||||
|
||||
# A WHERE clause to test that all columns match.
|
||||
#
|
||||
set where2 [list]
|
||||
foreach c $cols { lappend where2 "main.$tbl.$c IS orig.$tbl.$c" }
|
||||
set ucontrol "ucontrol([join $where2 ,])"
|
||||
set where2 [join $where2 " AND "]
|
||||
|
||||
# Create a data_xxx table in the OTA update database.
|
||||
dbOta eval "
|
||||
CREATE TABLE ota.data_$tbl AS SELECT *, '' AS ota_control
|
||||
FROM main.$tbl LIMIT 0
|
||||
"
|
||||
|
||||
# Find all new rows INSERTed by the script.
|
||||
dbOta eval "
|
||||
INSERT INTO ota.data_$tbl
|
||||
SELECT *, 0 AS ota_control FROM main.$tbl
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM orig.$tbl WHERE $where
|
||||
)
|
||||
"
|
||||
|
||||
# Find all old rows DELETEd by the script.
|
||||
dbOta eval "
|
||||
INSERT INTO ota.data_$tbl
|
||||
SELECT *, 1 AS ota_control FROM orig.$tbl
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM main.$tbl WHERE $where
|
||||
)
|
||||
"
|
||||
|
||||
# Find all rows UPDATEd by the script.
|
||||
set origcols [list]
|
||||
foreach c $cols { lappend origcols "main.$tbl.$c" }
|
||||
set origcols [join $origcols ,]
|
||||
dbOta eval "
|
||||
INSERT INTO ota.data_$tbl
|
||||
SELECT $origcols, $ucontrol AS ota_control
|
||||
FROM orig.$tbl, main.$tbl
|
||||
WHERE $where AND NOT ($where2)
|
||||
"
|
||||
|
||||
}
|
||||
|
||||
dbOta close
|
||||
forcedelete copy.db
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(x, y, z, PRIMARY KEY(y, z)) WITHOUT ROWID;
|
||||
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(2, 4, 6);
|
||||
INSERT INTO t1 VALUES(3, 6, 9);
|
||||
|
||||
INSERT INTO t2 VALUES(1, 2, 3);
|
||||
INSERT INTO t2 VALUES(2, 4, 6);
|
||||
INSERT INTO t2 VALUES(3, 6, 9);
|
||||
}
|
||||
db close
|
||||
|
||||
set cksum [generate_ota_db test.db ota.db {
|
||||
INSERT INTO t1 VALUES(4, 8, 12);
|
||||
DELETE FROM t1 WHERE a = 2;
|
||||
UPDATE t1 SET c = 15 WHERE a=3;
|
||||
|
||||
INSERT INTO t2 VALUES(4, 8, 12);
|
||||
DELETE FROM t2 WHERE x = 2;
|
||||
UPDATE t2 SET x = 15 WHERE z=9;
|
||||
}]
|
||||
|
||||
foreach {tn idx} {
|
||||
1 {
|
||||
}
|
||||
2 {
|
||||
CREATE INDEX i1 ON t1(a, b, c);
|
||||
CREATE INDEX i2 ON t2(x, y, z);
|
||||
}
|
||||
} {
|
||||
foreach cmd {run step} {
|
||||
forcecopy test.db test.db2
|
||||
forcecopy ota.db ota.db2
|
||||
|
||||
sqlite3 db test.db2
|
||||
db eval $idx
|
||||
|
||||
do_test 1.$tn.$cmd.1 {
|
||||
${cmd}_ota test.db2 ota.db2
|
||||
datacksum db
|
||||
} $cksum
|
||||
|
||||
do_test 1.$tn.$cmd.2 {
|
||||
db eval { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d, e);
|
||||
INSERT INTO t1 VALUES(-750250,'fyetckfaagjkzqjx',-185831,X'FEAD',444258.29);
|
||||
INSERT INTO t1 VALUES(649081,NULL,X'7DF25BF78778',-342324.63,'akvspktocwozo');
|
||||
INSERT INTO t1 VALUES(-133045,-44822.31,X'',287935,NULL);
|
||||
INSERT INTO t1 VALUES(202132,NULL,X'5399','cujsjtspryqeyovcdpz','m');
|
||||
INSERT INTO t1 VALUES(302910,NULL,'dvdhivtfkaedzhdcnn',-717113.41,688487);
|
||||
INSERT INTO t1 VALUES(-582327,X'7A267A',X'7E6B3CFE5CB9','zacuzilrok',-196478);
|
||||
INSERT INTO t1 VALUES(-190462,X'D1A087E7D68D9578','lsmleti',NULL,-928094);
|
||||
INSERT INTO t1 VALUES(-467665,176344.57,-536684.23,828876.22,X'903E');
|
||||
INSERT INTO t1 VALUES(-629138,632630.29,X'28D6',-774501,X'819BBBFC65');
|
||||
INSERT INTO t1 VALUES(-828110,-54379.24,-881121.44,X'',X'8D5A894F0D');
|
||||
|
||||
CREATE TABLE t2(a PRIMARY KEY, b, c, d, e) WITHOUT ROWID;
|
||||
INSERT INTO t2 VALUES(-65174,X'AC1DBFFE27310F',-194471.08,347988,X'84041BA6F9BDDE86A8');
|
||||
INSERT INTO t2 VALUES('bzbpi',-952693.69,811628.25,NULL,-817434);
|
||||
INSERT INTO t2 VALUES(-643830,NULL,'n',NULL,'dio');
|
||||
INSERT INTO t2 VALUES('rovoenxxj',NULL,'owupbtdcoxxnvg',-119676,X'55431DFA');
|
||||
INSERT INTO t2 VALUES(899770,'jlygdl',X'DBCA4D1A',NULL,-631773);
|
||||
INSERT INTO t2 VALUES(334698.80,NULL,-697585.58,-89277,-817352);
|
||||
INSERT INTO t2 VALUES(X'1A9EB7547A4AAF38','aiprdhkpzdz','anw','szvjbwdvzucybpwwqjt',X'53');
|
||||
INSERT INTO t2 VALUES(713220,NULL,'hfcqhqzjuqplvkum',X'20B076075649DE','fthgpvqdyy');
|
||||
INSERT INTO t2 VALUES(763908,NULL,'xgslzcpvwfknbr',X'75',X'668146');
|
||||
INSERT INTO t2 VALUES(X'E1BA2B6BA27278','wjbpd',NULL,139341,-290086.15);
|
||||
}
|
||||
db close
|
||||
|
||||
set cksum [generate_ota_db test.db ota.db {
|
||||
INSERT INTO t2 VALUES(222916.23,'idh',X'472C517405',X'E3',X'7C4F31824669');
|
||||
INSERT INTO t2 VALUES('xcndjwafcoxwxizoktd',-319567.21,NULL,-720906.43,-577170);
|
||||
INSERT INTO t2 VALUES(376369.99,-536058,'yoaiurfqupdscwc',X'29EC8A2542EC3953E9',-740485.22);
|
||||
INSERT INTO t2 VALUES(X'0EFB4DC50693',-175590.83,X'1779E253CAB5B1789E',X'BC6903',NULL);
|
||||
INSERT INTO t2 VALUES(-288299,'hfrp',NULL,528477,730676.77);
|
||||
DELETE FROM t2 WHERE a < -60000;
|
||||
|
||||
UPDATE t2 SET b = 'pgnnaaoflnw' WHERE a = 'bzbpi';
|
||||
UPDATE t2 SET c = -675583 WHERE a = 'rovoenxxj';
|
||||
UPDATE t2 SET d = X'09CDF2B2C241' WHERE a = 713220;
|
||||
|
||||
INSERT INTO t1 VALUES(224938,'bmruycvfznhhnfmgqys','fr',854381,789143);
|
||||
INSERT INTO t1 VALUES(-863931,-1386.26,X'2A058540C2FB5C',NULL,X'F9D5990A');
|
||||
INSERT INTO t1 VALUES(673696,X'97301F0AC5735F44B5',X'440C',227999.92,-709599.79);
|
||||
INSERT INTO t1 VALUES(-243640,NULL,-71718.11,X'1EEFEB38',X'8CC7C55D95E142FBA5');
|
||||
INSERT INTO t1 VALUES(275893,X'',375606.30,X'0AF9EC334711FB',-468194);
|
||||
DELETE FROM t1 WHERE a > 200000;
|
||||
|
||||
UPDATE t1 SET b = 'pgnnaaoflnw' WHERE a = -190462;
|
||||
UPDATE t1 SET c = -675583 WHERE a = -467665;
|
||||
UPDATE t1 SET d = X'09CDF2B2C241' WHERE a = -133045;
|
||||
|
||||
}]
|
||||
|
||||
foreach {tn idx} {
|
||||
1 {
|
||||
}
|
||||
2 {
|
||||
CREATE UNIQUE INDEX i1 ON t1(b, c, d);
|
||||
CREATE UNIQUE INDEX i2 ON t1(d, e, a);
|
||||
CREATE UNIQUE INDEX i3 ON t1(e, d, c, b);
|
||||
|
||||
CREATE UNIQUE INDEX i4 ON t2(b, c, d);
|
||||
CREATE UNIQUE INDEX i5 ON t2(d, e, a);
|
||||
CREATE UNIQUE INDEX i6 ON t2(e, d, c, b);
|
||||
}
|
||||
} {
|
||||
foreach cmd {run step} {
|
||||
forcecopy test.db test.db2
|
||||
forcecopy ota.db ota.db2
|
||||
|
||||
sqlite3 db test.db2
|
||||
db eval $idx
|
||||
|
||||
do_test 2.$tn.$cmd.1 {
|
||||
${cmd}_ota test.db2 ota.db2
|
||||
datacksum db
|
||||
} $cksum
|
||||
|
||||
do_test 2.$tn.$cmd.2 {
|
||||
db eval { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
# 2014 October 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the OTA module. Specifically, it tests the
|
||||
# outcome of some other client writing to the database while an OTA update
|
||||
# is being applied.
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix ota6
|
||||
|
||||
proc setup_test {} {
|
||||
reset_db
|
||||
execsql {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b UNIQUE);
|
||||
}
|
||||
db close
|
||||
|
||||
forcedelete ota.db
|
||||
sqlite3 ota ota.db
|
||||
ota eval {
|
||||
CREATE TABLE data_t1(a, b, ota_control);
|
||||
CREATE TABLE data_t2(a, b, ota_control);
|
||||
CREATE TABLE data_t3(a, b, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, 't1', 0);
|
||||
INSERT INTO data_t2 VALUES(2, 't2', 0);
|
||||
INSERT INTO data_t3 VALUES(3, 't3', 0);
|
||||
}
|
||||
ota close
|
||||
}
|
||||
|
||||
# Test the outcome of some other client writing the db while the *-oal
|
||||
# file is being generated. Once this has happened, the update cannot be
|
||||
# progressed.
|
||||
#
|
||||
for {set nStep 1} {$nStep < 8} {incr nStep} {
|
||||
do_test 1.$nStep.1 {
|
||||
setup_test
|
||||
sqlite3ota ota test.db ota.db
|
||||
for {set i 0} {$i<$nStep} {incr i} {ota step}
|
||||
|
||||
ota close
|
||||
sqlite3 db test.db
|
||||
execsql { INSERT INTO t1 VALUES(5, 'hello') }
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.$nStep.2 {
|
||||
ota step
|
||||
} {SQLITE_BUSY}
|
||||
do_test 1.$nStep.3 {
|
||||
list [file exists test.db-oal] [file exists test.db-wal]
|
||||
} {1 0}
|
||||
do_test 1.$nStep.4 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {1 {SQLITE_BUSY - database modified during ota update}}
|
||||
}
|
||||
|
||||
# Test the outcome of some other client writing the db after the *-oal
|
||||
# file has been copied to the *-wal path. Once this has happened, any
|
||||
# other client writing to the db causes OTA to consider its job finished.
|
||||
#
|
||||
for {set nStep 8} {$nStep < 20} {incr nStep} {
|
||||
do_test 1.$nStep.1 {
|
||||
setup_test
|
||||
sqlite3ota ota test.db ota.db
|
||||
for {set i 0} {$i<$nStep} {incr i} {ota step}
|
||||
ota close
|
||||
sqlite3 db test.db
|
||||
execsql { INSERT INTO t1 VALUES(5, 'hello') }
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_DONE}
|
||||
do_test 1.$nStep.2 {
|
||||
ota step
|
||||
} {SQLITE_DONE}
|
||||
do_test 1.$nStep.3 {
|
||||
file exists test.db-oal
|
||||
} {0}
|
||||
do_test 1.$nStep.4 {
|
||||
list [catch { ota close } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.$nStep.5 {
|
||||
SELECT * FROM t1;
|
||||
} {1 t1 5 hello}
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
# 2014 October 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# This file contains tests for the OTA module.
|
||||
#
|
||||
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix ota7
|
||||
|
||||
# Test index:
|
||||
#
|
||||
# 1.*: That affinities are correctly applied to values within the
|
||||
# OTA database.
|
||||
#
|
||||
# 2.*: Tests for multi-column primary keys.
|
||||
#
|
||||
|
||||
do_test 1.0 {
|
||||
execsql {
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 'abc');
|
||||
INSERT INTO t1 VALUES(2, 'def');
|
||||
}
|
||||
|
||||
forcedelete ota.db
|
||||
sqlite3 ota ota.db
|
||||
ota eval {
|
||||
CREATE TABLE data_t1(a, b, ota_control);
|
||||
INSERT INTO data_t1 VALUES('1', NULL, 1);
|
||||
}
|
||||
ota close
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 1.2 {
|
||||
SELECT * FROM t1
|
||||
} {2 def}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn tbl} {
|
||||
1 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID }
|
||||
2 { CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b)) }
|
||||
} {
|
||||
reset_db
|
||||
|
||||
execsql $tbl
|
||||
do_execsql_test 2.$tn.1 {
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
INSERT INTO t1 VALUES(1, 1, 'a');
|
||||
INSERT INTO t1 VALUES(1, 2, 'b');
|
||||
INSERT INTO t1 VALUES(2, 1, 'c');
|
||||
INSERT INTO t1 VALUES(2, 2, 'd');
|
||||
}
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
forcedelete ota.db
|
||||
sqlite3 ota ota.db
|
||||
execsql {
|
||||
CREATE TABLE data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(3, 1, 'e', 0);
|
||||
INSERT INTO data_t1 VALUES(3, 2, 'f', 0);
|
||||
INSERT INTO data_t1 VALUES(1, 2, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(2, 1, 'X', '..x');
|
||||
} ota
|
||||
ota close
|
||||
} {}
|
||||
|
||||
do_test 2.$tn.3 {
|
||||
set rc "SQLITE_OK"
|
||||
while {$rc == "SQLITE_OK"} {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
set rc [ota close]
|
||||
}
|
||||
set rc
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 2.$tn.1 {
|
||||
SELECT * FROM t1 ORDER BY a, b
|
||||
} {
|
||||
1 1 a
|
||||
2 1 X
|
||||
2 2 d
|
||||
3 1 e
|
||||
3 2 f
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# 2014 November 20
|
||||
#
|
||||
# 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 the ota_delta() feature.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix ota8
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x, y PRIMARY KEY, z);
|
||||
INSERT INTO t1 VALUES(NULL, 1, 'one');
|
||||
INSERT INTO t1 VALUES(NULL, 2, 'two');
|
||||
INSERT INTO t1 VALUES(NULL, 3, 'three');
|
||||
CREATE INDEX i1z ON t1(z, x);
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_t1(x, y, z, ota_control);
|
||||
INSERT INTO data_t1 VALUES('a', 1, '_i' , 'x.d');
|
||||
INSERT INTO data_t1 VALUES('b', 2, 2 , '..x');
|
||||
INSERT INTO data_t1 VALUES('_iii', 3, '-III' , 'd.d');
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test 1.2.1 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
} {SQLITE_ERROR}
|
||||
do_test 1.2.2 {
|
||||
list [catch {ota close} msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such function: ota_delta}}
|
||||
|
||||
proc ota_delta {orig new} {
|
||||
return "${orig}${new}"
|
||||
}
|
||||
|
||||
do_test 1.3.1 {
|
||||
while 1 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota create_ota_delta
|
||||
set rc [ota step]
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
ota close
|
||||
}
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.3.2 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
a 1 one_i
|
||||
{} 2 2
|
||||
_iii 3 three-III
|
||||
}
|
||||
integrity_check 1.3.3
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# 2014 November 21
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test OTA with virtual tables. And tables with no PRIMARY KEY declarations.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix ota9
|
||||
|
||||
ifcapable !fts3 {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
do_execsql_test 1.1 {
|
||||
CREATE VIRTUAL TABLE f1 USING fts4(a, b, c);
|
||||
INSERT INTO f1(rowid, a, b, c) VALUES(11, 'a', 'b', 'c');
|
||||
INSERT INTO f1(rowid, a, b, c) VALUES(12, 'd', 'e', 'f');
|
||||
INSERT INTO f1(rowid, a, b, c) VALUES(13, 'g', 'h', 'i');
|
||||
}
|
||||
|
||||
do_test 1.1 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_f1(ota_rowid, a, b, c, ota_control);
|
||||
INSERT INTO data_f1 VALUES(14, 'x', 'y', 'z', 0); -- INSERT
|
||||
INSERT INTO data_f1 VALUES(11, NULL, NULL, NULL, 1); -- DELETE
|
||||
INSERT INTO data_f1 VALUES(13, NULL, NULL, 'X', '..x'); -- UPDATE
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test 1.2.1 {
|
||||
while 1 {
|
||||
sqlite3ota ota test.db ota.db
|
||||
set rc [ota step]
|
||||
if {$rc != "SQLITE_OK"} break
|
||||
ota close
|
||||
}
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_execsql_test 1.2.2 { SELECT rowid, * FROM f1 } {
|
||||
12 d e f
|
||||
13 g h X
|
||||
14 x y z
|
||||
}
|
||||
do_execsql_test 1.2.3 { INSERT INTO f1(f1) VALUES('integrity-check') }
|
||||
integrity_check 1.2.4
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Tables with no PK declaration.
|
||||
#
|
||||
|
||||
# Run the OTA in file $ota on target database $target until completion.
|
||||
#
|
||||
proc run_ota {target ota} {
|
||||
sqlite3ota ota $target $ota
|
||||
while { [ota step]=="SQLITE_OK" } {}
|
||||
ota close
|
||||
}
|
||||
|
||||
foreach {tn idx} {
|
||||
1 { }
|
||||
2 {
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
}
|
||||
3 {
|
||||
CREATE INDEX i1 ON t1(b, c);
|
||||
CREATE INDEX i2 ON t1(c, b);
|
||||
CREATE INDEX i3 ON t1(a, a, a, b, b, b, c, c, c);
|
||||
}
|
||||
} {
|
||||
|
||||
reset_db
|
||||
do_execsql_test 2.$tn.1 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1(rowid, a, b, c) VALUES(-1, 'a', 'b', 'c');
|
||||
INSERT INTO t1(rowid, a, b, c) VALUES(-2, 'd', 'e', 'f');
|
||||
}
|
||||
|
||||
db eval $idx
|
||||
|
||||
do_test 2.$tn.2 {
|
||||
forcedelete ota.db
|
||||
sqlite3 db2 ota.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_t1(ota_rowid, a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(3, 'x', 'y', 'z', 0);
|
||||
INSERT INTO data_t1 VALUES(NULL, 'X', 'Y', 'Z', 0);
|
||||
INSERT INTO data_t1 VALUES('1', NULL, NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(-2, NULL, NULL, 'fff', '..x');
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
run_ota test.db ota.db
|
||||
|
||||
do_execsql_test 2.$tn.3 {
|
||||
SELECT rowid, a, b, c FROM t1 ORDER BY rowid;
|
||||
} {
|
||||
-2 d e fff
|
||||
-1 a b c
|
||||
2 4 5 6
|
||||
3 x y z
|
||||
4 X Y Z
|
||||
}
|
||||
|
||||
integrity_check 2.$tn.4
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# 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 {
|
||||
db close
|
||||
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
|
||||
breakpoint
|
||||
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
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
# 2014 October 22
|
||||
#
|
||||
# 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 otacrash
|
||||
|
||||
db close
|
||||
forcedelete test.db-oal ota.db
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_uri 1
|
||||
reset_db
|
||||
|
||||
# Set up a target database and an ota update database. The target
|
||||
# db is the usual "test.db", the ota db is "test.db2".
|
||||
#
|
||||
forcedelete test.db2
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a), UNIQUE(b));
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
|
||||
ATTACH 'test.db2' AS ota;
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(10, 11, 12, 0);
|
||||
INSERT INTO data_t1 VALUES(13, 14, 15, 0);
|
||||
INSERT INTO data_t1 VALUES(4, NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(1, NULL, 100, '..x');
|
||||
}
|
||||
db_save_and_close
|
||||
|
||||
|
||||
# Determine the number of steps in applying the ota update to the test
|
||||
# target database created above. Set $::ota_num_steps accordingly
|
||||
#
|
||||
# Check that the same number of steps are required to apply the ota
|
||||
# update using many calls to sqlite3ota_step() on a single ota handle
|
||||
# as required to apply it using a series of ota handles, on each of
|
||||
# which sqlite3ota_step() is called once.
|
||||
#
|
||||
do_test 1.1 {
|
||||
db_restore
|
||||
sqlite3ota ota test.db test.db2
|
||||
breakpoint
|
||||
set nStep 0
|
||||
while {[ota step]=="SQLITE_OK"} { incr nStep }
|
||||
ota close
|
||||
} {SQLITE_DONE}
|
||||
set ota_num_steps $nStep
|
||||
do_test 1.2 {
|
||||
db_restore
|
||||
set nStep 0
|
||||
while {1} {
|
||||
sqlite3ota ota test.db test.db2
|
||||
ota step
|
||||
if {[ota close]=="SQLITE_DONE"} break
|
||||
incr nStep
|
||||
}
|
||||
set nStep
|
||||
} $ota_num_steps
|
||||
|
||||
|
||||
# Run one or more tests using the target (test.db) and ota (test.db2)
|
||||
# databases created above. As follows:
|
||||
#
|
||||
# 1. This process starts the ota update and calls sqlite3ota_step()
|
||||
# $nPre times. Then closes the ota update handle.
|
||||
#
|
||||
# 2. A second process resumes the ota update and attempts to call
|
||||
# sqlite3ota_step() $nStep times before closing the handle. A
|
||||
# crash is simulated during each xSync() of file test.db2.
|
||||
#
|
||||
# 3. This process attempts to resume the ota update from whatever
|
||||
# state it was left in by step (2). Test that it is successful
|
||||
# in doing so and that the final target database is as expected.
|
||||
#
|
||||
# In total (nSync+1) tests are run, where nSync is the number of times
|
||||
# xSync() is called on test.db2.
|
||||
#
|
||||
proc do_ota_crash_test {tn nPre nStep} {
|
||||
|
||||
set script [subst -nocommands {
|
||||
sqlite3ota ota test.db file:test.db2?vfs=crash
|
||||
set i 0
|
||||
while {[set i] < $nStep} {
|
||||
if {[ota step]!="SQLITE_OK"} break
|
||||
incr i
|
||||
}
|
||||
ota close
|
||||
}]
|
||||
|
||||
set bDone 0
|
||||
for {set iDelay 1} {$bDone==0} {incr iDelay} {
|
||||
forcedelete test.db2 test.db2-journal test.db test.db-oal test.db-wal
|
||||
db_restore
|
||||
|
||||
if {$nPre>0} {
|
||||
sqlite3ota ota test.db file:test.db2
|
||||
set i 0
|
||||
for {set i 0} {$i < $nPre} {incr i} {
|
||||
if {[ota step]!="SQLITE_OK"} break
|
||||
}
|
||||
ota close
|
||||
}
|
||||
|
||||
set res [
|
||||
crashsql -file test.db2 -delay $iDelay -tclbody $script -opendb {} {}
|
||||
]
|
||||
|
||||
set bDone 1
|
||||
if {$res == "1 {child process exited abnormally}"} {
|
||||
set bDone 0
|
||||
} elseif {$res != "0 {}"} {
|
||||
error "unexected catchsql result: $res"
|
||||
}
|
||||
|
||||
sqlite3ota ota test.db test.db2
|
||||
while {[ota step]=="SQLITE_OK"} {}
|
||||
ota close
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test $tn.delay=$iDelay {
|
||||
SELECT * FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
} {1 2 100 7 8 9 10 11 12 13 14 15 ok}
|
||||
db close
|
||||
}
|
||||
}
|
||||
|
||||
for {set nPre 0} {$nPre < $ota_num_steps} {incr nPre} {
|
||||
for {set is 1} {$is <= ($ota_num_steps - $nPre)} {incr is} {
|
||||
do_ota_crash_test 2.pre=$nPre.step=$is $nPre $is
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
# 2014 October 22
|
||||
#
|
||||
# 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
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix otafault
|
||||
|
||||
proc copy_if_exists {src target} {
|
||||
if {[file exists $src]} {
|
||||
forcecopy $src $target
|
||||
} else {
|
||||
forcedelete $target
|
||||
}
|
||||
}
|
||||
|
||||
foreach {tn2 setup sql expect} {
|
||||
1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX t1cb ON t1(c, b);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(2, NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES(3, 'three', NULL, '.x.');
|
||||
INSERT INTO data_t1 VALUES(4, 4, 4, 0);
|
||||
} {
|
||||
SELECT * FROM t1
|
||||
} {1 1 1 3 three 3 4 4 4}
|
||||
|
||||
2 {
|
||||
CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
CREATE INDEX t2cb ON t2(c, b);
|
||||
INSERT INTO t2 VALUES('a', 'a', 'a');
|
||||
INSERT INTO t2 VALUES('b', 'b', 'b');
|
||||
INSERT INTO t2 VALUES('c', 'c', 'c');
|
||||
|
||||
CREATE TABLE ota.data_t2(a, b, c, ota_control);
|
||||
INSERT INTO data_t2 VALUES('b', NULL, NULL, 1);
|
||||
INSERT INTO data_t2 VALUES('c', 'see', NULL, '.x.');
|
||||
INSERT INTO data_t2 VALUES('d', 'd', 'd', 0);
|
||||
} {
|
||||
SELECT * FROM t2
|
||||
} {a a a c see c d d d}
|
||||
|
||||
3 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
CREATE INDEX t1cb ON t1(c, b);
|
||||
CREATE INDEX t2cb ON t2(c, b);
|
||||
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_control);
|
||||
CREATE TABLE ota.data_t2(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES(1, 2, 3, 0);
|
||||
INSERT INTO data_t2 VALUES(4, 5, 6, 0);
|
||||
} {
|
||||
SELECT * FROM t1 UNION ALL SELECT * FROM t2
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
4 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
INSERT INTO t1 VALUES('A', 'B', 'C');
|
||||
INSERT INTO t1 VALUES('D', 'E', 'F');
|
||||
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_control);
|
||||
INSERT INTO data_t1 VALUES('D', NULL, NULL, 1);
|
||||
INSERT INTO data_t1 VALUES('A', 'Z', NULL, '.x.');
|
||||
INSERT INTO data_t1 VALUES('G', 'H', 'I', 0);
|
||||
} {
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
} {A Z C G H I}
|
||||
|
||||
5 {
|
||||
CREATE TABLE t1(a, b, c);
|
||||
CREATE INDEX t1c ON t1(c, b);
|
||||
|
||||
CREATE TABLE ota.data_t1(a, b, c, ota_rowid, ota_control);
|
||||
INSERT INTO data_t1 VALUES('a', 'b', 'c', 1, 0);
|
||||
INSERT INTO data_t1 VALUES('d', 'e', 'f', '2', 0);
|
||||
} {
|
||||
SELECT * FROM t1 ORDER BY a;
|
||||
} {a b c d e f}
|
||||
|
||||
} {
|
||||
catch {db close}
|
||||
forcedelete ota.db test.db
|
||||
sqlite3 db test.db
|
||||
execsql {
|
||||
PRAGMA encoding = utf16;
|
||||
ATTACH 'ota.db' AS ota;
|
||||
}
|
||||
execsql $setup
|
||||
db close
|
||||
|
||||
forcecopy test.db test.db.bak
|
||||
forcecopy ota.db ota.db.bak
|
||||
|
||||
foreach {tn f reslist} {
|
||||
1 oom-tra* {
|
||||
{0 SQLITE_DONE}
|
||||
{1 {SQLITE_NOMEM - out of memory}}
|
||||
{1 SQLITE_NOMEM}
|
||||
{1 SQLITE_IOERR_NOMEM}
|
||||
{1 {SQLITE_NOMEM - unable to open a temporary database file for storing temporary tables}}
|
||||
}
|
||||
|
||||
2 ioerr-* {
|
||||
{0 SQLITE_DONE}
|
||||
{1 {SQLITE_IOERR - disk I/O error}}
|
||||
{1 SQLITE_IOERR}
|
||||
{1 SQLITE_IOERR_WRITE}
|
||||
{1 SQLITE_IOERR_READ}
|
||||
{1 SQLITE_IOERR_FSYNC}
|
||||
{1 {SQLITE_ERROR - SQL logic error or missing database}}
|
||||
{1 {SQLITE_ERROR - unable to open database: ota.db}}
|
||||
{1 {SQLITE_IOERR - unable to open database: ota.db}}
|
||||
}
|
||||
|
||||
3 shmerr-* {
|
||||
{0 SQLITE_DONE}
|
||||
{1 {SQLITE_IOERR - disk I/O error}}
|
||||
{1 SQLITE_IOERR}
|
||||
}
|
||||
} {
|
||||
|
||||
catch {db close}
|
||||
sqlite3_shutdown
|
||||
set lookaside_config [sqlite3_config_lookaside 0 0]
|
||||
sqlite3_initialize
|
||||
autoinstall_test_functions
|
||||
|
||||
do_faultsim_test 2.$tn2 -faults $::f -prep {
|
||||
catch { db close }
|
||||
forcedelete test.db-journal test.db-wal ota.db-journal ota.db-wal
|
||||
forcecopy test.db.bak test.db
|
||||
forcecopy ota.db.bak ota.db
|
||||
} -body {
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[ota step]=="SQLITE_OK"} {}
|
||||
ota close
|
||||
} -test {
|
||||
faultsim_test_result {*}$::reslist
|
||||
if {$testrc==0} {
|
||||
sqlite3 db test.db
|
||||
faultsim_integrity_check
|
||||
set res [db eval $::sql]
|
||||
if {$res != [list {*}$::expect]} {
|
||||
puts ""
|
||||
puts "res: $res"
|
||||
puts "exp: $expect"
|
||||
error "data not as expected!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch {db close}
|
||||
sqlite3_shutdown
|
||||
sqlite3_config_lookaside {*}$lookaside_config
|
||||
sqlite3_initialize
|
||||
autoinstall_test_functions
|
||||
|
||||
|
||||
for {set iStep 0} {$iStep<=21} {incr iStep} {
|
||||
|
||||
forcedelete test.db-journal test.db-wal ota.db-journal ota.db-wal
|
||||
|
||||
copy_if_exists test.db.bak test.db
|
||||
copy_if_exists ota.db.bak ota.db
|
||||
|
||||
sqlite3ota ota test.db ota.db
|
||||
for {set x 0} {$x < $::iStep} {incr x} { ota step }
|
||||
ota close
|
||||
|
||||
# sqlite3 x ota.db ; puts "XYZ [x eval { SELECT * FROM ota_state } ]" ; x close
|
||||
|
||||
copy_if_exists test.db test.db.bak.2
|
||||
copy_if_exists test.db-wal test.db.bak.2-wal
|
||||
copy_if_exists test.db-oal test.db.bak.2-oal
|
||||
copy_if_exists ota.db ota.db.bak.2
|
||||
|
||||
do_faultsim_test 3.$tn.$iStep -faults $::f -prep {
|
||||
catch { db close }
|
||||
forcedelete test.db-journal test.db-wal ota.db-journal ota.db-wal
|
||||
copy_if_exists test.db.bak.2 test.db
|
||||
copy_if_exists test.db.bak.2-wal test.db-wal
|
||||
copy_if_exists test.db.bak.2-oal test.db-oal
|
||||
copy_if_exists ota.db.bak.2 ota.db
|
||||
} -body {
|
||||
sqlite3ota ota test.db ota.db
|
||||
ota step
|
||||
ota close
|
||||
} -test {
|
||||
|
||||
if {$testresult=="SQLITE_OK"} {set testresult "SQLITE_DONE"}
|
||||
faultsim_test_result {*}$::reslist
|
||||
|
||||
if {$testrc==0} {
|
||||
# No error occurred. If the OTA has not already been fully applied,
|
||||
# apply the rest of it now. Then ensure that the final state of the
|
||||
# target db is as expected. And that "PRAGMA integrity_check"
|
||||
# passes.
|
||||
sqlite3ota ota test.db ota.db
|
||||
while {[ota step] == "SQLITE_OK"} {}
|
||||
ota close
|
||||
|
||||
sqlite3 db test.db
|
||||
faultsim_integrity_check
|
||||
|
||||
set res [db eval $::sql]
|
||||
if {$res != [list {*}$::expect]} {
|
||||
puts ""
|
||||
puts "res: $res"
|
||||
puts "exp: $::expect"
|
||||
error "data not as expected!"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# 2014 October 22
|
||||
#
|
||||
# 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
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix otafault2
|
||||
|
||||
forcedelete ota.db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE target(x UNIQUE, y, z, PRIMARY KEY(y));
|
||||
INSERT INTO target VALUES(1, 2, 3);
|
||||
INSERT INTO target VALUES(4, 5, 6);
|
||||
|
||||
ATTACH 'ota.db' AS ota;
|
||||
CREATE TABLE ota.data_target(x, y, z, ota_control);
|
||||
INSERT INTO data_target VALUES(7, 8, 9, 0);
|
||||
INSERT INTO data_target VALUES(1, 11, 12, 0);
|
||||
DETACH ota;
|
||||
}
|
||||
db close
|
||||
|
||||
forcecopy test.db test.db-bak
|
||||
forcecopy ota.db ota.db-bak
|
||||
|
||||
do_faultsim_test 1 -faults oom* -prep {
|
||||
forcecopy test.db-bak test.db
|
||||
forcecopy ota.db-bak ota.db
|
||||
forcedelete test.db-oal test.db-wal ota.db-journal
|
||||
sqlite3ota ota test.db ota.db
|
||||
} -body {
|
||||
while {[ota step]=="SQLITE_OK"} { }
|
||||
ota close
|
||||
} -test {
|
||||
faultsim_test_result \
|
||||
{1 {SQLITE_CONSTRAINT - UNIQUE constraint failed: target.x}} \
|
||||
{1 SQLITE_CONSTRAINT} \
|
||||
{1 SQLITE_NOMEM} \
|
||||
{1 {SQLITE_NOMEM - unable to open a temporary database file for storing temporary tables}} \
|
||||
{1 {SQLITE_NOMEM - out of memory}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
** 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 the public interface for the OTA extension.
|
||||
*/
|
||||
|
||||
/*
|
||||
** SUMMARY
|
||||
**
|
||||
** Writing a transaction containing a large number of operations on
|
||||
** b-tree indexes that are collectively larger than the available cache
|
||||
** memory can be very inefficient.
|
||||
**
|
||||
** The problem is that in order to update a b-tree, the leaf page (at least)
|
||||
** containing the entry being inserted or deleted must be modified. If the
|
||||
** working set of leaves is larger than the available cache memory, then a
|
||||
** single leaf that is modified more than once as part of the transaction
|
||||
** may be loaded from or written to the persistent media multiple times.
|
||||
** Additionally, because the index updates are likely to be applied in
|
||||
** random order, access to pages within the database is also likely to be in
|
||||
** random order, which is itself quite inefficient.
|
||||
**
|
||||
** One way to improve the situation is to sort the operations on each index
|
||||
** by index key before applying them to the b-tree. This leads to an IO
|
||||
** pattern that resembles a single linear scan through the index b-tree,
|
||||
** and all but guarantees each modified leaf page is loaded and stored
|
||||
** exactly once. SQLite uses this trick to improve the performance of
|
||||
** CREATE INDEX commands. This extension allows it to be used to improve
|
||||
** the performance of large transactions on existing databases.
|
||||
**
|
||||
** Additionally, this extension allows the work involved in writing the
|
||||
** large transaction to be broken down into sub-transactions performed
|
||||
** sequentially by separate processes. This is useful if the system cannot
|
||||
** guarantee that a single update process will run for long enough to apply
|
||||
** the entire update, for example because the update is being applied on a
|
||||
** mobile device that is frequently rebooted. Even after the writer process
|
||||
** has committed one or more sub-transactions, other database clients continue
|
||||
** to read from the original database snapshot. In other words, partially
|
||||
** applied transactions are not visible to other clients.
|
||||
**
|
||||
** "OTA" stands for "Over The Air" update. As in a large database update
|
||||
** transmitted via a wireless network to a mobile device. A transaction
|
||||
** applied using this extension is hence refered to as an "OTA update".
|
||||
**
|
||||
**
|
||||
** LIMITATIONS
|
||||
**
|
||||
** An "OTA update" transaction is subject to the following limitations:
|
||||
**
|
||||
** * The transaction must consist of INSERT, UPDATE and DELETE operations
|
||||
** only.
|
||||
**
|
||||
** * INSERT statements may not use any default values.
|
||||
**
|
||||
** * UPDATE and DELETE statements must identify their target rows by
|
||||
** non-NULL PRIMARY KEY values. Rows with NULL values stored in PRIMARY
|
||||
** KEY fields may not be updated or deleted. If the table being written
|
||||
** has no PRIMARY KEY, affected rows must be identified by rowid.
|
||||
**
|
||||
** * UPDATE statements may not modify PRIMARY KEY columns.
|
||||
**
|
||||
** * No triggers will be fired.
|
||||
**
|
||||
** * No foreign key violations are detected or reported.
|
||||
**
|
||||
** * CHECK constraints are not enforced.
|
||||
**
|
||||
** * No constraint handling mode except for "OR ROLLBACK" is supported.
|
||||
**
|
||||
**
|
||||
** PREPARATION
|
||||
**
|
||||
** An "OTA update" is stored as a separate SQLite database. A database
|
||||
** containing an OTA update is an "OTA database". For each table in the
|
||||
** target database to be updated, the OTA database should contain a table
|
||||
** named "data_<target name>" containing the same set of columns as the
|
||||
** target table, and one more - "ota_control". The data_% table should
|
||||
** have no PRIMARY KEY or UNIQUE constraints, but each column should have
|
||||
** the same type as the corresponding column in the target database.
|
||||
** The "ota_control" column should have no type at all. For example, if
|
||||
** the target database contains:
|
||||
**
|
||||
** CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);
|
||||
**
|
||||
** Then the OTA database should contain:
|
||||
**
|
||||
** CREATE TABLE data_t1(a INTEGER, b TEXT, c, ota_control);
|
||||
**
|
||||
** The order of the columns in the data_% table does not matter.
|
||||
**
|
||||
** If the target database table is a virtual table or a table that has no
|
||||
** PRIMARY KEY declaration, the data_% table must also contain a column
|
||||
** named "ota_rowid". This column is mapped to the tables implicit primary
|
||||
** key column - "rowid". Virtual tables for which the "rowid" column does
|
||||
** not function like a primary key value cannot be updated using OTA. For
|
||||
** example, if the target db contains either of the following:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE x1 USING fts3(a, b);
|
||||
** CREATE TABLE x1(a, b)
|
||||
**
|
||||
** then the OTA database should contain:
|
||||
**
|
||||
** CREATE TABLE data_x1(a, b, ota_rowid, ota_control);
|
||||
**
|
||||
** All non-hidden columns (i.e. all columns matched by "SELECT *") of the
|
||||
** target table must be present in the input table. For virtual tables,
|
||||
** hidden columns are optional - they are updated by OTA if present in
|
||||
** the input table, or not otherwise. For example, to write to an fts4
|
||||
** table with a hidden languageid column such as:
|
||||
**
|
||||
** CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');
|
||||
**
|
||||
** Either of the following input table schemas may be used:
|
||||
**
|
||||
** CREATE TABLE data_ft1(a, b, langid, ota_rowid, ota_control);
|
||||
** CREATE TABLE data_ft1(a, b, ota_rowid, ota_control);
|
||||
**
|
||||
** For each row to INSERT into the target database as part of the OTA
|
||||
** update, the corresponding data_% table should contain a single record
|
||||
** with the "ota_control" column set to contain integer value 0. The
|
||||
** other columns should be set to the values that make up the new record
|
||||
** to insert.
|
||||
**
|
||||
** If the target database table has an INTEGER PRIMARY KEY, it is not
|
||||
** possible to insert a NULL value into the IPK column. Attempting to
|
||||
** do so results in an SQLITE_MISMATCH error.
|
||||
**
|
||||
** For each row to DELETE from the target database as part of the OTA
|
||||
** update, the corresponding data_% table should contain a single record
|
||||
** with the "ota_control" column set to contain integer value 1. The
|
||||
** real primary key values of the row to delete should be stored in the
|
||||
** corresponding columns of the data_% table. The values stored in the
|
||||
** other columns are not used.
|
||||
**
|
||||
** For each row to UPDATE from the target database as part of the OTA
|
||||
** update, the corresponding data_% table should contain a single record
|
||||
** with the "ota_control" column set to contain a value of type text.
|
||||
** The real primary key values identifying the row to update should be
|
||||
** stored in the corresponding columns of the data_% table row, as should
|
||||
** the new values of all columns being update. The text value in the
|
||||
** "ota_control" column must contain the same number of characters as
|
||||
** there are columns in the target database table, and must consist entirely
|
||||
** of 'x' and '.' characters (or in some special cases 'd' - see below). For
|
||||
** each column that is being updated, the corresponding character is set to
|
||||
** 'x'. For those that remain as they are, the corresponding character of the
|
||||
** ota_control value should be set to '.'. For example, given the tables
|
||||
** above, the update statement:
|
||||
**
|
||||
** UPDATE t1 SET c = 'usa' WHERE a = 4;
|
||||
**
|
||||
** is represented by the data_t1 row created by:
|
||||
**
|
||||
** INSERT INTO data_t1(a, b, c, ota_control) VALUES(4, NULL, 'usa', '..x');
|
||||
**
|
||||
** Instead of an 'x' character, characters of the ota_control value specified
|
||||
** for UPDATEs may also be set to 'd'. In this case, instead of updating the
|
||||
** target table with the value stored in the corresponding data_% column, the
|
||||
** user-defined SQL function "ota_delta()" is invoked and the result stored in
|
||||
** the target table column. ota_delta() is invoked with two arguments - the
|
||||
** original value currently stored in the target table column and the
|
||||
** value specified in the data_xxx table.
|
||||
**
|
||||
** For example, this row:
|
||||
**
|
||||
** INSERT INTO data_t1(a, b, c, ota_control) VALUES(4, NULL, 'usa', '..d');
|
||||
**
|
||||
** is similar to an UPDATE statement such as:
|
||||
**
|
||||
** UPDATE t1 SET c = ota_delta(c, 'usa') WHERE a = 4;
|
||||
**
|
||||
** If the target database table is a virtual table or a table with no PRIMARY
|
||||
** KEY, the ota_control value should not include a character corresponding
|
||||
** to the ota_rowid value. For example, this:
|
||||
**
|
||||
** INSERT INTO data_ft1(a, b, ota_rowid, ota_control)
|
||||
** VALUES(NULL, 'usa', 12, '.x');
|
||||
**
|
||||
** causes a result similar to:
|
||||
**
|
||||
** UPDATE ft1 SET b = 'usa' WHERE rowid = 12;
|
||||
**
|
||||
** The data_xxx tables themselves should have no PRIMARY KEY declarations.
|
||||
** However, OTA is more efficient if reading the rows in from each data_xxx
|
||||
** table in "rowid" order is roughly the same as reading them sorted by
|
||||
** the PRIMARY KEY of the corresponding target database table. In other
|
||||
** words, rows should be sorted using the destination table PRIMARY KEY
|
||||
** fields before they are inserted into the data_xxx tables.
|
||||
**
|
||||
** USAGE
|
||||
**
|
||||
** The API declared below allows an application to apply an OTA update
|
||||
** stored on disk to an existing target database. Essentially, the
|
||||
** application:
|
||||
**
|
||||
** 1) Opens an OTA handle using the sqlite3ota_open() function.
|
||||
**
|
||||
** 2) Registers any required virtual table modules with the database
|
||||
** handle returned by sqlite3ota_db(). Also, if required, register
|
||||
** the ota_delta() implementation.
|
||||
**
|
||||
** 3) Calls the sqlite3ota_step() function one or more times on
|
||||
** the new handle. Each call to sqlite3ota_step() performs a single
|
||||
** b-tree operation, so thousands of calls may be required to apply
|
||||
** a complete update.
|
||||
**
|
||||
** 4) Calls sqlite3ota_close() to close the OTA update handle. If
|
||||
** sqlite3ota_step() has been called enough times to completely
|
||||
** apply the update to the target database, then the OTA database
|
||||
** is marked as fully applied. Otherwise, the state of the OTA
|
||||
** update application is saved in the OTA database for later
|
||||
** resumption.
|
||||
**
|
||||
** See comments below for more detail on APIs.
|
||||
**
|
||||
** If an update is only partially applied to the target database by the
|
||||
** time sqlite3ota_close() is called, various state information is saved
|
||||
** within the OTA database. This allows subsequent processes to automatically
|
||||
** resume the OTA update from where it left off.
|
||||
**
|
||||
** To remove all OTA extension state information, returning an OTA database
|
||||
** to its original contents, it is sufficient to drop all tables that begin
|
||||
** with the prefix "ota_"
|
||||
**
|
||||
** DATABASE LOCKING
|
||||
**
|
||||
** An OTA update may not be applied to a database in WAL mode. Attempting
|
||||
** to do so is an error (SQLITE_ERROR).
|
||||
**
|
||||
** While an OTA handle is open, a SHARED lock may be held on the target
|
||||
** database file. This means it is possible for other clients to read the
|
||||
** database, but not to write it.
|
||||
**
|
||||
** If an OTA update is started and then suspended before it is completed,
|
||||
** then an external client writes to the database, then attempting to resume
|
||||
** the suspended OTA update is also an error (SQLITE_BUSY).
|
||||
*/
|
||||
|
||||
#ifndef _SQLITE3OTA_H
|
||||
#define _SQLITE3OTA_H
|
||||
|
||||
#include "sqlite3.h" /* Required for error code definitions */
|
||||
|
||||
typedef struct sqlite3ota sqlite3ota;
|
||||
|
||||
/*
|
||||
** Open an OTA handle.
|
||||
**
|
||||
** 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(). When opening the databases, OTA passes
|
||||
** the SQLITE_CONFIG_URI flag to sqlite3_open_v2(). So if either zTarget
|
||||
** or zOta begin with "file:", it will be interpreted as an SQLite
|
||||
** database URI, not a regular file name.
|
||||
**
|
||||
** If the zState argument is passed a NULL value, the OTA extension stores
|
||||
** the current state of the update (how many rows have been updated, which
|
||||
** indexes are yet to be updated etc.) within the OTA database itself. This
|
||||
** can be convenient, as it means that the OTA application does not need to
|
||||
** organize removing a separate state file after the update is concluded.
|
||||
** Or, if zState is non-NULL, it must be a path to a database file in which
|
||||
** the OTA extension can store the state of the update.
|
||||
**
|
||||
** When resuming an OTA update, the zState argument must be passed the same
|
||||
** value as when the OTA update was started.
|
||||
**
|
||||
** Once the OTA update is finished, the OTA extension does not
|
||||
** automatically remove any zState database file, even if it created it.
|
||||
**
|
||||
** 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,
|
||||
const char *zState
|
||||
);
|
||||
|
||||
/*
|
||||
** Internally, each OTA connection uses a separate SQLite database
|
||||
** connection to access the target and ota update databases. This
|
||||
** API allows the application direct access to these database handles.
|
||||
**
|
||||
** The first argument passed to this function must be a valid, open, OTA
|
||||
** handle. The second argument should be passed zero to access the target
|
||||
** database handle, or non-zero to access the ota update database handle.
|
||||
** Accessing the underlying database handles may be useful in the
|
||||
** following scenarios:
|
||||
**
|
||||
** * If any target tables are virtual tables, it may be necessary to
|
||||
** call sqlite3_create_module() on the target database handle to
|
||||
** register the required virtual table implementations.
|
||||
**
|
||||
** * If the data_xxx tables in the OTA source database are virtual
|
||||
** tables, the application may need to call sqlite3_create_module() on
|
||||
** the ota update db handle to any required virtual table
|
||||
** implementations.
|
||||
**
|
||||
** * If the application uses the "ota_delta()" feature described above,
|
||||
** it must use sqlite3_create_function() or similar to register the
|
||||
** ota_delta() implementation with the target database handle.
|
||||
**
|
||||
** If an error has occurred, either while opening or stepping the OTA object,
|
||||
** this function may return NULL. The error code and message may be collected
|
||||
** when sqlite3ota_close() is called.
|
||||
*/
|
||||
sqlite3 *sqlite3ota_db(sqlite3ota*, int bOta);
|
||||
|
||||
/*
|
||||
** Do some work towards applying the OTA update to the target db.
|
||||
**
|
||||
** Return SQLITE_DONE if the update has been completely applied, or
|
||||
** SQLITE_OK if no error occurs but there remains work to do to apply
|
||||
** the OTA update. If an error does occur, some other error code is
|
||||
** returned.
|
||||
**
|
||||
** Once a call to sqlite3ota_step() has returned a value other than
|
||||
** SQLITE_OK, all subsequent calls on the same OTA handle are no-ops
|
||||
** that immediately return the same value.
|
||||
*/
|
||||
int sqlite3ota_step(sqlite3ota *pOta);
|
||||
|
||||
/*
|
||||
** Close an OTA handle.
|
||||
**
|
||||
** If the OTA update has been completely applied, mark the OTA database
|
||||
** as fully applied. 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()
|
||||
** or sqlite3ota_open() call, or if one occurs within this function, an
|
||||
** SQLite error code is returned. Additionally, *pzErrmsg may be set to
|
||||
** point to a buffer containing a utf-8 formatted English language error
|
||||
** message. It is the responsibility of the caller to eventually free any
|
||||
** such buffer using sqlite3_free().
|
||||
**
|
||||
** Otherwise, if no error occurs, this function returns SQLITE_OK if the
|
||||
** update has been partially applied, or SQLITE_DONE if it has been
|
||||
** completely applied.
|
||||
*/
|
||||
int sqlite3ota_close(sqlite3ota *pOta, char **pzErrmsg);
|
||||
|
||||
/*
|
||||
** Return the total number of key-value operations (inserts, deletes or
|
||||
** updates) that have been performed on the target database since the
|
||||
** current OTA update was started.
|
||||
*/
|
||||
sqlite3_int64 sqlite3ota_progress(sqlite3ota *pOta);
|
||||
|
||||
/*
|
||||
** Create an OTA VFS named zName that accesses the underlying file-system
|
||||
** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
|
||||
** then the new OTA VFS uses the default system VFS to access the file-system.
|
||||
** The new object is registered as a non-default VFS with SQLite before
|
||||
** returning.
|
||||
**
|
||||
** 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 "multiplex" (not the default).
|
||||
** sqlite3_multiplex_initialize(0, 0);
|
||||
**
|
||||
** // Create an ota VFS named "ota" that uses multiplexor. If the
|
||||
** // second argument were replaced with NULL, the "ota" VFS would
|
||||
** // access the file-system via the system default VFS, bypassing the
|
||||
** // multiplexor.
|
||||
** sqlite3ota_create_vfs("ota", "multiplex");
|
||||
**
|
||||
** // 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 */
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
** 2015 February 16
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
*************************************************************************
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
#if defined(SQLITE_TEST)
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_OTA)
|
||||
|
||||
#include "sqlite3ota.h"
|
||||
#include <tcl.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* From main.c (apparently...) */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
|
||||
void test_ota_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
|
||||
Tcl_Interp *interp = (Tcl_Interp*)sqlite3_user_data(pCtx);
|
||||
Tcl_Obj *pScript;
|
||||
int i;
|
||||
|
||||
pScript = Tcl_NewObj();
|
||||
Tcl_IncrRefCount(pScript);
|
||||
Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj("ota_delta", -1));
|
||||
for(i=0; i<nArg; i++){
|
||||
sqlite3_value *pIn = apVal[i];
|
||||
const char *z = (const char*)sqlite3_value_text(pIn);
|
||||
Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(z, -1));
|
||||
}
|
||||
|
||||
if( TCL_OK==Tcl_EvalObjEx(interp, pScript, TCL_GLOBAL_ONLY) ){
|
||||
const char *z = Tcl_GetStringResult(interp);
|
||||
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
|
||||
}else{
|
||||
Tcl_BackgroundError(interp);
|
||||
}
|
||||
|
||||
Tcl_DecrRefCount(pScript);
|
||||
}
|
||||
|
||||
|
||||
static int test_sqlite3ota_cmd(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int ret = TCL_OK;
|
||||
sqlite3ota *pOta = (sqlite3ota*)clientData;
|
||||
const char *azMethod[] = { "step", "close", "create_ota_delta", 0 };
|
||||
int iMethod;
|
||||
|
||||
if( objc!=2 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "METHOD");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIndexFromObj(interp, objv[1], azMethod, "method", 0, &iMethod) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
switch( iMethod ){
|
||||
case 0: /* step */ {
|
||||
int rc = sqlite3ota_step(pOta);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: /* close */ {
|
||||
char *zErrmsg = 0;
|
||||
int rc;
|
||||
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
|
||||
rc = sqlite3ota_close(pOta, &zErrmsg);
|
||||
if( rc==SQLITE_OK || rc==SQLITE_DONE ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
assert( zErrmsg==0 );
|
||||
}else{
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
if( zErrmsg ){
|
||||
Tcl_AppendResult(interp, " - ", zErrmsg, 0);
|
||||
sqlite3_free(zErrmsg);
|
||||
}
|
||||
ret = TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: /* create_ota_delta */ {
|
||||
sqlite3 *db = sqlite3ota_db(pOta, 0);
|
||||
int rc = sqlite3_create_function(
|
||||
db, "ota_delta", -1, SQLITE_UTF8, (void*)interp, test_ota_delta, 0, 0
|
||||
);
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
ret = (rc==SQLITE_OK ? TCL_OK : TCL_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* seems unlikely */
|
||||
assert( !"cannot happen" );
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3ota CMD <target-db> <ota-db> ?<state-db>?
|
||||
*/
|
||||
static int test_sqlite3ota(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3ota *pOta = 0;
|
||||
const char *zCmd;
|
||||
const char *zTarget;
|
||||
const char *zOta;
|
||||
const char *zStateDb = 0;
|
||||
|
||||
if( objc!=4 && objc!=5 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB OTA-DB ?STATE-DB?");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
zCmd = Tcl_GetString(objv[1]);
|
||||
zTarget = Tcl_GetString(objv[2]);
|
||||
zOta = Tcl_GetString(objv[3]);
|
||||
if( objc==5 ) zStateDb = Tcl_GetString(objv[4]);
|
||||
|
||||
pOta = sqlite3ota_open(zTarget, zOta, zStateDb);
|
||||
Tcl_CreateObjCommand(interp, zCmd, test_sqlite3ota_cmd, (ClientData)pOta, 0);
|
||||
Tcl_SetObjResult(interp, objv[1]);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
** Tclcmd: sqlite3ota_internal_test
|
||||
*/
|
||||
static int test_sqlite3ota_internal_test(
|
||||
ClientData clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
sqlite3 *db;
|
||||
|
||||
if( objc!=1 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
db = sqlite3ota_db(0, 0);
|
||||
if( db!=0 ){
|
||||
Tcl_AppendResult(interp, "sqlite3ota_db(0, 0)!=0", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
int SqliteOta_Init(Tcl_Interp *interp){
|
||||
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 },
|
||||
{ "sqlite3ota_internal_test", test_sqlite3ota_internal_test },
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <tcl.h>
|
||||
int SqliteOta_Init(Tcl_Interp *interp){ return TCL_OK; }
|
||||
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_OTA) */
|
||||
#endif /* defined(SQLITE_TEST) */
|
||||
|
||||
+9
-2
@@ -3401,11 +3401,18 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
|
||||
pBlob->cb = pGeomCtx[0];
|
||||
pBlob->nParam = nArg;
|
||||
for(i=0; i<nArg; i++){
|
||||
if( sqlite3_value_type(aArg[i])==SQLITE_BLOB
|
||||
&& sqlite3_value_bytes(aArg[i])==sizeof(sqlite3_rtree_dbl)
|
||||
){
|
||||
memcpy(&pBlob->aParam[i], sqlite3_value_blob(aArg[i]),
|
||||
sizeof(sqlite3_rtree_dbl));
|
||||
}else{
|
||||
#ifdef SQLITE_RTREE_INT_ONLY
|
||||
pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
|
||||
pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
|
||||
#else
|
||||
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
|
||||
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ LIBOBJ+= vdbe.o parse.o \
|
||||
mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
|
||||
notify.o opcodes.o os.o os_unix.o os_win.o \
|
||||
pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o status.o \
|
||||
random.o resolve.o rowset.o rtree.o select.o sqlite3ota.o status.o \
|
||||
table.o threads.o tokenize.o trigger.o \
|
||||
update.o userauth.o util.o vacuum.o \
|
||||
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
|
||||
@@ -221,6 +221,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
|
||||
#
|
||||
@@ -238,6 +241,7 @@ SRC += \
|
||||
TESTSRC = \
|
||||
$(TOP)/ext/fts3/fts3_term.c \
|
||||
$(TOP)/ext/fts3/fts3_test.c \
|
||||
$(TOP)/ext/ota/test_ota.c \
|
||||
$(TOP)/src/test1.c \
|
||||
$(TOP)/src/test2.c \
|
||||
$(TOP)/src/test3.c \
|
||||
@@ -339,7 +343,7 @@ TESTSRC2 = \
|
||||
$(TOP)/ext/fts3/fts3_expr.c \
|
||||
$(TOP)/ext/fts3/fts3_tokenizer.c \
|
||||
$(TOP)/ext/fts3/fts3_write.c \
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
$(TOP)/ext/async/sqlite3async.c
|
||||
|
||||
# Header files used by all library source files.
|
||||
#
|
||||
@@ -603,6 +607,9 @@ rtree.o: $(TOP)/ext/rtree/rtree.c $(HDR) $(EXTHDR)
|
||||
userauth.o: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/userauth/userauth.c
|
||||
|
||||
sqlite3ota.o: $(TOP)/ext/ota/sqlite3ota.c $(HDR) $(EXTHDR)
|
||||
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/ota/sqlite3ota.c
|
||||
|
||||
|
||||
# Rules for building test programs and for running tests
|
||||
#
|
||||
@@ -734,7 +741,11 @@ wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
|
||||
$(TOP)/test/wordcount.c sqlite3.c
|
||||
|
||||
speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o
|
||||
$(TCC) -I. -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB)
|
||||
$(TCC) -I. $(OTAFLAGS) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB)
|
||||
|
||||
ota$(EXE): $(TOP)/ext/ota/ota.c $(TOP)/ext/ota/sqlite3ota.c sqlite3.o
|
||||
$(TCC) -I. -o ota$(EXE) $(TOP)/ext/ota/ota.c sqlite3.o \
|
||||
$(THREADLIB)
|
||||
|
||||
# This target will fail if the SQLite amalgamation contains any exported
|
||||
# symbols that do not begin with "sqlite3_". It is run as part of the
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
C Use\san\sALWAY()\son\sconditionals\sin\sthe\stransitive\sconstraint\slogic\sthat\sare\nalways\strue\sas\sfar\sas\swe\sknow.
|
||||
D 2015-05-18T12:18:37.067
|
||||
C Add\sa\stest\scase\sto\sverify\sthat\sCREATE\sTABLE\sAS\sdoes\snot\sstore\sINT\svalues\nin\sTEXT\scolumns.\s\sTicket\s[f2ad7de056ab1dc92].
|
||||
D 2015-05-20T17:25:44.766
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736
|
||||
F Makefile.in 0a6ae26396ec696221021780dffbb894ff3cead7
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
F Makefile.msc f9da80e91c8f953f7517c979a1540ef8af1b386f
|
||||
F Makefile.msc 55fdbee85598e2c61fc1e643d02f38f1f943463c
|
||||
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
|
||||
F README.md 0bfccb18927349653c09137a458b961fa8ab4cb9
|
||||
F VERSION ce0ae95abd7121c534f6917c1c8f2b70d9acd4db
|
||||
@@ -87,7 +87,7 @@ F ext/fts3/fts3_hash.c 29b986e43f4e9dd40110eafa377dc0d63c422c60
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
F ext/fts3/fts3_icu.c e319e108661147bcca8dd511cd562f33a1ba81b5
|
||||
F ext/fts3/fts3_porter.c 3565faf04b626cddf85f03825e86056a4562c009
|
||||
F ext/fts3/fts3_snippet.c b7aaa8698096b26e1c6eb563e317409323398142
|
||||
F ext/fts3/fts3_snippet.c 97a16ea233106b83b84a7a1e821318ea333688f7
|
||||
F ext/fts3/fts3_term.c 88c55a6fa1a51ab494e33dced0401a6c28791fd7
|
||||
F ext/fts3/fts3_test.c 8a3a78c4458b2d7c631fcf4b152a5cd656fa7038
|
||||
F ext/fts3/fts3_tokenize_vtab.c a27593ab19657166f6fa5ec073b678cc29a75860
|
||||
@@ -123,8 +123,27 @@ 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/ota.c c47352838b967384a81eda5de75c352922a0dd6e
|
||||
F ext/ota/ota1.test abdcbe746db4c7f7b51e842b576cacb33eef28f5
|
||||
F ext/ota/ota10.test 85e0f6e7964db5007590c1b299e75211ed4240d4
|
||||
F ext/ota/ota11.test 2f606cd2b4af260a86b549e91b9f395450fc75cb
|
||||
F ext/ota/ota12.test 0dff44474de448fb4b0b28c20da63273a4149abb
|
||||
F ext/ota/ota13.test f7a3d73fa5d3fabf2755b569f125fce7390a874c
|
||||
F ext/ota/ota3.test 3fe3521fbdce32d0e4e116a60999c3cba47712c5
|
||||
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
|
||||
F ext/ota/ota6.test 3bde7f69a894748b27206b6753462ec3b75b6bb6
|
||||
F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
|
||||
F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
|
||||
F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
|
||||
F ext/ota/otaA.test ab67f7f53670b81c750dcc946c5b704f51c429a4
|
||||
F ext/ota/otacrash.test 8346192b2d46cbe7787d5d65904d81d3262a3cbf
|
||||
F ext/ota/otafault.test 8c43586c2b96ca16bbce00b5d7e7d67316126db8
|
||||
F ext/ota/otafault2.test fa202a98ca221faec318f3e5c5f39485b1256561
|
||||
F ext/ota/sqlite3ota.c 2246b779f46ab20d5e7876f5b96c378c601d20f4
|
||||
F ext/ota/sqlite3ota.h 00028de37eede471ff1947d455cc3f33d3a911c6
|
||||
F ext/ota/test_ota.c a876f88550d7d59a3ef62d4c1a5c04c4c2f1ebe1
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 0c207fd8b814a35537d96681cbf57436e200b75e
|
||||
F ext/rtree/rtree.c 60ec0a71d4d213665a706c795c887e7c4d148635
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -152,7 +171,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk d49723483ee9e4fb71dc2bd0e6be58705a481e73
|
||||
F main.mk 59e4ac92e080c23d8e260195748c8a706dd18a20
|
||||
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
|
||||
F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
@@ -176,7 +195,7 @@ F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79
|
||||
F src/btree.c 30a80340481098d699398cba3536c895373b2e2c
|
||||
F src/btree.h 969adc948e89e449220ff0ff724c94bb2a52e9f1
|
||||
F src/btreeInt.h 973a22a6fd61350b454ad614832b1f0a5e25a1e4
|
||||
F src/build.c 61b47073f79f31e80a05db9ce13c5ca81bf8f74e
|
||||
F src/build.c d5d9090788118178190c5724c19f93953b8c7a4e
|
||||
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
|
||||
F src/complete.c a5cf5b4b56390cfb7b8636e8f7ddef90258dd575
|
||||
F src/ctime.c 5a0b735dc95604766f5dac73973658eef782ee8b
|
||||
@@ -220,7 +239,7 @@ F src/os_win.c 97f7828a9554d753665b6fcf7540e31c2b3d6a6e
|
||||
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
|
||||
F src/pager.c 97110085b1321298412f1e5c37bddb95b36d9208
|
||||
F src/pager.h c3476e7c89cdf1c6914e50a11f3714e30b4e0a77
|
||||
F src/parse.y c4e0387bc88c8e21e5ba653e2578959a1f3cdbc7
|
||||
F src/parse.y af55d4fb5e588705112e9788364ca3af09651fcf
|
||||
F src/pcache.c 10539fb959849ad6efff80050541cab3d25089d4
|
||||
F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
|
||||
F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
|
||||
@@ -231,16 +250,16 @@ F src/printf.c 13ce37e5574f9b0682fa86dbcf9faf76b9d82a15
|
||||
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
|
||||
F src/resolve.c 99eabf7eff0bfa65b75939b46caa82e2b2133f28
|
||||
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
|
||||
F src/select.c 1b0bfc7d59e48c26b895a6b719157111a617d9e3
|
||||
F src/select.c 95e728e1bdbca2ee45016308e3eb6d19095155cc
|
||||
F src/shell.c 07dda7cd692911d2f22269953418d049f2e2c0ee
|
||||
F src/sqlite.h.in bf3fe5eba3a5142477b8dae3cfce627c3e971455
|
||||
F src/sqlite.h.in 0127e418883c2b41f7fbc056bc1033fa56fbd2a5
|
||||
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
|
||||
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
|
||||
F src/sqliteInt.h 88738d94a343000e7a5c0e295d111c4cfccb18b0
|
||||
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
|
||||
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
|
||||
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
|
||||
F src/tclsqlite.c 9111a95999edac92229c972e2c34fbc171bbb6c5
|
||||
F src/tclsqlite.c 555f6467706905ab02a9df6cf1a84b1774a4754a
|
||||
F src/test1.c a8e09b811f70184ce65012f27f30cfee7e54f268
|
||||
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
|
||||
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
|
||||
@@ -255,7 +274,7 @@ F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
|
||||
F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803
|
||||
F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07
|
||||
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
|
||||
F src/test_config.c c2d3ff6c129d50183900c7eff14158ff7e9b3f03
|
||||
F src/test_config.c 7d28ede476189eefd75252fa9acaadc6ba93a733
|
||||
F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852
|
||||
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
|
||||
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
|
||||
@@ -293,7 +312,7 @@ F src/update.c 3c4ecc282accf12d39edb8d524cf089645e55a13
|
||||
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
|
||||
F src/util.c a6431c92803b975b7322724a7b433e538d243539
|
||||
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
|
||||
F src/vdbe.c 8775967d8ae262f4134a34f539b8747b23421000
|
||||
F src/vdbe.c 6aee8a041742413ab3113e6682bc7ad1628a2bbe
|
||||
F src/vdbe.h 7e538ecf47dccb307ea2d087c3ddc2dd8d70e79d
|
||||
F src/vdbeInt.h de3291a6688dfef9f61d47705c1bd57008b1185d
|
||||
F src/vdbeapi.c 583d56b129dd27f12bed518270de9ebe521e6a75
|
||||
@@ -747,10 +766,10 @@ F test/minmax.test 42fbad0e81afaa6e0de41c960329f2b2c3526efd
|
||||
F test/minmax2.test b44bae787fc7b227597b01b0ca5575c7cb54d3bc
|
||||
F test/minmax3.test cc1e8b010136db0d01a6f2a29ba5a9f321034354
|
||||
F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
|
||||
F test/misc1.test 783ba75743b2cf71e0f646bf540a6cef57264811
|
||||
F test/misc1.test 2bb46a3656e97f80c82880a94ea10d76a3b60cb0
|
||||
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
|
||||
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
|
||||
F test/misc4.test 21c3a52354c022d2388a40c5082e9348e041bef7
|
||||
F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
|
||||
F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1
|
||||
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
|
||||
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
|
||||
@@ -785,6 +804,7 @@ F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
|
||||
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
|
||||
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
|
||||
F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7
|
||||
F test/ota.test 3a8d97cbf8f7210dc6a638797c4e4cd674036927
|
||||
F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
|
||||
@@ -798,7 +818,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
|
||||
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
|
||||
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
|
||||
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
|
||||
F test/permutations.test f9cc1dd987986c9d4949211c7a4ed55ec9aecba1
|
||||
F test/permutations.test 0e2dc2aab7b1043bd2b4404f51651c31da007e52
|
||||
F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
|
||||
F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
|
||||
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
|
||||
@@ -817,7 +837,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
|
||||
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
|
||||
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
|
||||
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
|
||||
F test/releasetest.tcl 7ad4fd49ae50c41ec7781815bdda8a8b278781d4
|
||||
F test/releasetest.tcl e340abab899d4b8bdd87ff434e10a83ebe38c5ac
|
||||
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
|
||||
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
|
||||
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
|
||||
@@ -844,7 +864,7 @@ F test/securedel2.test 2d54c28e46eb1fd6902089958b20b1b056c6f1c5
|
||||
F test/select1.test be62204d2bd9a5a8a149e9974cfddce893d8f686
|
||||
F test/select2.test 352480e0e9c66eda9c3044e412abdf5be0215b56
|
||||
F test/select3.test 2ce595f8fb8e2ac10071d3b4e424cadd4634a054
|
||||
F test/select4.test 824342f382f16b4afe42d0887ef82b3d6abb6075
|
||||
F test/select4.test 6d5bc6d178a367e8b48fa1c1d3ea12cae9c2d650
|
||||
F test/select5.test e758b8ef94f69b111df4cb819008856655dcd535
|
||||
F test/select6.test 39eac4a5c03650b2b473c532882273283ee8b7a0
|
||||
F test/select7.test 7fd2ef598cfabb6b9ff6ac13973b91d0527df49d
|
||||
@@ -900,7 +920,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c 2b416dca3a155fcaa849540b2e7fc1df12896c23
|
||||
F test/speedtest1.c 9f1b745c24886cced3f70ffc666300152a39013c
|
||||
F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49
|
||||
F test/sqldiff1.test e5ecfe95b3a2ff6380f0db6ea8bec246b675e122
|
||||
F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68
|
||||
@@ -922,7 +942,7 @@ F test/tclsqlite.test 7fb866443c7deceed22b63948ccd6f76b52ad054
|
||||
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
|
||||
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
|
||||
F test/tester.tcl 51211254f2ee2340d3e4fa0a83bd5381b9e1a227
|
||||
F test/tester.tcl c18dbf42f4b0c1fb889b0efeb8a59d5143dd9828
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1083,7 +1103,7 @@ F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
|
||||
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
|
||||
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
|
||||
F test/transitive1.test 293300f46916569f08875cdb2fe2134be2c27677
|
||||
F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03
|
||||
F test/trigger1.test ea9624cc1dae05645469df6119fa815f9e6f1e8c
|
||||
F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
|
||||
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
|
||||
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
|
||||
@@ -1223,7 +1243,7 @@ F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
|
||||
F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d
|
||||
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
|
||||
F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
|
||||
F tool/mksqlite3c.tcl 10c06c9c616415c0269c13a33304a75e3c319c3f
|
||||
F tool/mksqlite3c.tcl fdeab4c1eed90b7ab741ec12a7bc5c2fb60188bd
|
||||
F tool/mksqlite3h.tcl 44730d586c9031638cdd2eb443b801c0d2dbd9f8
|
||||
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
|
||||
F tool/mkvsix.tcl 3b58b9398f91c7dbf18d49eb87cefeee9efdbce1
|
||||
@@ -1258,7 +1278,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 6bfaf525cac2e0c0a4a3bd3a1fc7bf5bd3234303
|
||||
R 6e68677be01e9eb58bd600864e0bd14f
|
||||
P 937f659e8933011c44e822328a97cb5f0607c04d
|
||||
R a7c7024ecc6e55965138342c00704c17
|
||||
U drh
|
||||
Z db7553e06fec928a1e05d39be9821e62
|
||||
Z 8a4264223b9336427c1887f1ad564ff3
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
204e567f68e4b3e069f04ca0643c6e5db781d39f
|
||||
0e45e8f1574ef19a43dbd118440ddbc5cec80ce7
|
||||
+32
-13
@@ -1913,26 +1913,45 @@ void sqlite3EndTable(
|
||||
** be redundant.
|
||||
*/
|
||||
if( pSelect ){
|
||||
SelectDest dest;
|
||||
Table *pSelTab;
|
||||
SelectDest dest; /* Where the SELECT should store results */
|
||||
int regYield; /* Register holding co-routine entry-point */
|
||||
int addrTop; /* Top of the co-routine */
|
||||
int regRec; /* A record to be insert into the new table */
|
||||
int regRowid; /* Rowid of the next row to insert */
|
||||
int addrInsLoop; /* Top of the loop for inserting rows */
|
||||
Table *pSelTab; /* A table that describes the SELECT results */
|
||||
|
||||
regYield = ++pParse->nMem;
|
||||
regRec = ++pParse->nMem;
|
||||
regRowid = ++pParse->nMem;
|
||||
assert(pParse->nTab==1);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
|
||||
pParse->nTab = 2;
|
||||
sqlite3SelectDestInit(&dest, SRT_Table, 1);
|
||||
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
|
||||
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
|
||||
sqlite3Select(pParse, pSelect, &dest);
|
||||
sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
|
||||
sqlite3VdbeJumpHere(v, addrTop - 1);
|
||||
if( pParse->nErr ) return;
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
p->nCol = pSelTab->nCol;
|
||||
p->aCol = pSelTab->aCol;
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
|
||||
VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
|
||||
sqlite3TableAffinity(v, p, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
|
||||
sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrInsLoop);
|
||||
sqlite3VdbeJumpHere(v, addrInsLoop);
|
||||
sqlite3VdbeAddOp1(v, OP_Close, 1);
|
||||
if( pParse->nErr==0 ){
|
||||
pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
|
||||
if( pSelTab==0 ) return;
|
||||
assert( p->aCol==0 );
|
||||
p->nCol = pSelTab->nCol;
|
||||
p->aCol = pSelTab->aCol;
|
||||
pSelTab->nCol = 0;
|
||||
pSelTab->aCol = 0;
|
||||
sqlite3DeleteTable(db, pSelTab);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute the complete text of the CREATE statement */
|
||||
|
||||
+4
-2
@@ -448,6 +448,7 @@ selectnowith(A) ::= oneselect(X). {A = X;}
|
||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
||||
Select *pRhs = Z;
|
||||
Select *pLhs = X;
|
||||
if( pRhs && pRhs->pPrior ){
|
||||
SrcList *pFrom;
|
||||
Token x;
|
||||
@@ -458,11 +459,12 @@ selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
||||
}
|
||||
if( pRhs ){
|
||||
pRhs->op = (u8)Y;
|
||||
pRhs->pPrior = X;
|
||||
pRhs->pPrior = pLhs;
|
||||
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
|
||||
pRhs->selFlags &= ~SF_MultiValue;
|
||||
if( Y!=TK_ALL ) pParse->hasCompound = 1;
|
||||
}else{
|
||||
sqlite3SelectDelete(pParse->db, X);
|
||||
sqlite3SelectDelete(pParse->db, pLhs);
|
||||
}
|
||||
A = pRhs;
|
||||
}
|
||||
|
||||
+9
-4
@@ -713,8 +713,13 @@ static void selectInnerLoop(
|
||||
/* If the destination is an EXISTS(...) expression, the actual
|
||||
** values returned by the SELECT are not required.
|
||||
*/
|
||||
sqlite3ExprCodeExprList(pParse, pEList, regResult,
|
||||
(eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0);
|
||||
u8 ecelFlags;
|
||||
if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
ecelFlags = SQLITE_ECEL_DUP;
|
||||
}else{
|
||||
ecelFlags = 0;
|
||||
}
|
||||
sqlite3ExprCodeExprList(pParse, pEList, regResult, ecelFlags);
|
||||
}
|
||||
|
||||
/* If the DISTINCT keyword was present on the SELECT statement
|
||||
@@ -5518,9 +5523,9 @@ select_end:
|
||||
void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
int n = 0;
|
||||
pView = sqlite3TreeViewPush(pView, moreToFollow);
|
||||
sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p)",
|
||||
sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x",
|
||||
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
||||
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p
|
||||
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags
|
||||
);
|
||||
if( p->pSrc && p->pSrc->nSrc ) n++;
|
||||
if( p->pWhere ) n++;
|
||||
|
||||
+11
-1
@@ -956,13 +956,21 @@ struct sqlite3_io_methods {
|
||||
** pointed to by the pArg argument. This capability is used during testing
|
||||
** and only needs to be supported when SQLITE_TEST is defined.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_WAL_BLOCK]]
|
||||
* <li>[[SQLITE_FCNTL_WAL_BLOCK]]
|
||||
** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
|
||||
** be advantageous to block on the next WAL lock if the lock is not immediately
|
||||
** available. The WAL subsystem issues this signal during rare
|
||||
** circumstances in order to fix a problem with priority inversion.
|
||||
** Applications should <em>not</em> use this file-control.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_ZIPVFS]]
|
||||
** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
|
||||
** VFS should return SQLITE_NOTFOUND for this opcode.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_OTA]]
|
||||
** The [SQLITE_FCNTL_OTA] opcode is implemented by the special VFS used by
|
||||
** the OTA extension only. All other VFS should return SQLITE_NOTFOUND for
|
||||
** this opcode.
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
@@ -988,6 +996,8 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
|
||||
#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
|
||||
#define SQLITE_FCNTL_WAL_BLOCK 24
|
||||
#define SQLITE_FCNTL_ZIPVFS 25
|
||||
#define SQLITE_FCNTL_OTA 26
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
|
||||
@@ -3760,6 +3760,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
extern int Sqlitemultiplex_Init(Tcl_Interp*);
|
||||
extern int SqliteSuperlock_Init(Tcl_Interp*);
|
||||
extern int SqlitetestSyscall_Init(Tcl_Interp*);
|
||||
extern int SqliteOta_Init(Tcl_Interp*);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
|
||||
@@ -3803,6 +3804,7 @@ static void init_all(Tcl_Interp *interp){
|
||||
Sqlitemultiplex_Init(interp);
|
||||
SqliteSuperlock_Init(interp);
|
||||
SqlitetestSyscall_Init(interp);
|
||||
SqliteOta_Init(interp);
|
||||
|
||||
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
|
||||
Sqlitetestfts3_Init(interp);
|
||||
|
||||
@@ -430,6 +430,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
|
||||
Tcl_SetVar2(interp, "sqlite_options", "or_opt", "1", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_OTA
|
||||
Tcl_SetVar2(interp, "sqlite_options", "ota", "1", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
Tcl_SetVar2(interp, "sqlite_options", "ota", "0", TCL_GLOBAL_ONLY);
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_OMIT_PAGER_PRAGMAS
|
||||
Tcl_SetVar2(interp, "sqlite_options", "pager_pragmas", "0", TCL_GLOBAL_ONLY);
|
||||
#else
|
||||
|
||||
@@ -296,6 +296,7 @@ static void applyAffinity(
|
||||
if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
|
||||
sqlite3VdbeMemStringify(pRec, enc, 1);
|
||||
}
|
||||
pRec->flags &= ~(MEM_Real|MEM_Int);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -603,6 +603,15 @@ do_execsql_test misc1-19.2 {
|
||||
SELECT * FROM t19b;
|
||||
} {4 5 6}
|
||||
|
||||
# 2015-05-20: CREATE TABLE AS should not store INT value is a TEXT
|
||||
# column.
|
||||
#
|
||||
do_execsql_test misc1-19.3 {
|
||||
CREATE TABLE t19c(x TEXT);
|
||||
CREATE TABLE t19d AS SELECT * FROM t19c UNION ALL SELECT 1234;
|
||||
SELECT x, typeof(x) FROM t19d;
|
||||
} {1234 text}
|
||||
|
||||
# 2014-05-16: Tests for the SQLITE_TESTCTRL_FAULT_INSTALL feature.
|
||||
#
|
||||
unset -nocomplain fault_callbacks
|
||||
|
||||
@@ -219,4 +219,14 @@ do_catchsql_test misc4-7.1 {
|
||||
VACUUM;
|
||||
} {1 {unrecognized token: "[M%s%s%s%s%s%s%s%s%s%s%s%s%s"}}
|
||||
|
||||
# 2015-05-18. Use of ephermeral Mem content after the cursor that holds
|
||||
# the canonical content has moved on.
|
||||
#
|
||||
do_execsql_test misc4-7.2 {
|
||||
CREATE TABLE t0(a,b);
|
||||
INSERT INTO t0 VALUES(1,0),(2,0);
|
||||
UPDATE t0 SET b=9 WHERE a AND (SELECT a FROM t0 WHERE a);
|
||||
SELECT * FROM t0 ORDER BY +a;
|
||||
} {1 9 2 9}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2014 September 20
|
||||
#
|
||||
# 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 runs all rtree related tests.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/permutations.test
|
||||
|
||||
ifcapable !ota { finish_test ; return }
|
||||
|
||||
run_test_suite ota
|
||||
finish_test
|
||||
|
||||
@@ -113,7 +113,7 @@ set allquicktests [test_set $alltests -exclude {
|
||||
vtab_err.test walslow.test walcrash.test walcrash3.test
|
||||
walthread.test rtree3.test indexfault.test securedel2.test
|
||||
sort3.test sort4.test fts4growth.test fts4growth2.test
|
||||
bigsort.test
|
||||
bigsort.test ota.test
|
||||
}]
|
||||
if {[info exists ::env(QUICKTEST_INCLUDE)]} {
|
||||
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
|
||||
@@ -939,6 +939,12 @@ test_suite "rtree" -description {
|
||||
All R-tree related tests. Provides coverage of source file rtree.c.
|
||||
} -files [glob -nocomplain $::testdir/../ext/rtree/*.test]
|
||||
|
||||
test_suite "ota" -description {
|
||||
OTA tests.
|
||||
} -files [
|
||||
test_set [glob -nocomplain $::testdir/../ext/ota/*.test] -exclude ota.test
|
||||
]
|
||||
|
||||
test_suite "no_optimization" -description {
|
||||
Run test scripts with optimizations disabled using the
|
||||
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface.
|
||||
|
||||
@@ -116,6 +116,7 @@ array set ::Configs [strip_comments {
|
||||
-DSQLITE_ENABLE_FTS4=1
|
||||
-DSQLITE_ENABLE_RTREE=1
|
||||
-DSQLITE_ENABLE_STAT4
|
||||
-DSQLITE_ENABLE_OTA
|
||||
-DSQLITE_MAX_ATTACHED=125
|
||||
}
|
||||
"Device-One" {
|
||||
|
||||
@@ -909,5 +909,11 @@ do_execsql_test select4-14.14 {
|
||||
do_execsql_test select4-14.15 {
|
||||
SELECT * FROM (SELECT 123), (SELECT 456) ON likely(0 OR 1) OR 0;
|
||||
} {123 456}
|
||||
do_execsql_test select4-14.16 {
|
||||
VALUES(1),(2),(3),(4) UNION ALL SELECT 5 LIMIT 99;
|
||||
} {1 2 3 4 5}
|
||||
do_execsql_test select4-14.17 {
|
||||
VALUES(1),(2),(3),(4) UNION ALL SELECT 5 LIMIT 3;
|
||||
} {1 2 3}
|
||||
|
||||
finish_test
|
||||
|
||||
+10
-1
@@ -43,6 +43,10 @@ static const char zHelp[] =
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef SQLITE_ENABLE_OTA
|
||||
# include "sqlite3ota.h"
|
||||
#endif
|
||||
|
||||
/* All global state is held in this structure */
|
||||
static struct Global {
|
||||
sqlite3 *db; /* The open database connection */
|
||||
@@ -534,7 +538,7 @@ void testset_main(void){
|
||||
speedtest1_exec("COMMIT");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = 10; //g.szTest/5;
|
||||
n = 10; /* g.szTest/5; */
|
||||
speedtest1_begin_test(145, "%d SELECTS w/ORDER BY and LIMIT, unindexed", n);
|
||||
speedtest1_exec("BEGIN");
|
||||
speedtest1_prepare(
|
||||
@@ -1204,6 +1208,11 @@ int main(int argc, char **argv){
|
||||
noSync = 1;
|
||||
}else if( strcmp(z,"notnull")==0 ){
|
||||
g.zNN = "NOT NULL";
|
||||
#ifdef SQLITE_ENABLE_OTA
|
||||
}else if( strcmp(z,"ota")==0 ){
|
||||
sqlite3ota_create_vfs("ota", 0);
|
||||
sqlite3_vfs_register(sqlite3_vfs_find("ota"), 1);
|
||||
#endif
|
||||
}else if( strcmp(z,"pagesize")==0 ){
|
||||
if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
|
||||
pageSize = integerValue(argv[++i]);
|
||||
|
||||
+6
-4
@@ -1342,14 +1342,16 @@ proc crashsql {args} {
|
||||
puts $f "sqlite3_crash_enable 1"
|
||||
puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
|
||||
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
|
||||
puts $f $opendb
|
||||
|
||||
# This block sets the cache size of the main database to 10
|
||||
# pages. This is done in case the build is configured to omit
|
||||
# "PRAGMA cache_size".
|
||||
puts $f {db eval {SELECT * FROM sqlite_master;}}
|
||||
puts $f {set bt [btree_from_db db]}
|
||||
puts $f {btree_set_cache_size $bt 10}
|
||||
if {$opendb!=""} {
|
||||
puts $f $opendb
|
||||
puts $f {db eval {SELECT * FROM sqlite_master;}}
|
||||
puts $f {set bt [btree_from_db db]}
|
||||
puts $f {btree_set_cache_size $bt 10}
|
||||
}
|
||||
|
||||
if {$prngseed} {
|
||||
set seed [expr {$prngseed%10007+1}]
|
||||
|
||||
@@ -712,4 +712,20 @@ do_test trigger1-16.7 {
|
||||
}
|
||||
} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that bug [34cd55d68e0e6e7c] has been fixed.
|
||||
#
|
||||
do_execsql_test trigger1-17.0 {
|
||||
CREATE TABLE t17a(ii INT);
|
||||
CREATE TABLE t17b(tt TEXT PRIMARY KEY, ss);
|
||||
CREATE TRIGGER t17a_ai AFTER INSERT ON t17a BEGIN
|
||||
INSERT INTO t17b(tt) VALUES(new.ii);
|
||||
END;
|
||||
CREATE TRIGGER t17b_ai AFTER INSERT ON t17b BEGIN
|
||||
UPDATE t17b SET ss = 4;
|
||||
END;
|
||||
INSERT INTO t17a(ii) VALUES('1');
|
||||
PRAGMA integrity_check;
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
|
||||
+4
-2
@@ -111,8 +111,9 @@ foreach hdr {
|
||||
pcache.h
|
||||
pragma.h
|
||||
rtree.h
|
||||
sqlite3ext.h
|
||||
sqlite3.h
|
||||
sqlite3ext.h
|
||||
sqlite3ota.h
|
||||
sqliteicu.h
|
||||
sqliteInt.h
|
||||
sqliteLimit.h
|
||||
@@ -214,7 +215,7 @@ proc copy_file {filename} {
|
||||
regsub {^SQLITE_API } $line {} line
|
||||
# Add the SQLITE_PRIVATE or SQLITE_API keyword before functions.
|
||||
# so that linkage can be modified at compile-time.
|
||||
if {[regexp {^sqlite3_} $funcname]} {
|
||||
if {[regexp {^sqlite3(_|ota_)} $funcname]} {
|
||||
set line SQLITE_API
|
||||
append line " " [string trim $rettype]
|
||||
if {[string index $rettype end] ne "*"} {
|
||||
@@ -368,6 +369,7 @@ foreach file {
|
||||
rtree.c
|
||||
icu.c
|
||||
fts3_icu.c
|
||||
sqlite3ota.c
|
||||
dbstat.c
|
||||
} {
|
||||
copy_file tsrc/$file
|
||||
|
||||
Reference in New Issue
Block a user