From: dan Date: Thu, 30 Jul 2015 11:38:19 +0000 (+0000) Subject: Allow RBU tables to be named "data[0-9]*_" instead of strictly "data_" instead of strictly "data_". Also update RBU so that it always processes data tables in order sorted by name. FossilOrigin-Name: 287aa30601506f168d355c35176a3383474444ca --- diff --git a/ext/rbu/rbufts.test b/ext/rbu/rbufts.test new file mode 100644 index 0000000000..e2526a02a6 --- /dev/null +++ b/ext/rbu/rbufts.test @@ -0,0 +1,80 @@ +# 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 RBU module. More specifically, it +# contains tests to ensure that RBU works with FTS tables. +# + +if {![info exists testdir]} { + set testdir [file join [file dirname [info script]] .. .. test] +} +source $testdir/tester.tcl +set ::testprefix rbufts + +ifcapable !fts3 { + finish_test + return +} + +proc step_rbu {target rbu} { + while 1 { + sqlite3rbu rbu $target $rbu + set rc [rbu step] + rbu close + if {$rc != "SQLITE_OK"} break + } + set rc +} + +do_execsql_test 1.0 { + CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b); + CREATE VIRTUAL TABLE xx USING fts4(content=t1, a, b); + INSERT INTO t1(rowid, a, b) VALUES(10, 'a b c', 'c b a'); + INSERT INTO t1(rowid, a, b) VALUES(20, 'a b c', 'd e f'); + INSERT INTO t1(rowid, a, b) VALUES(30, 'd e f', 'a b c'); + INSERT INTO t1(rowid, a, b) VALUES(40, 'd e f', 'd e f'); +} + +do_execsql_test 1.1 { + INSERT INTO xx(xx) VALUES('rebuild'); + INSERT INTO xx(xx) VALUES('integrity-check'); +} + +forcedelete rbu.db +do_test 2.0 { + sqlite3 dbrbu rbu.db + dbrbu eval { + CREATE TABLE data_t1(i, a, b, rbu_control); + INSERT INTO data_t1 VALUES(20, NULL, NULL, 1); -- delete + INSERT INTO data_t1 VALUES(30, 'x y z', NULL, '.x.'); -- update + INSERT INTO data_t1 VALUES(50, '1 2 3', 'x y z', 0); -- insert + + CREATE VIEW data0_xx AS + SELECT i AS rbu_rowid, a, b, + CASE WHEN rbu_control IN (0, 1) + THEN rbu_control ELSE substr(rbu_control, 2) END AS rbu_control + FROM data_t1; + + } + dbrbu close + + step_rbu test.db rbu.db +} {SQLITE_DONE} + +do_execsql_test 2.1 { + INSERT INTO xx(xx) VALUES('integrity-check'); +} + + + + +finish_test + diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c index 491313ad9a..1598ed315e 100644 --- a/ext/rbu/sqlite3rbu.c +++ b/ext/rbu/sqlite3rbu.c @@ -239,6 +239,7 @@ struct RbuObjIter { /* Output variables. zTbl==0 implies EOF. */ int bCleanup; /* True in "cleanup" state */ const char *zTbl; /* Name of target db table */ + const char *zDataTbl; /* Name of rbu db table (or null) */ const char *zIdx; /* Name of target db index (or null) */ int iTnum; /* Root page of current object */ int iPkTnum; /* If eType==EXTERNAL, root of PK index */ @@ -249,7 +250,7 @@ struct RbuObjIter { sqlite3_stmt *pSelect; /* Source data */ sqlite3_stmt *pInsert; /* Statement for INSERT operations */ sqlite3_stmt *pDelete; /* Statement for DELETE ops */ - sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zTbl */ + sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */ /* Last UPDATE used (for PK b-tree updates only), or NULL. */ RbuUpdateStmt *pRbuUpdate; @@ -526,7 +527,8 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){ pIter->zTbl = 0; }else{ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0); - rc = pIter->zTbl ? SQLITE_OK : SQLITE_NOMEM; + pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1); + rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM; } }else{ if( pIter->zIdx==0 ){ @@ -557,6 +559,40 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){ return rc; } + +/* +** The implementation of the rbu_target_name() SQL function. This function +** accepts one argument - the name of a table in the RBU database. If the +** table name matches the pattern: +** +** data[0-9]_ +** +** where is any sequence of 1 or more characters, is returned. +** Otherwise, if the only argument does not match the above pattern, an SQL +** NULL is returned. +** +** "data_t1" -> "t1" +** "data0123_t2" -> "t2" +** "dataAB_t3" -> NULL +*/ +static void rbuTargetNameFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zIn; + assert( argc==1 ); + + zIn = (const char*)sqlite3_value_text(argv[0]); + if( zIn && strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){ + int i; + for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++); + if( zIn[i]=='_' && zIn[i+1] ){ + sqlite3_result_text(context, &zIn[i+1], -1, SQLITE_STATIC); + } + } +} + /* ** Initialize the iterator structure passed as the second argument. ** @@ -570,8 +606,9 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){ memset(pIter, 0, sizeof(RbuObjIter)); rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, - "SELECT substr(name, 6) FROM sqlite_master " - "WHERE type IN ('table', 'view') AND name LIKE 'data_%'" + "SELECT rbu_target_name(name) AS target, name FROM sqlite_master " + "WHERE type IN ('table', 'view') AND target IS NOT NULL " + "ORDER BY name" ); if( rc==SQLITE_OK ){ @@ -917,7 +954,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){ ** of the input table. Ignore any input table columns that begin with ** "rbu_". */ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, - sqlite3_mprintf("SELECT * FROM 'data_%q'", pIter->zTbl) + sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl) ); if( p->rc==SQLITE_OK ){ nCol = sqlite3_column_count(pStmt); @@ -942,7 +979,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){ ){ p->rc = SQLITE_ERROR; p->zErrmsg = sqlite3_mprintf( - "table data_%q %s rbu_rowid column", pIter->zTbl, + "table %q %s rbu_rowid column", pIter->zDataTbl, (bRbuRowid ? "may not have" : "requires") ); } @@ -963,8 +1000,8 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){ } if( i==pIter->nTblCol ){ p->rc = SQLITE_ERROR; - p->zErrmsg = sqlite3_mprintf("column missing from data_%q: %s", - pIter->zTbl, zName + p->zErrmsg = sqlite3_mprintf("column missing from %q: %s", + pIter->zDataTbl, zName ); }else{ int iPk = sqlite3_column_int(pStmt, 5); @@ -1519,7 +1556,7 @@ static void rbuObjIterPrepareTmpInsert( p->rc = prepareFreeAndCollectError( p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf( "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)", - p->zStateDb, pIter->zTbl, zCollist, zRbuRowid, zBind + p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind )); } } @@ -1615,18 +1652,18 @@ static int rbuObjIterPrepareAll( if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", - zCollist, p->zStateDb, pIter->zTbl, + zCollist, p->zStateDb, pIter->zDataTbl, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM 'data_%q' " + "SELECT %s, rbu_control FROM '%q' " "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " "UNION ALL " "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " "ORDER BY %s%s", - zCollist, pIter->zTbl, - zCollist, p->zStateDb, pIter->zTbl, + zCollist, pIter->zDataTbl, + zCollist, p->zStateDb, pIter->zDataTbl, zCollist, zLimit ); } @@ -1654,8 +1691,9 @@ static int rbuObjIterPrepareAll( if( p->rc==SQLITE_OK ){ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, sqlite3_mprintf( - "SELECT %s, rbu_control%s FROM 'data_%q'%s", - zCollist, (bRbuRowid ? ", rbu_rowid" : ""), zTbl, zLimit + "SELECT %s, rbu_control%s FROM '%q'%s", + zCollist, (bRbuRowid ? ", rbu_rowid" : ""), + pIter->zDataTbl, zLimit ) ); } @@ -1693,10 +1731,10 @@ static int rbuObjIterPrepareAll( /* Create the rbu_tmp_xxx table and the triggers to populate it. */ rbuMPrintfExec(p, p->dbRbu, "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS " - "SELECT *%s FROM 'data_%q' WHERE 0;" - , p->zStateDb - , zTbl, (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "") - , zTbl + "SELECT *%s FROM '%q' WHERE 0;" + , p->zStateDb, pIter->zDataTbl + , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "") + , pIter->zDataTbl ); rbuMPrintfExec(p, p->dbMain, @@ -1862,6 +1900,12 @@ static void rbuOpenDatabase(sqlite3rbu *p){ ); } + if( p->rc==SQLITE_OK ){ + p->rc = sqlite3_create_function(p->dbRbu, + "rbu_target_name", 1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0 + ); + } + if( p->rc==SQLITE_OK ){ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p); } @@ -2403,7 +2447,7 @@ int sqlite3rbu_step(sqlite3rbu *p){ ** But the contents can be deleted. */ if( pIter->abIndexed ){ rbuMPrintfExec(p, p->dbRbu, - "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zTbl + "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl ); } }else{ diff --git a/ext/rbu/sqlite3rbu.h b/ext/rbu/sqlite3rbu.h index c3e32f9410..d80e182424 100644 --- a/ext/rbu/sqlite3rbu.h +++ b/ext/rbu/sqlite3rbu.h @@ -97,6 +97,18 @@ ** ** The order of the columns in the data_% table does not matter. ** +** Instead of a regular table, the RBU database may also contain virtual +** tables or view named using the data_ naming scheme. +** +** Instead of the plain data_ naming scheme, RBU database tables +** may also be named data_, where is any sequence +** of zero or more numeric characters (0-9). This can be significant because +** tables within the RBU database are always processed in order sorted by +** name. By judicious selection of the the portion of the names +** of the RBU tables the user can therefore control the order in which they +** are processed. This can be useful, for example, to ensure that "external +** content" FTS4 tables are updated before their underlying content tables. +** ** 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 "rbu_rowid". This column is mapped to the tables implicit primary diff --git a/manifest b/manifest index de30096e24..922ed843dc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Return\san\serror\smessage\s(instead\sof\ssegfaulting)\sif\stest\sfunction\sfts5_expr()\sis\sinvoked\swith\sno\sarguments. -D 2015-07-30T11:26:10.463 +C Allow\sRBU\stables\sto\sbe\snamed\s"data[0-9]*_"\sinstead\sof\sstrictly\s"data_".\sAlso\supdate\sRBU\sso\sthat\sit\salways\sprocesses\sdata\stables\sin\sorder\ssorted\sby\sname. +D 2015-07-30T11:38:19.207 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 4de3ef40c8b3b75c0c55ff4242a43c8ce1ad90ee F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -217,8 +217,9 @@ F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3 F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695 F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89 F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06 -F ext/rbu/sqlite3rbu.c dbd7e4b31821398dcdeb21492970401ff1027881 -F ext/rbu/sqlite3rbu.h 6a280298e9eeb8ef59841a620f07f4f844651545 +F ext/rbu/rbufts.test 17db7d968b3d073788bcba044c498d09e830726b +F ext/rbu/sqlite3rbu.c 75409b33f20ef6acd3689206a707de79e17a8628 +F ext/rbu/sqlite3rbu.h 67977687c3acf995295f5f461809cac030098afd F ext/rbu/test_rbu.c f99698956cc9158d6bf865e461e2d15876538ac1 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c @@ -1366,7 +1367,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 96559adbf18bbbf25ff4e1579ee3ff2afef4c4d7 -R 47e11f7f15f9372decc5bbb9c166f3bf +P 8e818b8985c0196cd9671a6491796faaeebeb16e +R d2e717e7df793b66cba7be5b1c5545b9 U dan -Z e4fcfe99ec97c4ebc5a25850df719a0d +Z b15734d6bf397de4fcabb387cb6f82da diff --git a/manifest.uuid b/manifest.uuid index 881a91aac6..edf9475164 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8e818b8985c0196cd9671a6491796faaeebeb16e \ No newline at end of file +287aa30601506f168d355c35176a3383474444ca \ No newline at end of file