Update ota so that the hidden columns of virtual tables may be written.
FossilOrigin-Name: ccee999649d0fa1d48e53847542f4cbe05e3d694
This commit is contained in:
parent
7bf9ec1c26
commit
ca3d648f9c
122
ext/ota/ota10.test
Normal file
122
ext/ota/ota10.test
Normal 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
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
|
@ -96,7 +96,7 @@ do_test 2.1 {
|
||||
}
|
||||
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;
|
||||
|
@ -223,7 +223,6 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
|
||||
sqlite3_free(pIter->azTblCol[i]);
|
||||
}
|
||||
sqlite3_free(pIter->azTblCol);
|
||||
sqlite3_free(pIter->abTblPk);
|
||||
pIter->azTblCol = 0;
|
||||
pIter->abTblPk = 0;
|
||||
pIter->nTblCol = 0;
|
||||
@ -369,6 +368,24 @@ static char *otaQuoteName(const char *zName){
|
||||
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
|
||||
@ -396,21 +413,22 @@ static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
|
||||
}
|
||||
|
||||
/*
|
||||
** 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){
|
||||
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);
|
||||
static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
|
||||
int nByte = sizeof(char*) * nCol + sizeof(unsigned char*) * nCol;
|
||||
char **azNew;
|
||||
|
||||
if( azNew ) pIter->azTblCol = azNew;
|
||||
if( abNew ) pIter->abTblPk = abNew;
|
||||
if( azNew==0 || abNew==0 ) p->rc = SQLITE_NOMEM;
|
||||
assert( p->rc==SQLITE_OK );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,30 +469,60 @@ static int otaIsVtab(sqlite3ota *p, const char *zTab){
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
int iPk = sqlite3_column_int(pStmt, 5);
|
||||
pIter->abTblPk[nCol] = (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++;
|
||||
/* 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) ){
|
||||
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[i] = (iPk!=0);
|
||||
if( iPk ) bSeenPk = 1;
|
||||
if( iPk<0 ) pIter->bRowid = 1;
|
||||
}
|
||||
}
|
||||
pIter->nTblCol = nCol;
|
||||
rc2 = sqlite3_finalize(pStmt);
|
||||
if( p->rc==SQLITE_OK ) p->rc = rc2;
|
||||
|
||||
|
@ -106,6 +106,19 @@
|
||||
**
|
||||
** 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
|
||||
|
17
manifest
17
manifest
@ -1,5 +1,5 @@
|
||||
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
|
||||
@ -126,8 +126,9 @@ F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
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
|
||||
@ -135,8 +136,8 @@ F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
|
||||
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
|
||||
@ -1236,7 +1237,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
P 7ef44c5b5bd30bcc4ef59ed172b9ce9ac6a843f6
|
||||
R a969a1e9314cd2ef179f3839efd0b8ec
|
||||
P 600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
|
||||
R 19cf180a791a327f1f4fb68723a586ef
|
||||
U dan
|
||||
Z bba2f4cd69d571c58d439710133b8bb2
|
||||
Z 7646a22500cce7c7ef211b057e34ff27
|
||||
|
@ -1 +1 @@
|
||||
600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
|
||||
ccee999649d0fa1d48e53847542f4cbe05e3d694
|
Loading…
x
Reference in New Issue
Block a user