1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Merge recent trunk changes, include the R-Tree enhancement that allows

8-byte BLOB arguments to geometry functions, and the fix for the TEXT affinity
problem that could cause corrupt indexes.

FossilOrigin-Name: 0a0de8b72ca24f287f9c84766a14e12ea4564b59
This commit is contained in:
drh
2015-05-19 22:42:23 +00:00
52 changed files with 9845 additions and 4583 deletions

View File

@ -345,8 +345,9 @@ SRC += \
SRC += \ SRC += \
$(TOP)/ext/session/sqlite3session.c \ $(TOP)/ext/session/sqlite3session.c \
$(TOP)/ext/session/sqlite3session.h $(TOP)/ext/session/sqlite3session.h
SRC += \
$(TOP)/ext/ota/sqlite3ota.h \
$(TOP)/ext/ota/sqlite3ota.c
# Generated source code files # Generated source code files
# #
@ -403,7 +404,8 @@ TESTSRC = \
$(TOP)/src/test_wsd.c \ $(TOP)/src/test_wsd.c \
$(TOP)/ext/fts3/fts3_term.c \ $(TOP)/ext/fts3/fts3_term.c \
$(TOP)/ext/fts3/fts3_test.c \ $(TOP)/ext/fts3/fts3_test.c \
$(TOP)/ext/session/test_session.c $(TOP)/ext/session/test_session.c \
$(TOP)/ext/ota/test_ota.c
# Statically linked extensions # Statically linked extensions
# #
@ -1059,7 +1061,7 @@ speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.lo
# releasetest.tcl script. # releasetest.tcl script.
# #
checksymbols: sqlite3.lo checksymbols: sqlite3.lo
nm -g --defined-only sqlite3.o | grep -v " sqlite3_" ; test $$? -ne 0 nm -g --defined-only sqlite3.o | egrep -v ' sqlite3(changeset|session)?_' ; test $$? -ne 0
echo '0 errors out of 1 tests' echo '0 errors out of 1 tests'
# Build the amalgamation-autoconf package. # Build the amalgamation-autoconf package.

View File

@ -1762,8 +1762,8 @@ speedtest1.exe: $(TOP)\test\speedtest1.c $(SQLITE3C)
$(TOP)\test\speedtest1.c $(SQLITE3C) $(TOP)\test\speedtest1.c $(SQLITE3C)
clean: clean:
del /Q *.exp *.lo *.ilk *.lib *.obj *.pdb 2>NUL del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL
del /Q *.cod *.da *.bb *.bbg gmon.out 2>NUL del /Q *.bsc *.cod *.da *.bb *.bbg gmon.out 2>NUL
del /Q sqlite3.h opcodes.c opcodes.h 2>NUL del /Q sqlite3.h opcodes.c opcodes.h 2>NUL
del /Q lemon.* lempar.c parse.* 2>NUL del /Q lemon.* lempar.c parse.* 2>NUL
del /Q mkkeywordhash.* keywordhash.h 2>NUL del /Q mkkeywordhash.* keywordhash.h 2>NUL

View File

@ -4,6 +4,10 @@ This repository contains the complete source code for the SQLite database
engine. Some test scripts are also include. However, many other test scripts engine. Some test scripts are also include. However, many other test scripts
and most of the documentation are managed separately. and most of the documentation are managed separately.
If you are reading this on a Git mirror someplace, you are doing it wrong.
The [official repository](https://www.sqlite.org/src/) is better. Go there
now.
## Compiling ## Compiling
First create a directory in which to place First create a directory in which to place

View File

@ -1 +1 @@
3.8.10.1 3.8.11

6443
configure vendored

File diff suppressed because it is too large Load Diff

122
ext/ota/ota.c Normal file
View File

@ -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;
}

680
ext/ota/ota1.test Normal file
View File

@ -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

188
ext/ota/ota10.test Normal file
View File

@ -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

198
ext/ota/ota11.test Normal file
View File

@ -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

172
ext/ota/ota12.test Normal file
View File

@ -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

65
ext/ota/ota13.test Normal file
View File

@ -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

207
ext/ota/ota3.test Normal file
View File

@ -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

331
ext/ota/ota5.test Normal file
View File

@ -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

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

@ -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

110
ext/ota/ota7.test Normal file
View File

@ -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

75
ext/ota/ota8.test Normal file
View File

@ -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

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

@ -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

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

@ -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

149
ext/ota/otacrash.test Normal file
View File

@ -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

237
ext/ota/otafault.test Normal file
View File

@ -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

58
ext/ota/otafault2.test Normal file
View File

@ -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

3661
ext/ota/sqlite3ota.c Normal file

File diff suppressed because it is too large Load Diff

419
ext/ota/sqlite3ota.h Normal file
View File

@ -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 */

247
ext/ota/test_ota.c Normal file
View File

@ -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) */

View File

@ -3401,12 +3401,19 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
pBlob->cb = pGeomCtx[0]; pBlob->cb = pGeomCtx[0];
pBlob->nParam = nArg; pBlob->nParam = nArg;
for(i=0; i<nArg; i++){ 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 #ifdef SQLITE_RTREE_INT_ONLY
pBlob->aParam[i] = sqlite3_value_int64(aArg[i]); pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
#else #else
pBlob->aParam[i] = sqlite3_value_double(aArg[i]); pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
#endif #endif
} }
}
sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free); sqlite3_result_blob(ctx, pBlob, nBlob, sqlite3_free);
} }
} }

View File

