--- /dev/null
+# 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}}
+}
+
+
+
+finish_test
}
db2 close
list [catch { run_ota test.db ota.db } msg] $msg
-} {1 {SQLITE_ERROR - no such column: c}}
+} {1 {SQLITE_ERROR - column missing from data_x1: c}}
do_execsql_test 2.2 {
PRAGMA integrity_check;
sqlite3_free(pIter->azTblCol[i]);
}
sqlite3_free(pIter->azTblCol);
- sqlite3_free(pIter->abTblPk);
pIter->azTblCol = 0;
pIter->abTblPk = 0;
pIter->nTblCol = 0;
return zRet;
}
+/*
+** Argument zName points to a column name. Argument zQuoted also points
+** to a column name, but one that has been quoted using otaQuoteName().
+** Return true if the column names are the same, or false otherwise.
+*/
+static int otaMatchName(const char *zName, const char *zQuoted){
+ const char *p = zName;
+ const char *q = &zQuoted[1];
+ while( 1 ){
+ if( *q=='`' ) q++;
+ if( sqlite3_strnicmp(q, p, 1) ) return 0;
+ if( !*q ) break;
+ p++;
+ q++;
+ }
+ return 1;
+}
+
/*
** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
** arguments are the usual subsitution values. This function performs
}
/*
-** Increase the size of the pIter->azTblCol[] and abTblPk[] arrays so that
+** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
** there is room for at least nCol elements. If an OOM occurs, store an
** error code in the OTA handle passed as the first argument.
*/
-static void otaExtendIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
+static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
+ int nByte = sizeof(char*) * nCol + sizeof(unsigned char*) * nCol;
+ char **azNew;
+
assert( p->rc==SQLITE_OK );
- if( (nCol % 8)==0 ){
- unsigned char *abNew;
- int nByte = sizeof(char*) * (nCol+8);
- char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte);
- abNew = (unsigned char*)sqlite3_realloc(pIter->abTblPk, nCol+8);
-
- if( azNew ) pIter->azTblCol = azNew;
- if( abNew ) pIter->abTblPk = abNew;
- if( azNew==0 || abNew==0 ) p->rc = SQLITE_NOMEM;
+ azNew = (char**)sqlite3_malloc(nByte);
+ if( azNew ){
+ memset(azNew, 0, nByte);
+ pIter->azTblCol = azNew;
+ pIter->abTblPk = (unsigned char*)&pIter->azTblCol[nCol];
+ }else{
+ p->rc = SQLITE_NOMEM;
}
}
*/
static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
if( pIter->azTblCol==0 ){
- sqlite3_stmt *pStmt;
- char *zSql;
+ sqlite3_stmt *pStmt = 0;
int nCol = 0;
int bSeenPk = 0;
+ int i; /* for() loop iterator variable */
int rc2; /* sqlite3_finalize() return value */
assert( pIter->bRowid==0 && pIter->bVtab==0 );
- zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl);
- p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg, zSql);
+ /* Populate the azTblCol[] and nTblCol variables based on the columns
+ ** of the input table. Ignore any input table columns that begin with
+ ** "ota_". */
+ p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("SELECT * FROM 'data_%q'", pIter->zTbl)
+ );
+ if( p->rc==SQLITE_OK ){
+ nCol = sqlite3_column_count(pStmt);
+ otaAllocateIterArrays(p, pIter, nCol);
+ }
+ for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
+ const char *zName = (const char*)sqlite3_column_name(pStmt, i);
+ if( sqlite3_strnicmp("ota_", zName, 4) ){
+ char *zCopy = otaQuoteName(zName);
+ pIter->azTblCol[pIter->nTblCol++] = zCopy;
+ if( zCopy==0 ) p->rc = SQLITE_NOMEM;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+
+ /* Check that all non-HIDDEN columns in the destination table are also
+ ** present in the input table. Populate the abTblPk[] array at the
+ ** same time. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl)
+ );
+ }
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- otaExtendIterArrays(p, pIter, nCol);
- if( p->rc==SQLITE_OK ){
- const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+ const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+ for(i=0; i<pIter->nTblCol; i++){
+ if( otaMatchName(zName, pIter->azTblCol[i]) ) break;
+ }
+ if( i==pIter->nTblCol ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("column missing from data_%q: %s",
+ pIter->zTbl, zName
+ );
+ }else{
int iPk = sqlite3_column_int(pStmt, 5);
- pIter->abTblPk[nCol] = (iPk!=0);
+ pIter->abTblPk[i] = (iPk!=0);
if( iPk ) bSeenPk = 1;
if( iPk<0 ) pIter->bRowid = 1;
- pIter->azTblCol[nCol] = otaQuoteName(zName);
- if( pIter->azTblCol[nCol]==0 ) p->rc = SQLITE_NOMEM;
- nCol++;
}
}
- pIter->nTblCol = nCol;
rc2 = sqlite3_finalize(pStmt);
if( p->rc==SQLITE_OK ) p->rc = rc2;
**
** CREATE TABLE data_ft1(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
-C Add\sSQLITE_ENABLE_OTA\spre-processor\sdirectives\sso\sthat\sthis\sbranch\smay\sbe\scompiled\swith\sor\swithout\sOTA.
-D 2014-11-22T09:09:50.320
+C Update\sota\sso\sthat\sthe\shidden\scolumns\sof\svirtual\stables\smay\sbe\swritten.
+D 2014-11-27T18:09:46.630
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/ota/README.txt 78d4a9f78f567d4bf826cf0f02df6254902562ca
F ext/ota/ota.c c11a85af71dccc45976622fe7a51169a481caa91
F ext/ota/ota1.test a8f9d89c9b2d381a663bcedaa5dd5952cdbd1231
+F ext/ota/ota10.test ab815dff9cef7248c504f06b888627d236f25e9c
F ext/ota/ota2.test 4568c2671d19dbde789fb9091d727a2e94880128
-F ext/ota/ota3.test 215dd4a8e238567e0f890a5139b6fdf5494ef311
+F ext/ota/ota3.test 71bd8cc0cf8d7e7d9bb11a1fcc238320a5a9d8c8
F ext/ota/ota4.test 60f897f329a6782ef2f24862640acf3c52e48077
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
F ext/ota/ota9.test d9ad30ccb4e08f878e382876fe67752309538af9
F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
-F ext/ota/sqlite3ota.c accfada2ab182dc52225e7345f656520a4e8db22
-F ext/ota/sqlite3ota.h 04577b00c456aacb99be9c8b55572a6e3ca9aa27
+F ext/ota/sqlite3ota.c f1e930690ec2bcb72138301ebf05ea2ffd4450de
+F ext/ota/sqlite3ota.h 8dc9c812e61f47f497336a45f820bee88b33a2c5
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 7ef44c5b5bd30bcc4ef59ed172b9ce9ac6a843f6
-R a969a1e9314cd2ef179f3839efd0b8ec
+P 600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
+R 19cf180a791a327f1f4fb68723a586ef
U dan
-Z bba2f4cd69d571c58d439710133b8bb2
+Z 7646a22500cce7c7ef211b057e34ff27
-600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
\ No newline at end of file
+ccee999649d0fa1d48e53847542f4cbe05e3d694
\ No newline at end of file