--- /dev/null
+# 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
+
return p->rc;
}
+/*
+** This is a wrapper around "sqlite3_mprintf(zFmt, ...)". If an OOM occurs,
+** an error code is stored in the OTA handle passed as the first argument.
+*/
+static char *otaMPrintfAndCollectError(sqlite3ota *p, const char *zFmt, ...){
+ char *zSql = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ if( p->rc==SQLITE_OK ){
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( zSql==0 ) p->rc = SQLITE_NOMEM;
+ }
+ va_end(ap);
+ return zSql;
+}
+
/*
** This function constructs and returns a pointer to a nul-terminated
** string containing some SQL clause or list based on one or more of the
p->zErrmsg = sqlite3_mprintf("Invalid ota_control value");
}
+
static char *otaObjIterGetSetlist(
sqlite3ota *p,
OtaObjIter *pIter,
}else{
const char *zSep = "";
for(i=0; i<pIter->nTblCol; i++){
- if( zMask[i]=='x' ){
- zList = sqlite3_mprintf("%z%s%s=?%d",
+ char c = zMask[i];
+ if( c=='x' ){
+ zList = otaMPrintfAndCollectError(p, "%z%s%s=?%d",
zList, zSep, pIter->azTblCol[i], i+1
);
- if( zList==0 ){
- p->rc = SQLITE_NOMEM;
- break;
- }
+ zSep = ", ";
+ }
+ if( c=='d' ){
+ zList = otaMPrintfAndCollectError(p, "%z%s%s=ota_delta(%s, ?%d)",
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
+ );
zSep = ", ";
}
}
return p;
}
+/*
+** Return the database handle used by pOta.
+*/
+sqlite3 *sqlite3ota_db(sqlite3ota *pOta){
+ return (pOta ? pOta->db : 0);
+}
+
/*
** Close the OTA handle.
*/
/* 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 ret = TCL_OK;
sqlite3ota *pOta = (sqlite3ota*)clientData;
- const char *azMethod[] = { "step", "close", 0 };
+ const char *azMethod[] = { "step", "close", "create_ota_delta", 0 };
int iMethod;
if( objc!=2 ){
break;
}
+ case 2: /* create_ota_delta */ {
+ sqlite3 *db = sqlite3ota_db(pOta);
+ 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;
**
** 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;
**
** USAGE
**
*/
sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta);
+/*
+** Obtain the underlying database handle used by the OTA extension.
+**
+** The only argument passed to this function must be a valid, open, OTA
+** handle. This function returns the database handle used by OTA for all
+** operations on the target and source databases. This may be useful in
+** two scenarios:
+**
+** * If the data_xxx tables in the OTA source database are virtual
+** tables, or if any of the tables being updated are virtual tables,
+** the application may need to call sqlite3_create_module() on
+** the db handle to register the 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 OTA.
+*/
+sqlite3 *sqlite3ota_db(sqlite3ota*);
+
/*
** Do some work towards applying the OTA update to the target db.
**
-C Update\sthe\sota\sextension\sso\sthat\sit\scan\sbe\sused\sto\supdate\stables\swith\sexternal\sPRIMARY\sKEY\sindexes.
-D 2014-11-20T17:37:08.997
+C Add\sthe\s"ota_delta()"\sfeature\sfor\sdelta-compressed\supdates.
+D 2014-11-20T19:19:02.502
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
+F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
-F ext/ota/sqlite3ota.c 2c31a56890e915e13eb5d6ced02325e1f4db7487
-F ext/ota/sqlite3ota.h 7b20abe9247d292429d00f0a5c237ff6e0dc0196
+F ext/ota/sqlite3ota.c edeea10871d1307ff9ee9ccc765ba4031b507509
+F ext/ota/sqlite3ota.h 08b276fc9f56c04cdb454cf7aefa41c29361ed7a
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 556c3de53ad33d11d33ec794345c2100aa76f3e1
-R c3a23811c2315fa65ee1bcdda07f2567
+P 55066a1171cbd3077f5e6c8ceb2745e810d9476e
+R 97efd71a02c88dd0da29f19b3d083930
U dan
-Z eebecee2ceb200b37a4bd343197c1b04
+Z 1557aa5ff604851a6b4517de0e4ff6aa
-55066a1171cbd3077f5e6c8ceb2745e810d9476e
\ No newline at end of file
+c64dcd1788f5cc7db197a0ec4ab0981f34a72c6b
\ No newline at end of file