@ -2201,7 +2201,7 @@ static void sessionAppendTableHdr(
** occurs, an SQLite error code is returned and both output variables set ** occurs, an SQLite error code is returned and both output variables set
** to 0. ** to 0.
*/ */
int sessionGenerateChangeset( static int sessionGenerateChangeset(
sqlite3_session *pSession, /* Session object */ sqlite3_session *pSession, /* Session object */
int bPatchset, /* True for patchset, false for changeset */ int bPatchset, /* True for patchset, false for changeset */
int (*xOutput)(void *pOut, const void *pData, int nData), int (*xOutput)(void *pOut, const void *pData, int nData),
@ -2420,7 +2420,7 @@ int sqlite3session_isempty(sqlite3_session *pSession){
/* /*
** Do the work for either sqlite3changeset_start() or start_strm(). ** Do the work for either sqlite3changeset_start() or start_strm().
*/ */
int sessionChangesetStart( static int sessionChangesetStart(
sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
int (*xInput)(void *pIn, void *pData, int *pnData), int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn, void *pIn,
@ -4267,7 +4267,7 @@ static int sessionChangesetToHash(
** **
** 3. Write an output changeset based on the contents of the hash table. ** 3. Write an output changeset based on the contents of the hash table.
*/ */
int sessionChangesetConcat( static int sessionChangesetConcat(
sqlite3_changeset_iter *pLeft, sqlite3_changeset_iter *pLeft,
sqlite3_changeset_iter *pRight, sqlite3_changeset_iter *pRight,
int (*xOutput)(void *pOut, const void *pData, int nData), int (*xOutput)(void *pOut, const void *pData, int nData),

14
main.mk
View File

@ -66,7 +66,7 @@ LIBOBJ+= vdbe.o parse.o \
mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
notify.o opcodes.o os.o os_unix.o os_win.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 \ 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 \ table.o threads.o tokenize.o trigger.o \
update.o userauth.o util.o vacuum.o \ update.o userauth.o util.o vacuum.o \
vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
@ -227,6 +227,9 @@ SRC += \
SRC += \ SRC += \
$(TOP)/ext/userauth/userauth.c \ $(TOP)/ext/userauth/userauth.c \
$(TOP)/ext/userauth/sqlite3userauth.h $(TOP)/ext/userauth/sqlite3userauth.h
SRC += \
$(TOP)/ext/ota/sqlite3ota.c \
$(TOP)/ext/ota/sqlite3ota.h
# Generated source code files # Generated source code files
# #
@ -244,6 +247,7 @@ SRC += \
TESTSRC = \ TESTSRC = \
$(TOP)/ext/fts3/fts3_term.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 \
$(TOP)/src/test1.c \ $(TOP)/src/test1.c \
$(TOP)/src/test2.c \ $(TOP)/src/test2.c \
$(TOP)/src/test3.c \ $(TOP)/src/test3.c \
@ -615,6 +619,8 @@ userauth.o: $(TOP)/ext/userauth/userauth.c $(HDR) $(EXTHDR)
sqlite3session.o: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR) sqlite3session.o: $(TOP)/ext/session/sqlite3session.c $(HDR) $(EXTHDR)
$(TCCX) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.c $(TCCX) -DSQLITE_CORE -c $(TOP)/ext/session/sqlite3session.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 # Rules for building test programs and for running tests
# #
@ -752,7 +758,11 @@ wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
$(TOP)/test/wordcount.c sqlite3.c $(TOP)/test/wordcount.c sqlite3.c
speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o 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 # 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 # symbols that do not begin with "sqlite3_". It is run as part of the

View File

@ -1,12 +1,12 @@
C Merge\sin\sthe\sIS-operator\senhancements\sand\sother\srecent\schanges\sfrom\strunk. C Merge\srecent\strunk\schanges,\sinclude\sthe\sR-Tree\senhancement\sthat\sallows\n8-byte\sBLOB\sarguments\sto\sgeometry\sfunctions,\sand\sthe\sfix\sfor\sthe\sTEXT\saffinity\nproblem\sthat\scould\scause\scorrupt\sindexes.
D 2015-05-14T15:17:20.176 D 2015-05-19T22:42:23.384
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 73b64617bc169971c12be153e3ad9ef76e662fe4 F Makefile.in 66db4ec2c6cd63d0f3e29d366aaaab769aba175b
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F Makefile.msc 35200c2f470516f178392b0135375f656fdeac02 F Makefile.msc 3a41532fcdbd3ebd78cf8727d93f84cf35117361
F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858 F Makefile.vxworks e1b65dea203f054e71653415bd8f96dcaed47858
F README.md d58e3bebc0a4145e0f2a87994015fdb575a8e866 F README.md 0bfccb18927349653c09137a458b961fa8ab4cb9
F VERSION 8af05c43e00f7de32be74ff9984d792c96cdb0de F VERSION ce0ae95abd7121c534f6917c1c8f2b70d9acd4db
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811 F addopcodes.awk 9eb448a552d5c0185cf62c463f9c173cedae3811
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
@ -38,7 +38,7 @@ F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63
F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977 F config.guess 226d9a188c6196f3033ffc651cbc9dcee1a42977
F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0 F config.h.in 42b71ad3fe21c9e88fa59e8458ca1a6bc72eb0c0
F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55 F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
F configure dae9f0ec4df32a9e300befbcdbc4ff8874731357 x F configure 35cbd52af707ae22401641fe7b3672f05aea0eb1 x
F configure.ac 0b775d383c536bbaafc1e46dd3cbb81a7ea11aeb F configure.ac 0b775d383c536bbaafc1e46dd3cbb81a7ea11aeb
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1 F doc/lemon.html 334dbf6621b8fb8790297ec1abf3cfa4621709d1
@ -123,8 +123,27 @@ F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 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/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 0c207fd8b814a35537d96681cbf57436e200b75e F ext/rtree/rtree.c 60ec0a71d4d213665a706c795c887e7c4d148635
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9 F ext/rtree/rtree1.test 541bbcab74613907fea08b2ecdcdd5b7aa724cc9
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
@ -161,7 +180,7 @@ F ext/session/sessionC.test 3982f8577b0744c5ce3aaef7cfeb5bd903f17fe4
F ext/session/sessionD.test d4744c78334162851d2a2f285c7e603e31b49aa2 F ext/session/sessionD.test d4744c78334162851d2a2f285c7e603e31b49aa2
F ext/session/session_common.tcl 9de0451b6a47218fc16b9ed8876b6238a0a3d88d F ext/session/session_common.tcl 9de0451b6a47218fc16b9ed8876b6238a0a3d88d
F ext/session/sessionfault.test bef044d0952c0d62c31c8d2400be72c8684545cc F ext/session/sessionfault.test bef044d0952c0d62c31c8d2400be72c8684545cc
F ext/session/sqlite3session.c d22a8996f44202a13d0b1ad18757cd509729a23c F ext/session/sqlite3session.c d961bfc5f4f4c4390b1f3c190096899b167ee2db
F ext/session/sqlite3session.h 8e86f9eec3ed71f1f30eefbe810cbe5bc10b5aa9 F ext/session/sqlite3session.h 8e86f9eec3ed71f1f30eefbe810cbe5bc10b5aa9
F ext/session/test_session.c 187bd344c5ae9d5be85e22ef7c3010f0c17307ce F ext/session/test_session.c 187bd344c5ae9d5be85e22ef7c3010f0c17307ce
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220 F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
@ -170,7 +189,7 @@ F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk a76e778116586db55895d3cc9796bde70bd9db08 F main.mk 4a6b557c390edd263cf975a9d2837d5efb6a1217
F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea F mkopcodec.awk c2ff431854d702cdd2d779c9c0d1f58fa16fa4ea
F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32 F mkopcodeh.awk d5e22023b5238985bb54a72d33e0ac71fe4f8a32
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
@ -215,7 +234,7 @@ F src/legacy.c ba1863ea58c4c840335a84ec276fc2b25e22bc4e
F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770 F src/lempar.c 7274c97d24bb46631e504332ccd3bd1b37841770
F src/loadext.c 29255bbe1cfb2ce9bbff2526a5ecfddcb49b9271 F src/loadext.c 29255bbe1cfb2ce9bbff2526a5ecfddcb49b9271
F src/main.c 3fd7ec2399ce67b7dda1b155925d95b8afe2b9ea F src/main.c 3fd7ec2399ce67b7dda1b155925d95b8afe2b9ea
F src/malloc.c 5bc15d525811d387b37c29f2e368143460e41e96 F src/malloc.c 908c780fdddd472163c2d1b1820ae4081f01ad20
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987 F src/mem1.c abe6ee469b6c5a35c7f22bfeb9c9bac664a1c987
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3 F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
@ -244,21 +263,21 @@ F src/pcache.h b44658c9c932d203510279439d891a2a83e12ba8
F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9 F src/pcache1.c 69d137620a305f814398bd29a0c998038c0695e9
F src/pragma.c c1f4d012ea9f6b1ce52d341b2cd0ad72d560afd7 F src/pragma.c c1f4d012ea9f6b1ce52d341b2cd0ad72d560afd7
F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f F src/pragma.h 09c89bca58e9a44de2116cc8272b8d454657129f
F src/prepare.c 1fffbdcd6f8a0173a8f70d71f22528f4c0e1e3d3 F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1
F src/printf.c 54dd6dce95454fadffa3ebf7717c5f6c06250d1d F src/printf.c 13ce37e5574f9b0682fa86dbcf9faf76b9d82a15
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c 99eabf7eff0bfa65b75939b46caa82e2b2133f28 F src/resolve.c 99eabf7eff0bfa65b75939b46caa82e2b2133f28
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c 1b0bfc7d59e48c26b895a6b719157111a617d9e3 F src/select.c 6d9d6ae899acb9bf148862e8cccdf16085514b26
F src/shell.c f26cca96f7dadab5efb5e655edf548f4b91695c5 F src/shell.c f26cca96f7dadab5efb5e655edf548f4b91695c5
F src/sqlite.h.in 0d711b199290c187b6013bc6b519ddc8ec36d4d8 F src/sqlite.h.in 6805658a16c77dc153131c2dd7e39cc766bd9454
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h 5f7643182e9da425d16a9ea4ad2264adf77c2c4e F src/sqliteInt.h 88f58e88856dcfed5b03ae020335e3a562c91f29
F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46
F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179 F src/status.c f266ad8a2892d659b74f0f50cb6a88b6e7c12179
F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e
F src/tclsqlite.c 3e98ca80d7c0b66c2e89721ee263d1f396fad816 F src/tclsqlite.c 060e6398f47a187ec707030e6306c730a48c845d
F src/test1.c a8e09b811f70184ce65012f27f30cfee7e54f268 F src/test1.c a8e09b811f70184ce65012f27f30cfee7e54f268
F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d F src/test2.c 577961fe48961b2f2e5c8b56ee50c3f459d3359d
F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622 F src/test3.c 64d2afdd68feac1bb5e2ffb8226c8c639f798622
@ -273,7 +292,7 @@ F src/test_autoext.c dea8a01a7153b9adc97bd26161e4226329546e12
F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803
F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07 F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07
F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f
F src/test_config.c 5140cf3a0b766d177dfb9f8f2a3b00224ec3f5c8 F src/test_config.c 32606543e66d128e8891aa750ab37791211e5623
F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852
F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc
F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f
@ -305,17 +324,17 @@ F src/test_vfs.c 3b65d42e18b262805716bd96178c81da8f2d9283
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698 F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481 F src/threads.c 6bbcc9fe50c917864d48287b4792d46d6e873481
F src/tokenize.c b15511a2396641792f386ceb440d1d922972a78e F src/tokenize.c af8cbbca6db6b664ffecafa236b06629ef6d35c4
F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f
F src/update.c d207deb7a031f698104bee879de0632b611e72dd F src/update.c d207deb7a031f698104bee879de0632b611e72dd
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c a6431c92803b975b7322724a7b433e538d243539 F src/util.c a6431c92803b975b7322724a7b433e538d243539
F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701
F src/vdbe.c f3033cd2b37e730574cc1865d2ba73766d768065 F src/vdbe.c e4b07daec26aaeb3700308f82770485f0a28a988
F src/vdbe.h 01d8c35cb877faca74331bb690f0327493c2cb50 F src/vdbe.h 01d8c35cb877faca74331bb690f0327493c2cb50
F src/vdbeInt.h 96e4303a96c6f983e36e1fe32657b2c547f5c8f1 F src/vdbeInt.h 0c025e70881b42896c0a7575f4b1191a2f3ea5f9
F src/vdbeapi.c d95f2bb43d01a91d93231cde181811b38182202e F src/vdbeapi.c d95f2bb43d01a91d93231cde181811b38182202e
F src/vdbeaux.c 4ccd9105cca5c2cbc0481034fd468e3aa184f48f F src/vdbeaux.c 38233cc7faa4ad1dfbc1a9bc5a54ecf996d6840b
F src/vdbeblob.c ab33f9b57cfce7dddb23853090186da614be4846 F src/vdbeblob.c ab33f9b57cfce7dddb23853090186da614be4846
F src/vdbemem.c eda55a13cfaa797f89ef243a129f3f5a457719e7 F src/vdbemem.c eda55a13cfaa797f89ef243a129f3f5a457719e7
F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b F src/vdbesort.c f5009e7a35e3065635d8918b9a31f498a499976b
@ -325,7 +344,7 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113 F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 08fadd0d211699348349be5449f3a1e391adf20e F src/where.c e71eae3b1383249c3b5d136d6b71ca8d28fb8d61
F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435 F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -768,7 +787,7 @@ F test/minmax4.test 936941484ebdceb8adec7c86b6cd9b6e5e897c1f
F test/misc1.test 783ba75743b2cf71e0f646bf540a6cef57264811 F test/misc1.test 783ba75743b2cf71e0f646bf540a6cef57264811
F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d F test/misc2.test 00d7de54eda90e237fc9a38b9e5ccc769ebf6d4d
F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d F test/misc3.test cf3dda47d5dda3e53fc5804a100d3c82be736c9d
F test/misc4.test 9c078510fbfff05a9869a0b6d8b86a623ad2c4f6 F test/misc4.test 0d8be3466adf123a7791a66ba2bc8e8d229e87f3
F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1 F test/misc5.test f96428ea95b3820aafc6f1c50cf48a09e4597ee1
F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2 F test/misc7.test edd0b63e2ee29a256900b0514f6fff27e19e9bb2
@ -803,6 +822,7 @@ F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da F test/orderby7.test 3d1383d52ade5b9eb3a173b3147fdd296f0202da
F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd F test/orderby8.test 23ef1a5d72bd3adcc2f65561c654295d1b8047bd
F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7 F test/oserror.test 14fec2796c2b6fe431c7823750e8a18a761176d7
F test/ota.test 3a8d97cbf8f7210dc6a638797c4e4cd674036927
F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799 F test/ovfl.test 4f7ca651cba5c059a12d8c67dddd49bec5747799
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71 F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
@ -816,7 +836,7 @@ F test/pagesize.test 5769fc62d8c890a83a503f67d47508dfdc543305
F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
F test/permutations.test e5e9f708879336e10095ded8d61966007f4a2c5c F test/permutations.test 7019d6cb0452372b4b5475ebd145a65f746f1f1b
F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2 F test/pragma.test be7195f0aa72bdb8a512133e9640ac40f15b57a2
F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028 F test/pragma2.test f624a496a95ee878e81e59961eade66d5c00c028
F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c F test/pragma3.test 6f849ccffeee7e496d2f2b5e74152306c0b8757c
@ -835,7 +855,7 @@ F test/randexpr1.test eda062a97e60f9c38ae8d806b03b0ddf23d796df
F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8 F test/rdonly.test 64e2696c322e3538df0b1ed624e21f9a23ed9ff8
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8 F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254 F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.tcl 7ad4fd49ae50c41ec7781815bdda8a8b278781d4 F test/releasetest.tcl e340abab899d4b8bdd87ff434e10a83ebe38c5ac
F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb F test/resolver01.test f4022acafda7f4d40eca94dbf16bc5fc4ac30ceb
F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea F test/rollback.test 458fe73eb3ffdfdf9f6ba3e9b7350a6220414dea
F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14 F test/rollback2.test fc14cf6d1a2b250d2735ef16124b971bce152f14
@ -919,7 +939,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715 F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/speedtest1.c 2b416dca3a155fcaa849540b2e7fc1df12896c23 F test/speedtest1.c 9f1b745c24886cced3f70ffc666300152a39013c
F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49 F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49
F test/sqldiff1.test e5ecfe95b3a2ff6380f0db6ea8bec246b675e122 F test/sqldiff1.test e5ecfe95b3a2ff6380f0db6ea8bec246b675e122
F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68 F test/sqllimits1.test e05786eaed7950ff6a2d00031d001d8a26131e68
@ -941,7 +961,7 @@ F test/tclsqlite.test 7179b4e0bf236ddf0bfa6bfaefa76fbe0a23c28a
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30 F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1 F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
F test/tester.tcl 1769622cf6e9750530c4f08897f843a48a22b10f F test/tester.tcl 60a09b25c1c1b96ccd3a697bbf80e7c2402c94db
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@ -1101,8 +1121,8 @@ F test/trace2.test f5cb67ad3bc09e0c58e8cca78dfd0b5639259983
F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6 F test/trans.test 6e1b4c6a42dba31bd65f8fa5e61a2708e08ddde6
F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76 F test/trans2.test 62bd045bfc7a1c14c5ba83ba64d21ade31583f76
F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94 F test/trans3.test 91a100e5412b488e22a655fe423a14c26403ab94
F test/transitive1.test 875a9f0097a15b30a62431d183f989364d5accac F test/transitive1.test 293300f46916569f08875cdb2fe2134be2c27677
F test/trigger1.test dc47573ac79ffe0ee3eecaa517d70d8dacbccd03 F test/trigger1.test ea9624cc1dae05645469df6119fa815f9e6f1e8c
F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6 F test/trigger2.test 5cd7d69a7ba1143ee045e4ae2963ff32ae4c87a6
F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945 F test/trigger3.test aa640bb2bbb03edd5ff69c055117ea088f121945
F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359 F test/trigger4.test 74700b76ebf3947b2f7a92405141eb2cf2a5d359
@ -1187,7 +1207,7 @@ F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
F test/where.test 1ff3d9f8da0a6c0dc5ccfd38d9225b2cdb5b6afb F test/where.test 1ff3d9f8da0a6c0dc5ccfd38d9225b2cdb5b6afb
F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e
F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e
F test/where4.test a4603fa0d018bd4b9430dac840c9c522af421dd5 F test/where4.test 68aa5ad796e33816db2078bc0f6de719c7a0e21f
F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8 F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8
@ -1242,7 +1262,7 @@ F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d F tool/mkpragmatab.tcl 94f196c9961e0ca3513e29f57125a3197808be2d
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e F tool/mksqlite3c-noext.tcl 69bae8ce4aa52d2ff82d4a8a856bf283ec035b2e
F tool/mksqlite3c.tcl afdd92627c1fb31088ba798761f4701f675a4bf5 F tool/mksqlite3c.tcl 508aac36881e12c4f6e5eb72d9920d1fbc1a1216
F tool/mksqlite3h.tcl 96d92fcac21c6037d9db20c7cb2e06b534b550ac F tool/mksqlite3h.tcl 96d92fcac21c6037d9db20c7cb2e06b534b550ac
F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b
F tool/mkvsix.tcl 3b58b9398f91c7dbf18d49eb87cefeee9efdbce1 F tool/mkvsix.tcl 3b58b9398f91c7dbf18d49eb87cefeee9efdbce1
@ -1277,7 +1297,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 0404ef885e47f0df1daa04feb28452beb4d32a35 3428043cd00294457548bb07ada2ad526b6532d6 P 4f9229445c293b39c80b2a662901f608c85b36ef b271ed56532a78323accc8a7cb348d55f95c350e
R 25710cfb819c9f25a12055b84606adbc R 8a8bf6521434fdcdf694b65ab11c8823
U drh U drh
Z 9620c8120a371d7867c988796203e7a5 Z 32cff82852bbec32d5276c785011fff4

View File

@ -1 +1 @@
4f9229445c293b39c80b2a662901f608c85b36ef 0a0de8b72ca24f287f9c84766a14e12ea4564b59

View File

@ -771,19 +771,11 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
} }
/* /*
** Create a string from the zFromat argument and the va_list that follows. ** Free any prior content in *pz and replace it with a copy of zNew.
** Store the string in memory obtained from sqliteMalloc() and make *pz
** point to that string.
*/ */
void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){ void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
va_list ap;
char *z;
va_start(ap, zFormat);
z = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
sqlite3DbFree(db, *pz); sqlite3DbFree(db, *pz);
*pz = z; *pz = sqlite3DbStrDup(db, zNew);
} }
/* /*

View File

@ -26,13 +26,13 @@ static void corruptSchema(
){ ){
sqlite3 *db = pData->db; sqlite3 *db = pData->db;
if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){ if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
char *z;
if( zObj==0 ) zObj = "?"; if( zObj==0 ) zObj = "?";
sqlite3SetString(pData->pzErrMsg, db, z = sqlite3_mprintf("malformed database schema (%s)", zObj);
"malformed database schema (%s)", zObj); if( z && zExtra ) z = sqlite3_mprintf("%z - %s", z, zExtra);
if( zExtra ){ sqlite3DbFree(db, *pData->pzErrMsg);
*pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg, *pData->pzErrMsg = z;
"%s - %s", *pData->pzErrMsg, zExtra); if( z==0 ) db->mallocFailed = 1;
}
} }
pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
} }
@ -224,7 +224,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); rc = sqlite3BtreeBeginTrans(pDb->pBt, 0);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
goto initone_error_out; goto initone_error_out;
} }
openedTransaction = 1; openedTransaction = 1;

View File

@ -931,24 +931,6 @@ char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
return z; return z;
} }
/*
** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
** the string and before returning. This routine is intended to be used
** to modify an existing string. For example:
**
** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
**
*/
char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
va_list ap;
char *z;
va_start(ap, zFormat);
z = sqlite3VMPrintf(db, zFormat, ap);
va_end(ap);
sqlite3DbFree(db, zStr);
return z;
}
/* /*
** Print into memory obtained from sqlite3_malloc(). Omit the internal ** Print into memory obtained from sqlite3_malloc(). Omit the internal
** %-conversion extensions. ** %-conversion extensions.

View File

@ -713,8 +713,13 @@ static void selectInnerLoop(
/* If the destination is an EXISTS(...) expression, the actual /* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required. ** values returned by the SELECT are not required.
*/ */
sqlite3ExprCodeExprList(pParse, pEList, regResult, u8 ecelFlags;
(eDest==SRT_Output||eDest==SRT_Coroutine)?SQLITE_ECEL_DUP:0); 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 /* If the DISTINCT keyword was present on the SELECT statement

View File

@ -956,13 +956,21 @@ struct sqlite3_io_methods {
** pointed to by the pArg argument. This capability is used during testing ** pointed to by the pArg argument. This capability is used during testing
** and only needs to be supported when SQLITE_TEST is defined. ** 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 ** 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 ** be advantageous to block on the next WAL lock if the lock is not immediately
** available. The WAL subsystem issues this signal during rare ** available. The WAL subsystem issues this signal during rare
** circumstances in order to fix a problem with priority inversion. ** circumstances in order to fix a problem with priority inversion.
** Applications should <em>not</em> use this file-control. ** 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> ** </ul>
*/ */
#define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_FCNTL_LOCKSTATE 1
@ -988,6 +996,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_COMMIT_PHASETWO 22 #define SQLITE_FCNTL_COMMIT_PHASETWO 22
#define SQLITE_FCNTL_WIN32_SET_HANDLE 23 #define SQLITE_FCNTL_WIN32_SET_HANDLE 23
#define SQLITE_FCNTL_WAL_BLOCK 24 #define SQLITE_FCNTL_WAL_BLOCK 24
#define SQLITE_FCNTL_ZIPVFS 25
#define SQLITE_FCNTL_OTA 26
/* deprecated names */ /* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE

View File

@ -3163,7 +3163,6 @@ void sqlite3VXPrintf(StrAccum*, u32, const char*, va_list);
void sqlite3XPrintf(StrAccum*, u32, const char*, ...); void sqlite3XPrintf(StrAccum*, u32, const char*, ...);
char *sqlite3MPrintf(sqlite3*,const char*, ...); char *sqlite3MPrintf(sqlite3*,const char*, ...);
char *sqlite3VMPrintf(sqlite3*,const char*, va_list); char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
void sqlite3DebugPrintf(const char*, ...); void sqlite3DebugPrintf(const char*, ...);
#endif #endif
@ -3182,7 +3181,7 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#endif #endif
void sqlite3SetString(char **, sqlite3*, const char*, ...); void sqlite3SetString(char **, sqlite3*, const char*);
void sqlite3ErrorMsg(Parse*, const char*, ...); void sqlite3ErrorMsg(Parse*, const char*, ...);
int sqlite3Dequote(char*); int sqlite3Dequote(char*);
int sqlite3KeywordCode(const unsigned char*, int); int sqlite3KeywordCode(const unsigned char*, int);

View File

@ -3909,6 +3909,7 @@ static void init_all(Tcl_Interp *interp){
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
extern int TestSession_Init(Tcl_Interp*); extern int TestSession_Init(Tcl_Interp*);
#endif #endif
extern int SqliteOta_Init(Tcl_Interp*);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
extern int Sqlitetestfts3_Init(Tcl_Interp *interp); extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
@ -3955,6 +3956,7 @@ static void init_all(Tcl_Interp *interp){
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK) #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
TestSession_Init(interp); TestSession_Init(interp);
#endif #endif
SqliteOta_Init(interp);
#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
Sqlitetestfts3_Init(interp); Sqlitetestfts3_Init(interp);

View File

@ -436,6 +436,12 @@ Tcl_SetVar2(interp, "sqlite_options", "mergesort", "1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "sqlite_options", "or_opt", "1", TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "sqlite_options", "or_opt", "1", TCL_GLOBAL_ONLY);
#endif #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 #ifdef SQLITE_OMIT_PAGER_PRAGMAS
Tcl_SetVar2(interp, "sqlite_options", "pager_pragmas", "0", TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "sqlite_options", "pager_pragmas", "0", TCL_GLOBAL_ONLY);
#else #else

View File

@ -472,7 +472,7 @@ abort_parse:
pParse->rc = SQLITE_NOMEM; pParse->rc = SQLITE_NOMEM;
} }
if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc)); pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
} }
assert( pzErrMsg!=0 ); assert( pzErrMsg!=0 );
if( pParse->zErrMsg ){ if( pParse->zErrMsg ){

View File

@ -306,6 +306,7 @@ static void applyAffinity(
if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){ if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
sqlite3VdbeMemStringify(pRec, enc, 1); sqlite3VdbeMemStringify(pRec, enc, 1);
} }
pRec->flags &= ~(MEM_Real|MEM_Int);
} }
} }
@ -977,12 +978,11 @@ case OP_Halt: {
assert( zType!=0 || pOp->p4.z!=0 ); assert( zType!=0 || pOp->p4.z!=0 );
zLogFmt = "abort at %d in [%s]: %s"; zLogFmt = "abort at %d in [%s]: %s";
if( zType && pOp->p4.z ){ if( zType && pOp->p4.z ){
sqlite3SetString(&p->zErrMsg, db, "%s constraint failed: %s", sqlite3VdbeError(p, "%s constraint failed: %s", zType, pOp->p4.z);
zType, pOp->p4.z);
}else if( pOp->p4.z ){ }else if( pOp->p4.z ){
sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); sqlite3VdbeError(p, "%s", pOp->p4.z);
}else{ }else{
sqlite3SetString(&p->zErrMsg, db, "%s constraint failed", zType); sqlite3VdbeError(p, "%s constraint failed", zType);
} }
sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg); sqlite3_log(pOp->p1, zLogFmt, pcx, p->zSql, p->zErrMsg);
} }
@ -1614,7 +1614,7 @@ case OP_Function: {
/* If the function returned an error, throw an exception */ /* If the function returned an error, throw an exception */
if( ctx.fErrorOrAux ){ if( ctx.fErrorOrAux ){
if( ctx.isError ){ if( ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(ctx.pOut)); sqlite3VdbeError(p, "%s", sqlite3_value_text(ctx.pOut));
rc = ctx.isError; rc = ctx.isError;
} }
sqlite3VdbeDeleteAuxData(p, (int)(pOp - aOp), pOp->p1); sqlite3VdbeDeleteAuxData(p, (int)(pOp - aOp), pOp->p1);
@ -2801,8 +2801,7 @@ case OP_Savepoint: {
/* A new savepoint cannot be created if there are active write /* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles). ** statements (i.e. open read/write incremental blob handles).
*/ */
sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - " sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress");
"SQL statements in progress");
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else{ }else{
nName = sqlite3Strlen30(zName); nName = sqlite3Strlen30(zName);
@ -2853,15 +2852,14 @@ case OP_Savepoint: {
iSavepoint++; iSavepoint++;
} }
if( !pSavepoint ){ if( !pSavepoint ){
sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", zName); sqlite3VdbeError(p, "no such savepoint: %s", zName);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
}else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){ }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
/* It is not possible to release (commit) a savepoint if there are /* It is not possible to release (commit) a savepoint if there are
** active write statements. ** active write statements.
*/ */
sqlite3SetString(&p->zErrMsg, db, sqlite3VdbeError(p, "cannot release savepoint - "
"cannot release savepoint - SQL statements in progress" "SQL statements in progress");
);
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else{ }else{
@ -2967,22 +2965,11 @@ case OP_AutoCommit: {
assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
assert( p->bIsReader ); assert( p->bIsReader );
#if 0
if( turnOnAC && iRollback && db->nVdbeActive>1 ){
/* If this instruction implements a ROLLBACK and other VMs are
** still running, and a transaction is active, return an error indicating
** that the other VMs must complete first.
*/
sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
}else
#endif
if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){ if( turnOnAC && !iRollback && db->nVdbeWrite>0 ){
/* If this instruction implements a COMMIT and other VMs are writing /* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first. ** return an error indicating that the other VMs must complete first.
*/ */
sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - " sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress"); "SQL statements in progress");
rc = SQLITE_BUSY; rc = SQLITE_BUSY;
}else if( desiredAutoCommit!=db->autoCommit ){ }else if( desiredAutoCommit!=db->autoCommit ){
@ -3010,7 +2997,7 @@ case OP_AutoCommit: {
} }
goto vdbe_return; goto vdbe_return;
}else{ }else{
sqlite3SetString(&p->zErrMsg, db, sqlite3VdbeError(p,
(!desiredAutoCommit)?"cannot start a transaction within a transaction":( (!desiredAutoCommit)?"cannot start a transaction within a transaction":(
(iRollback)?"cannot rollback - no transaction is active": (iRollback)?"cannot rollback - no transaction is active":
"cannot commit - no transaction is active")); "cannot commit - no transaction is active"));
@ -5493,7 +5480,7 @@ case OP_Program: { /* jump */
if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion"); sqlite3VdbeError(p, "too many levels of trigger recursion");
break; break;
} }
@ -5796,7 +5783,7 @@ case OP_AggStep: {
ctx.skipFlag = 0; ctx.skipFlag = 0;
(ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */ (ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
if( ctx.isError ){ if( ctx.isError ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&t)); sqlite3VdbeError(p, "%s", sqlite3_value_text(&t));
rc = ctx.isError; rc = ctx.isError;
} }
if( ctx.skipFlag ){ if( ctx.skipFlag ){
@ -5828,7 +5815,7 @@ case OP_AggFinal: {
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc); rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
if( rc ){ if( rc ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem)); sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
} }
sqlite3VdbeChangeEncoding(pMem, encoding); sqlite3VdbeChangeEncoding(pMem, encoding);
UPDATE_MAX_BLOBSIZE(pMem); UPDATE_MAX_BLOBSIZE(pMem);
@ -5933,7 +5920,7 @@ case OP_JournalMode: { /* out2 */
){ ){
if( !db->autoCommit || db->nVdbeRead>1 ){ if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db, sqlite3VdbeError(p,
"cannot change %s wal mode from within a transaction", "cannot change %s wal mode from within a transaction",
(eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of") (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
); );
@ -6064,7 +6051,7 @@ case OP_TableLock: {
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
if( (rc&0xFF)==SQLITE_LOCKED ){ if( (rc&0xFF)==SQLITE_LOCKED ){
const char *z = pOp->p4.z; const char *z = pOp->p4.z;
sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); sqlite3VdbeError(p, "database table is locked: %s", z);
} }
} }
break; break;
@ -6612,7 +6599,7 @@ vdbe_return:
** is encountered. ** is encountered.
*/ */
too_big: too_big:
sqlite3SetString(&p->zErrMsg, db, "string or blob too big"); sqlite3VdbeError(p, "string or blob too big");
rc = SQLITE_TOOBIG; rc = SQLITE_TOOBIG;
goto vdbe_error_halt; goto vdbe_error_halt;
@ -6620,7 +6607,7 @@ too_big:
*/ */
no_mem: no_mem:
db->mallocFailed = 1; db->mallocFailed = 1;
sqlite3SetString(&p->zErrMsg, db, "out of memory"); sqlite3VdbeError(p, "out of memory");
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto vdbe_error_halt; goto vdbe_error_halt;
@ -6631,7 +6618,7 @@ abort_due_to_error:
assert( p->zErrMsg==0 ); assert( p->zErrMsg==0 );
if( db->mallocFailed ) rc = SQLITE_NOMEM; if( db->mallocFailed ) rc = SQLITE_NOMEM;
if( rc!=SQLITE_IOERR_NOMEM ){ if( rc!=SQLITE_IOERR_NOMEM ){
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc)); sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
} }
goto vdbe_error_halt; goto vdbe_error_halt;
@ -6642,6 +6629,6 @@ abort_due_to_interrupt:
assert( db->u1.isInterrupted ); assert( db->u1.isInterrupted );
rc = SQLITE_INTERRUPT; rc = SQLITE_INTERRUPT;
p->rc = rc; p->rc = rc;
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc)); sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
goto vdbe_error_halt; goto vdbe_error_halt;
} }

