mirror of https://github.com/sqlite/sqlite
Update the RBU vacuum code so that databases that use custom collation sequences can be vacuumed.
FossilOrigin-Name: 7dd48c10790a7b9c4165214399c261a0aa701297
This commit is contained in:
parent
b1ec87afdb
commit
ae20690e2c
|
@ -255,7 +255,41 @@ foreach step {0 1} {
|
|||
}
|
||||
} {}
|
||||
do_rbu_vacuum_test 1.10.2 $step
|
||||
|
||||
# Database with empty tables.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 1.11.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t4 VALUES(1, 2);
|
||||
}
|
||||
do_rbu_vacuum_test 1.11.2 $step
|
||||
do_execsql_test 1.11.3 {
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
SELECT * FROM t3;
|
||||
SELECT * FROM t4;
|
||||
} {1 2}
|
||||
reset_db
|
||||
do_execsql_test 1.12.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t3(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t4(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
}
|
||||
do_rbu_vacuum_test 1.12.2 $step
|
||||
do_execsql_test 1.12.3 {
|
||||
SELECT * FROM t1;
|
||||
SELECT * FROM t2;
|
||||
SELECT * FROM t3;
|
||||
SELECT * FROM t4;
|
||||
} {1 2}
|
||||
}
|
||||
set ::testprefix rbuvacuum
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test some error cases:
|
||||
|
@ -310,6 +344,67 @@ for {set i 1} 1 {incr i} {
|
|||
} {1 {SQLITE_BUSY - database modified during rbu vacuum}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that a database that uses custom collation sequences can be RBU
|
||||
# vacuumed.
|
||||
#
|
||||
reset_db
|
||||
forcedelete state.db
|
||||
proc noop {args} {}
|
||||
proc length_cmp {x y} {
|
||||
set n1 [string length $x]
|
||||
set n2 [string length $y]
|
||||
return [expr $n1 - $n2]
|
||||
}
|
||||
sqlite3_create_collation_v2 db length length_cmp noop
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
INSERT INTO t1 VALUES(1, 'i');
|
||||
INSERT INTO t1 VALUES(2, 'iiii');
|
||||
INSERT INTO t1 VALUES(3, 'ii');
|
||||
INSERT INTO t1 VALUES(4, 'iii');
|
||||
SELECT a FROM t1 ORDER BY b COLLATE length;
|
||||
} {1 3 4 2}
|
||||
do_execsql_test 3.1 {
|
||||
CREATE INDEX i1 ON t1(b COLLATE length);
|
||||
}
|
||||
|
||||
do_test 3.2 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such collation sequence: length}}
|
||||
|
||||
do_test 3.3 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
set db1 [rbu db 0]
|
||||
sqlite3_create_collation_v2 $db1 length length_cmp noop
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such collation sequence: length}}
|
||||
|
||||
do_test 3.4 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
set db1 [rbu db 1]
|
||||
sqlite3_create_collation_v2 $db1 length length_cmp noop
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {1 {SQLITE_ERROR - no such collation sequence: length}}
|
||||
|
||||
do_test 3.5 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
set db1 [rbu db 0]
|
||||
set db2 [rbu db 1]
|
||||
|
||||
sqlite3_create_collation_v2 $db1 length length_cmp noop
|
||||
sqlite3_create_collation_v2 $db2 length length_cmp noop
|
||||
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
list [catch { rbu close } msg] $msg
|
||||
} {0 SQLITE_DONE}
|
||||
|
||||
|
||||
catch { db close }
|
||||
finish_test
|
||||
|
||||
|
|
|
@ -3081,6 +3081,92 @@ static void rbuSaveState(sqlite3rbu *p, int eStage){
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** The second argument passed to this function is the name of a PRAGMA
|
||||
** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
|
||||
** This function executes the following on sqlite3rbu.dbRbu:
|
||||
**
|
||||
** "PRAGMA main.$zPragma"
|
||||
**
|
||||
** where $zPragma is the string passed as the second argument, then
|
||||
** on sqlite3rbu.dbMain:
|
||||
**
|
||||
** "PRAGMA main.$zPragma = $val"
|
||||
**
|
||||
** where $val is the value returned by the first PRAGMA invocation.
|
||||
**
|
||||
** In short, it copies the value of the specified PRAGMA setting from
|
||||
** dbRbu to dbMain.
|
||||
*/
|
||||
static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pPragma = 0;
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
|
||||
sqlite3_mprintf("PRAGMA main.%s", zPragma)
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
|
||||
p->rc = rbuMPrintfExec(p, p->dbMain, "PRAGMA main.%s = %d",
|
||||
zPragma, sqlite3_column_int(pPragma, 0)
|
||||
);
|
||||
}
|
||||
rbuFinalize(p, pPragma);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The RBU handle passed as the only argument has just been opened and
|
||||
** the state database is empty. If this RBU handle was opened for an
|
||||
** RBU vacuum operation, create the schema in the target db.
|
||||
*/
|
||||
static void rbuCreateTargetSchema(sqlite3rbu *p){
|
||||
sqlite3_stmt *pSql = 0;
|
||||
sqlite3_stmt *pInsert = 0;
|
||||
|
||||
assert( rbuIsVacuum(p) );
|
||||
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
|
||||
" AND name!='sqlite_sequence' "
|
||||
" ORDER BY type DESC"
|
||||
);
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
||||
p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
|
||||
}
|
||||
rbuFinalize(p, pSql);
|
||||
if( p->rc!=SQLITE_OK ) return;
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
|
||||
);
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
|
||||
"INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
|
||||
);
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
||||
int i;
|
||||
for(i=0; i<5; i++){
|
||||
sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
|
||||
}
|
||||
sqlite3_step(pInsert);
|
||||
p->rc = sqlite3_reset(pInsert);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
|
||||
}
|
||||
|
||||
rbuFinalize(p, pSql);
|
||||
rbuFinalize(p, pInsert);
|
||||
}
|
||||
|
||||
/*
|
||||
** Step the RBU object.
|
||||
*/
|
||||
|
@ -3089,6 +3175,15 @@ int sqlite3rbu_step(sqlite3rbu *p){
|
|||
switch( p->eStage ){
|
||||
case RBU_STAGE_OAL: {
|
||||
RbuObjIter *pIter = &p->objiter;
|
||||
|
||||
/* If this is an RBU vacuum operation and the state table was empty
|
||||
** when this handle was opened, create the target database schema. */
|
||||
if( rbuIsVacuum(p) && p->nProgress==0 && p->rc==SQLITE_OK ){
|
||||
rbuCreateTargetSchema(p);
|
||||
rbuCopyPragma(p, "user_version");
|
||||
rbuCopyPragma(p, "application_id");
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && pIter->zTbl ){
|
||||
|
||||
if( pIter->bCleanup ){
|
||||
|
@ -3371,92 +3466,6 @@ static void rbuInitPhaseOneSteps(sqlite3rbu *p){
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The second argument passed to this function is the name of a PRAGMA
|
||||
** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
|
||||
** This function executes the following on sqlite3rbu.dbRbu:
|
||||
**
|
||||
** "PRAGMA main.$zPragma"
|
||||
**
|
||||
** where $zPragma is the string passed as the second argument, then
|
||||
** on sqlite3rbu.dbMain:
|
||||
**
|
||||
** "PRAGMA main.$zPragma = $val"
|
||||
**
|
||||
** where $val is the value returned by the first PRAGMA invocation.
|
||||
**
|
||||
** In short, it copies the value of the specified PRAGMA setting from
|
||||
** dbRbu to dbMain.
|
||||
*/
|
||||
static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
|
||||
if( p->rc==SQLITE_OK ){
|
||||
sqlite3_stmt *pPragma = 0;
|
||||
p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
|
||||
sqlite3_mprintf("PRAGMA main.%s", zPragma)
|
||||
);
|
||||
if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
|
||||
p->rc = rbuMPrintfExec(p, p->dbMain, "PRAGMA main.%s = %d",
|
||||
zPragma, sqlite3_column_int(pPragma, 0)
|
||||
);
|
||||
}
|
||||
rbuFinalize(p, pPragma);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The RBU handle passed as the only argument has just been opened and
|
||||
** the state database is empty. If this RBU handle was opened for an
|
||||
** RBU vacuum operation, create the schema in the target db.
|
||||
*/
|
||||
static void rbuCreateTargetSchema(sqlite3rbu *p){
|
||||
sqlite3_stmt *pSql = 0;
|
||||
sqlite3_stmt *pInsert = 0;
|
||||
|
||||
assert( rbuIsVacuum(p) );
|
||||
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
|
||||
" AND name!='sqlite_sequence' "
|
||||
" ORDER BY type DESC"
|
||||
);
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
||||
const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
|
||||
p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
|
||||
}
|
||||
rbuFinalize(p, pSql);
|
||||
if( p->rc!=SQLITE_OK ) return;
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
|
||||
"SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
|
||||
);
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
|
||||
"INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
|
||||
);
|
||||
}
|
||||
|
||||
while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
|
||||
int i;
|
||||
for(i=0; i<5; i++){
|
||||
sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
|
||||
}
|
||||
sqlite3_step(pInsert);
|
||||
p->rc = sqlite3_reset(pInsert);
|
||||
}
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
|
||||
}
|
||||
|
||||
rbuFinalize(p, pSql);
|
||||
rbuFinalize(p, pInsert);
|
||||
}
|
||||
|
||||
|
||||
static sqlite3rbu *openRbuHandle(
|
||||
const char *zTarget,
|
||||
|
@ -3569,14 +3578,6 @@ static sqlite3rbu *openRbuHandle(
|
|||
}
|
||||
}
|
||||
|
||||
/* If this is an RBU vacuum operation and the state table was empty
|
||||
** when this handle was opened, create the target database schema. */
|
||||
if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
|
||||
rbuCreateTargetSchema(p);
|
||||
rbuCopyPragma(p, "user_version");
|
||||
rbuCopyPragma(p, "application_id");
|
||||
}
|
||||
|
||||
/* Point the object iterator at the first object */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
p->rc = rbuObjIterFirst(p, &p->objiter);
|
||||
|
|
|
@ -20,8 +20,9 @@
|
|||
#include <tcl.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* From main.c (apparently...) */
|
||||
/* From main.c */
|
||||
extern const char *sqlite3ErrName(int);
|
||||
extern int sqlite3TestMakePointerStr(Tcl_Interp*, char*, void*);
|
||||
|
||||
void test_rbu_delta(sqlite3_context *pCtx, int nArg, sqlite3_value **apVal){
|
||||
Tcl_Interp *interp = (Tcl_Interp*)sqlite3_user_data(pCtx);
|
||||
|
@ -66,7 +67,8 @@ static int test_sqlite3rbu_cmd(
|
|||
{"create_rbu_delta", 2, ""}, /* 2 */
|
||||
{"savestate", 2, ""}, /* 3 */
|
||||
{"dbMain_eval", 3, "SQL"}, /* 4 */
|
||||
{"bp_progress", 2, ""}, /* 5 */
|
||||
{"bp_progress", 2, ""}, /* 5 */
|
||||
{"db", 3, "RBU"}, /* 6 */
|
||||
{0,0,0}
|
||||
};
|
||||
int iCmd;
|
||||
|
@ -149,6 +151,22 @@ static int test_sqlite3rbu_cmd(
|
|||
break;
|
||||
}
|
||||
|
||||
case 6: /* db */ {
|
||||
int bArg;
|
||||
if( Tcl_GetBooleanFromObj(interp, objv[2], &bArg) ){
|
||||
ret = TCL_ERROR;
|
||||
}else{
|
||||
char zBuf[50];
|
||||
sqlite3 *db = sqlite3rbu_db(pRbu, bArg);
|
||||
if( sqlite3TestMakePointerStr(interp, zBuf, (void*)db) ){
|
||||
ret = TCL_ERROR;
|
||||
}else{
|
||||
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: /* seems unlikely */
|
||||
assert( !"cannot happen" );
|
||||
break;
|
||||
|
|
18
manifest
18
manifest
|
@ -1,5 +1,5 @@
|
|||
C When\schecking\sfor\sthe\sWHERE-clause\spush-down\soptimization,\sverify\sthat\nall\sterms\sof\sthe\scompound\sinner\sSELECT\sare\snon-aggregate,\snot\sjust\sthe\nlast\sterm.\s\sFix\sfor\sticket\s[f7f8c97e97597].
|
||||
D 2016-04-25T02:20:10.236
|
||||
C Update\sthe\sRBU\svacuum\scode\sso\sthat\sdatabases\sthat\suse\scustom\scollation\ssequences\scan\sbe\svacuumed.
|
||||
D 2016-04-25T19:25:12.645
|
||||
F Makefile.in a905f3180accdafbd5a534bf26126ee5306d5056
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 71b8b16cf9393f68e2e2035486ca104872558836
|
||||
|
@ -247,10 +247,10 @@ F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
|
|||
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
|
||||
F ext/rbu/rbuprogress.test 2023a7df2c523e3df1cb532eff811cda385a789a
|
||||
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
|
||||
F ext/rbu/rbuvacuum.test bcbc1dcd8e2a46a811e46477692ae1c0e8917a85
|
||||
F ext/rbu/sqlite3rbu.c 20922328dcebe89589638923bb46840df8bc7733
|
||||
F ext/rbu/rbuvacuum.test 66e02cf299836770e718e95c36686be0b26dbda3
|
||||
F ext/rbu/sqlite3rbu.c bf36625990c6865ecf08bd844d8097ed2d0a6958
|
||||
F ext/rbu/sqlite3rbu.h 2acd0a6344a6079de15c8dc9d84d3df83a665930
|
||||
F ext/rbu/test_rbu.c 430b8b9520c233505371d564d3561e0b554355f4
|
||||
F ext/rbu/test_rbu.c 9bbdf6bd8efd58fbc4f192698df50569598fbb9e
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 0b870ccb7b58b734a2a8e1e2755a7c0ded070920
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
|
@ -1484,7 +1484,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P d0a579b35105810821bbfec6b50ecfebac7a17ee
|
||||
R 858c551be0612332d0982a3a42946fb6
|
||||
U drh
|
||||
Z c1158477a3c6966fdb5709eb9b3c8fe5
|
||||
P ec215f94ac9748c0acd82af0cc9e7a92249462f9
|
||||
R c0a43d6aa5e9f6d906be746461438b7f
|
||||
U dan
|
||||
Z 33a592aa08c77e06ce6e19e096f78faa
|
||||
|
|
|
@ -1 +1 @@
|
|||
ec215f94ac9748c0acd82af0cc9e7a92249462f9
|
||||
7dd48c10790a7b9c4165214399c261a0aa701297
|
Loading…
Reference in New Issue