View File

@ -410,6 +410,7 @@ struct PreUpdate {
/* /*
** Function prototypes ** Function prototypes
*/ */
void sqlite3VdbeError(Vdbe*, const char *, ...);
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*); void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int); void sqliteVdbePopStack(Vdbe*,int);
int sqlite3VdbeCursorMoveto(VdbeCursor*); int sqlite3VdbeCursorMoveto(VdbeCursor*);

View File

@ -38,6 +38,17 @@ Vdbe *sqlite3VdbeCreate(Parse *pParse){
return p; return p;
} }
/*
** Change the error string stored in Vdbe.zErrMsg
*/
void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){
va_list ap;
sqlite3DbFree(p->db, p->zErrMsg);
va_start(ap, zFormat);
p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap);
va_end(ap);
}
/* /*
** Remember the SQL string for a prepared statement. ** Remember the SQL string for a prepared statement.
*/ */
@ -1395,7 +1406,7 @@ int sqlite3VdbeList(
}else if( db->u1.isInterrupted ){ }else if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT; p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{ }else{
char *zP4; char *zP4;
Op *pOp; Op *pOp;
@ -2298,7 +2309,7 @@ int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
){ ){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY; p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort; p->errorAction = OE_Abort;
sqlite3SetString(&p->zErrMsg, db, "FOREIGN KEY constraint failed"); sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
return SQLITE_ERROR; return SQLITE_ERROR;
} }
return SQLITE_OK; return SQLITE_OK;

View File

@ -636,7 +636,7 @@ static void exprAnalyzeAll(
** In order for the operator to be optimizible, the RHS must be a string ** In order for the operator to be optimizible, the RHS must be a string
** literal that does not begin with a wildcard. The LHS must be a column ** literal that does not begin with a wildcard. The LHS must be a column
** that may only be NULL, a string, or a BLOB, never a number. (This means ** that may only be NULL, a string, or a BLOB, never a number. (This means
** that virtual tables cannot participate in the LIKE optimization.) If the ** that virtual tables cannot participate in the LIKE optimization.) The
** collating sequence for the column on the LHS must be appropriate for ** collating sequence for the column on the LHS must be appropriate for
** the operator. ** the operator.
*/ */
@ -1182,6 +1182,46 @@ static void exprAnalyzeOrTerm(
} }
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */ #endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
/*
** We already know that pExpr is a binary operator where both operands are
** column references. This routine checks to see if pExpr is an equivalence
** relation:
** 1. The SQLITE_Transitive optimization must be enabled
** 2. Must be either an == or an IS operator
** 3. Not originating the ON clause of an OUTER JOIN
** 4. The affinities of A and B must be compatible
** 5a. Both operands use the same collating sequence OR
** 5b. The overall collating sequence is BINARY
** If this routine returns TRUE, that means that the RHS can be substituted
** for the LHS anyplace else in the WHERE clause where the LHS column occurs.
** This is an optimization. No harm comes from returning 0. But if 1 is
** returned when it should not be, then incorrect answers might result.
*/
static int termIsEquivalence(Parse *pParse, Expr *pExpr){
char aff1, aff2;
CollSeq *pColl;
const char *zColl1, *zColl2;
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
aff1 = sqlite3ExprAffinity(pExpr->pLeft);
aff2 = sqlite3ExprAffinity(pExpr->pRight);
if( aff1!=aff2
&& (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2))
){
return 0;
}
pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
/* Since pLeft and pRight are both a column references, their collating
** sequence should always be defined. */
zColl1 = ALWAYS(pColl) ? pColl->zName : 0;
pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
zColl2 = ALWAYS(pColl) ? pColl->zName : 0;
return sqlite3StrICmp(zColl1, zColl2)==0;
}
/* /*
** The input to this routine is an WhereTerm structure with only the ** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in. The job of this routine is to analyze the ** "pExpr" field filled in. The job of this routine is to analyze the
@ -1276,16 +1316,14 @@ static void exprAnalyze(
if( idxNew==0 ) return; if( idxNew==0 ) return;
pNew = &pWC->a[idxNew]; pNew = &pWC->a[idxNew];
markTermAsChild(pWC, idxNew, idxTerm); markTermAsChild(pWC, idxNew, idxTerm);
if( op==TK_IS ) pNew->wtFlags |= TERM_IS;
pTerm = &pWC->a[idxTerm]; pTerm = &pWC->a[idxTerm];
pTerm->wtFlags |= TERM_COPIED; pTerm->wtFlags |= TERM_COPIED;
if( (op==TK_EQ || op==TK_IS)
&& !ExprHasProperty(pExpr, EP_FromJoin) if( termIsEquivalence(pParse, pDup) ){
&& OptimizationEnabled(db, SQLITE_Transitive)
){
pTerm->eOperator |= WO_EQUIV; pTerm->eOperator |= WO_EQUIV;
eExtraOp = WO_EQUIV; eExtraOp = WO_EQUIV;
} }
if( op==TK_IS ) pNew->wtFlags |= TERM_IS;
}else{ }else{
pDup = pExpr; pDup = pExpr;
pNew = pTerm; pNew = pTerm;
@ -6330,13 +6368,15 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */ pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
}else{ }else{
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int opMask;
assert( pLoop->aLTermSpace==pLoop->aLTerm ); assert( pLoop->aLTermSpace==pLoop->aLTerm );
if( !IsUniqueIndex(pIdx) if( !IsUniqueIndex(pIdx)
|| pIdx->pPartIdxWhere!=0 || pIdx->pPartIdxWhere!=0
|| pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
) continue; ) continue;
opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ;
for(j=0; j<pIdx->nKeyCol; j++){ for(j=0; j<pIdx->nKeyCol; j++){
pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ|WO_IS, pIdx); pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, opMask, pIdx);
if( pTerm==0 ) break; if( pTerm==0 ) break;
testcase( pTerm->eOperator & WO_IS ); testcase( pTerm->eOperator & WO_IS );
pLoop->aLTerm[j] = pTerm; pLoop->aLTerm[j] = pTerm;

View File

@ -208,4 +208,25 @@ do_test misc4-6.2 {
} }
} {1} } {1}
# 2015-05-15. Error message formatting problem.
#
db close
sqlite3 db :memory:
do_catchsql_test misc4-7.1 {
CREATE TABLE t7(x);
PRAGMA writable_schema=ON;
UPDATE sqlite_master SET sql='CREATE TABLE [M%s%s%s%s%s%s%s%s%s%s%s%s%s';
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 finish_test

18
test/ota.test Normal file
View File

@ -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

View File

@ -116,7 +116,7 @@ set allquicktests [test_set $alltests -exclude {
vtab_err.test walslow.test walcrash.test walcrash3.test vtab_err.test walslow.test walcrash.test walcrash3.test
walthread.test rtree3.test indexfault.test securedel2.test walthread.test rtree3.test indexfault.test securedel2.test
sort3.test sort4.test fts4growth.test fts4growth2.test sort3.test sort4.test fts4growth.test fts4growth2.test
bigsort.test bigsort.test ota.test
}] }]
if {[info exists ::env(QUICKTEST_INCLUDE)]} { if {[info exists ::env(QUICKTEST_INCLUDE)]} {
set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)] set allquicktests [concat $allquicktests $::env(QUICKTEST_INCLUDE)]
@ -962,6 +962,12 @@ test_suite "session_strm" -description {
set ::sqlite3session_streams 1 set ::sqlite3session_streams 1
} }
test_suite "ota" -description {
OTA tests.
} -files [
test_set [glob -nocomplain $::testdir/../ext/ota/*.test] -exclude ota.test
]
test_suite "no_optimization" -description { test_suite "no_optimization" -description {
Run test scripts with optimizations disabled using the Run test scripts with optimizations disabled using the
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface. sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface.

View File

@ -116,6 +116,7 @@ array set ::Configs [strip_comments {
-DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_FTS4=1
-DSQLITE_ENABLE_RTREE=1 -DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_STAT4
-DSQLITE_ENABLE_OTA
-DSQLITE_MAX_ATTACHED=125 -DSQLITE_MAX_ATTACHED=125
} }
"Device-One" { "Device-One" {

View File

@ -43,6 +43,10 @@ static const char zHelp[] =
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#ifdef SQLITE_ENABLE_OTA
# include "sqlite3ota.h"
#endif
/* All global state is held in this structure */ /* All global state is held in this structure */
static struct Global { static struct Global {
sqlite3 *db; /* The open database connection */ sqlite3 *db; /* The open database connection */
@ -534,7 +538,7 @@ void testset_main(void){
speedtest1_exec("COMMIT"); speedtest1_exec("COMMIT");
speedtest1_end_test(); 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_begin_test(145, "%d SELECTS w/ORDER BY and LIMIT, unindexed", n);
speedtest1_exec("BEGIN"); speedtest1_exec("BEGIN");
speedtest1_prepare( speedtest1_prepare(
@ -1204,6 +1208,11 @@ int main(int argc, char **argv){
noSync = 1; noSync = 1;
}else if( strcmp(z,"notnull")==0 ){ }else if( strcmp(z,"notnull")==0 ){
g.zNN = "NOT NULL"; 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 ){ }else if( strcmp(z,"pagesize")==0 ){
if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
pageSize = integerValue(argv[++i]); pageSize = integerValue(argv[++i]);

View File

@ -1348,14 +1348,16 @@ proc crashsql {args} {
puts $f "sqlite3_crash_enable 1" puts $f "sqlite3_crash_enable 1"
puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile" puts $f "sqlite3_crashparams $blocksize $dc $crashdelay $cfile"
puts $f "sqlite3_test_control_pending_byte $::sqlite_pending_byte" 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 # This block sets the cache size of the main database to 10
# pages. This is done in case the build is configured to omit # pages. This is done in case the build is configured to omit
# "PRAGMA cache_size". # "PRAGMA cache_size".
if {$opendb!=""} {
puts $f $opendb
puts $f {db eval {SELECT * FROM sqlite_master;}} puts $f {db eval {SELECT * FROM sqlite_master;}}
puts $f {set bt [btree_from_db db]} puts $f {set bt [btree_from_db db]}
puts $f {btree_set_cache_size $bt 10} puts $f {btree_set_cache_size $bt 10}
}
if {$prngseed} { if {$prngseed} {
set seed [expr {$prngseed%10007+1}] set seed [expr {$prngseed%10007+1}]

View File

@ -296,5 +296,61 @@ do_execsql_test transitive1-410 {
GROUP BY episodeview.c12; GROUP BY episodeview.c12;
} {1 /tmp/tvshows/The.Big.Bang.Theory/ {The Big Bang Theory} {Leonard Hofstadter and Sheldon Cooper are brilliant physicists, the kind of "beautiful minds" that understand how the universe works. But none of that genius helps them interact with people, especially women. All this begins to change when a free-spirited beauty named Penny moves in next door. Sheldon, Leonard's roommate, is quite content spending his nights playing Klingon Boggle with their socially dysfunctional friends, fellow CalTech scientists Howard Wolowitz and Raj Koothrappali. However, Leonard sees in Penny a whole new universe of possibilities... including love.} 2007-09-24 Comedy CBS TV-PG 3 1 0} } {1 /tmp/tvshows/The.Big.Bang.Theory/ {The Big Bang Theory} {Leonard Hofstadter and Sheldon Cooper are brilliant physicists, the kind of "beautiful minds" that understand how the universe works. But none of that genius helps them interact with people, especially women. All this begins to change when a free-spirited beauty named Penny moves in next door. Sheldon, Leonard's roommate, is quite content spending his nights playing Klingon Boggle with their socially dysfunctional friends, fellow CalTech scientists Howard Wolowitz and Raj Koothrappali. However, Leonard sees in Penny a whole new universe of possibilities... including love.} 2007-09-24 Comedy CBS TV-PG 3 1 0}
##############################################################################
# 2015-05-18. Make sure transitive constraints are avoided when column
# affinities and collating sequences get in the way.
#
db close
forcedelete test.db
sqlite3 db test.db
do_execsql_test transitive1-500 {
CREATE TABLE x(i INTEGER PRIMARY KEY, y TEXT);
INSERT INTO x VALUES(10, '10');
SELECT * FROM x WHERE x.y>='1' AND x.y<'2' AND x.i=x.y;
} {10 10}
do_execsql_test transitive1-510 {
CREATE TABLE t1(x TEXT);
CREATE TABLE t2(y TEXT);
INSERT INTO t1 VALUES('abc');
INSERT INTO t2 VALUES('ABC');
SELECT * FROM t1 CROSS JOIN t2 WHERE (x=y COLLATE nocase) AND y='ABC';
} {abc ABC}
do_execsql_test transitive1-520 {
CREATE TABLE t3(i INTEGER PRIMARY KEY, t TEXT);
INSERT INTO t3 VALUES(10, '10');
SELECT * FROM t3 WHERE i=t AND t = '10 ';
} {}
do_execsql_test transitive1-530 {
CREATE TABLE u1(x TEXT, y INTEGER, z TEXT);
CREATE INDEX i1 ON u1(x);
INSERT INTO u1 VALUES('00013', 13, '013');
SELECT * FROM u1 WHERE x=y AND y=z AND z='013';
} {00013 13 013}
do_execsql_test transitive1-540 {
CREATE TABLE b1(x, y);
INSERT INTO b1 VALUES('abc', 'ABC');
CREATE INDEX b1x ON b1(x);
SELECT * FROM b1 WHERE (x=y COLLATE nocase) AND y='ABC';
} {abc ABC}
do_execsql_test transitive1-550 {
CREATE TABLE c1(x, y COLLATE nocase, z);
INSERT INTO c1 VALUES('ABC', 'ABC', 'abc');
SELECT * FROM c1 WHERE x=y AND y=z AND z='abc';
} {ABC ABC abc}
do_execsql_test transitive1-560 {
CREATE INDEX c1x ON c1(x);
SELECT * FROM c1 WHERE x=y AND y=z AND z='abc';
} {ABC ABC abc}
do_execsql_test transitive1-560eqp {
EXPLAIN QUERY PLAN
SELECT * FROM c1 WHERE x=y AND y=z AND z='abc';
} {/SCAN TABLE c1/}
do_execsql_test transitive1-570 {
SELECT * FROM c1 WHERE x=y AND z=y AND z='abc';
} {}
do_execsql_test transitive1-570eqp {
EXPLAIN QUERY PLAN
SELECT * FROM c1 WHERE x=y AND z=y AND z='abc';
} {/SEARCH TABLE c1 USING INDEX c1x/}
finish_test finish_test

View File

@ -712,4 +712,20 @@ do_test trigger1-16.7 {
} }
} {1 {the INDEXED BY clause is not allowed on UPDATE or DELETE statements within triggers}} } {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 finish_test

View File

@ -19,6 +19,7 @@
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
set testprefix where4
ifcapable !tclvar||!bloblit { ifcapable !tclvar||!bloblit {
finish_test finish_test
@ -285,4 +286,22 @@ do_test where4-7.2 {
}; #ifcapable subquery }; #ifcapable subquery
#-------------------------------------------------------------------------
# Verify that "IS ?" with a NULL bound to the variable also functions
# correctly.
unset -nocomplain null
do_execsql_test 8.1 {
CREATE TABLE u9(a UNIQUE, b);
INSERT INTO u9 VALUES(NULL, 1);
INSERT INTO u9 VALUES(NULL, 2);
}
do_execsql_test 8.2 { SELECT * FROM u9 WHERE a IS NULL } {{} 1 {} 2}
do_execsql_test 8.2 { SELECT * FROM u9 WHERE a IS $null } {{} 1 {} 2}
finish_test finish_test

View File

@ -114,6 +114,8 @@ foreach hdr {
sqlite3session.h sqlite3session.h
sqlite3ext.h sqlite3ext.h
sqlite3.h sqlite3.h
sqlite3ext.h
sqlite3ota.h
sqliteicu.h sqliteicu.h
sqliteInt.h sqliteInt.h
sqliteLimit.h sqliteLimit.h
@ -370,6 +372,7 @@ foreach file {
rtree.c rtree.c
icu.c icu.c
fts3_icu.c fts3_icu.c
sqlite3ota.c
dbstat.c dbstat.c
sqlite3session.c sqlite3session.c
} { } {