/ Check-in [f042fdd1]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add fault injection tests for rbu vacuum. Fix some problems revealed by the same.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rbu-vacuum
Files: files | file ages | folders
SHA1: f042fdd1ea7febec7228e51efc2b0281805e196a
User & Date: dan 2016-04-20 17:47:52
Context
2016-04-20
20:08
Add a documentation comment for sqlite3rbu_vacuum() to sqlite3rbu.h. check-in: da5c753d user: dan tags: rbu-vacuum
17:47
Add fault injection tests for rbu vacuum. Fix some problems revealed by the same. check-in: f042fdd1 user: dan tags: rbu-vacuum
2016-04-19
19:27
Updates to ensure the values of PRAGMA settings like "page_size", "auto_vacuum", "user_version" and "application_id" are not lost when a database is RBU vacuumed. check-in: 74ffea76 user: dan tags: rbu-vacuum
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Added ext/rbu/rbufault3.test.

            1  +# 2016 April 20
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# This file contains fault injection tests for RBU vacuum operations.
           13  +#
           14  +
           15  +source [file join [file dirname [info script]] rbu_common.tcl]
           16  +source $testdir/malloc_common.tcl
           17  +set ::testprefix rbufault3
           18  +
           19  +foreach {fault errlist} {
           20  +  oom-* {
           21  +    {1 SQLITE_NOMEM}
           22  +    {1 SQLITE_IOERR_NOMEM}
           23  +    {1 {SQLITE_NOMEM - out of memory}}
           24  +  }
           25  +
           26  +  ioerr-* {
           27  +    {1 {SQLITE_IOERR - disk I/O error}}
           28  +    {1 SQLITE_IOERR} 
           29  +    {1 SQLITE_IOERR_WRITE} 
           30  +    {1 SQLITE_IOERR_FSYNC} 
           31  +    {1 SQLITE_IOERR_READ} 
           32  +    {1 {SQLITE_IOERR - unable to open database: test.db2}} 
           33  +    {1 {SQLITE_ERROR - unable to open database: test.db2}} 
           34  +    {1 {SQLITE_ERROR - SQL logic error or missing database}}
           35  +  }
           36  +
           37  +  cantopen* {
           38  +    {1 {SQLITE_CANTOPEN - unable to open database: test.db2}}  
           39  +    {1 {SQLITE_CANTOPEN - unable to open database: test.db2}}  
           40  +    {1 {SQLITE_CANTOPEN - unable to open database file}}  
           41  +    {1 SQLITE_CANTOPEN} 
           42  +  }
           43  +
           44  +} {
           45  +
           46  +  reset_db
           47  +  do_execsql_test 0 {
           48  +    CREATE TABLE target(x UNIQUE, y, z, PRIMARY KEY(y));
           49  +    INSERT INTO target VALUES(1, 2, 3);
           50  +    INSERT INTO target VALUES(4, 5, 6);
           51  +    INSERT INTO target VALUES(7, 8, 9);
           52  +    CREATE INDEX i1 ON target(z);
           53  +  }
           54  +  faultsim_save_and_close
           55  +
           56  +  do_faultsim_test 1 -faults $fault -prep {
           57  +    faultsim_restore_and_reopen
           58  +    forcedelete test.db2
           59  +  } -body {
           60  +    sqlite3rbu_vacuum rbu test.db test.db2
           61  +    while {[rbu step]=="SQLITE_OK"} {}
           62  +    rbu close
           63  +  } -test {
           64  +    eval [list faultsim_test_result {0 SQLITE_DONE} {*}$::errlist]
           65  +  }
           66  +
           67  +  do_faultsim_test 2 -faults $fault -prep {
           68  +    faultsim_restore_and_reopen
           69  +    forcedelete test.db2
           70  +  } -body {
           71  +    sqlite3rbu_vacuum rbu test.db test.db2
           72  +    rbu step
           73  +    rbu close
           74  +  } -test {
           75  +    eval [list faultsim_test_result {0 SQLITE_OK} {*}$::errlist]
           76  +  }
           77  +
           78  +  forcedelete test.db2
           79  +  sqlite3rbu_vacuum rbu test.db test.db2
           80  +  rbu step
           81  +  rbu close
           82  +  faultsim_save_and_close
           83  +
           84  +  do_faultsim_test 3 -faults $fault -prep {
           85  +    faultsim_restore_and_reopen
           86  +    forcedelete test.db2
           87  +  } -body {
           88  +    sqlite3rbu_vacuum rbu test.db test.db2
           89  +    rbu step
           90  +    rbu close
           91  +  } -test {
           92  +    eval [list faultsim_test_result {0 SQLITE_OK} {*}$::errlist]
           93  +  }
           94  +
           95  +}
           96  +
           97  +finish_test
           98  +

Changes to ext/rbu/sqlite3rbu.c.

  2335   2335     assert( p->rc==SQLITE_OK );
  2336   2336     assert( p->dbMain==0 && p->dbRbu==0 );
  2337   2337     assert( rbuIsVacuum(p) || p->zTarget!=0 );
  2338   2338   
  2339   2339     /* Open the RBU database */
  2340   2340     p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
  2341   2341   
  2342         -  if( rbuIsVacuum(p) ){
         2342  +  if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
  2343   2343       sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
  2344   2344     }
  2345   2345   
  2346   2346     /* If using separate RBU and state databases, attach the state database to
  2347   2347     ** the RBU db handle now.  */
  2348   2348     if( p->zState ){
  2349   2349       rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
................................................................................
  2351   2351     }else{
  2352   2352       memcpy(p->zStateDb, "main", 4);
  2353   2353     }
  2354   2354   
  2355   2355     /* If it has not already been created, create the rbu_state table */
  2356   2356     rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
  2357   2357   
  2358         -  if( rbuIsVacuum(p) ){
         2358  +  if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
  2359   2359       int bOpen = 0;
         2360  +    int rc;
  2360   2361       p->nRbu = 0;
  2361   2362       p->pRbuFd = 0;
  2362         -    sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
         2363  +    rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
         2364  +    if( rc!=SQLITE_NOTFOUND ) p->rc = rc;
  2363   2365       if( p->eStage>=RBU_STAGE_MOVE ){
  2364   2366         bOpen = 1;
  2365   2367       }else{
  2366   2368         RbuState *pState = rbuLoadState(p);
  2367   2369         if( pState ){
  2368   2370           bOpen = (pState->eStage>RBU_STAGE_MOVE);
  2369   2371           rbuFreeState(pState);
................................................................................
  3407   3409       int i;
  3408   3410       for(i=0; i<5; i++){
  3409   3411         sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
  3410   3412       }
  3411   3413       sqlite3_step(pInsert);
  3412   3414       p->rc = sqlite3_reset(pInsert);
  3413   3415     }
         3416  +  if( p->rc==SQLITE_OK ){
         3417  +    p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
         3418  +  }
  3414   3419   
  3415   3420     rbuFinalize(p, pSql);
  3416   3421     rbuFinalize(p, pInsert);
  3417   3422   }
  3418   3423   
  3419   3424   
  3420   3425   static sqlite3rbu *openRbuHandle(
................................................................................
  3507   3512           if( pState->eStage==0 && rbuIsVacuum(p) ){
  3508   3513             rbuCopyPragma(p, "page_size");
  3509   3514             rbuCopyPragma(p, "auto_vacuum");
  3510   3515           }
  3511   3516   
  3512   3517           /* Open transactions both databases. The *-oal file is opened or
  3513   3518           ** created at this point. */
  3514         -        p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
         3519  +        if( p->rc==SQLITE_OK ){
         3520  +          p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
         3521  +        }
  3515   3522           if( p->rc==SQLITE_OK ){
  3516   3523             p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
  3517   3524           }
  3518   3525   
  3519   3526           /* Check if the main database is a zipvfs db. If it is, set the upper
  3520   3527           ** level pager to use "journal_mode=off". This prevents it from 
  3521   3528           ** generating a large journal using a temp file.  */
................................................................................
  3524   3531             if( frc==SQLITE_OK ){
  3525   3532               p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
  3526   3533             }
  3527   3534           }
  3528   3535   
  3529   3536           /* If this is an RBU vacuum operation and the state table was empty
  3530   3537           ** when this handle was opened, create the target database schema. */
  3531         -        if( pState->eStage==0 && rbuIsVacuum(p) ){
         3538  +        if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
  3532   3539             rbuCreateTargetSchema(p);
  3533   3540             rbuCopyPragma(p, "user_version");
  3534   3541             rbuCopyPragma(p, "application_id");
  3535   3542           }
  3536   3543   
  3537   3544           /* Point the object iterator at the first object */
  3538   3545           if( p->rc==SQLITE_OK ){
................................................................................
  3891   3898         /* If this is being called to read the first page of the target 
  3892   3899         ** database as part of an rbu vacuum operation, synthesize the 
  3893   3900         ** contents of the first page if it does not yet exist. Otherwise,
  3894   3901         ** SQLite will not check for a *-wal file.  */
  3895   3902         if( pRbu && rbuIsVacuum(pRbu) 
  3896   3903             && rc==SQLITE_IOERR_SHORT_READ && iOfst==0
  3897   3904             && (p->openFlags & SQLITE_OPEN_MAIN_DB)
  3898         -          && pRbu->pRbuFd->base.pMethods
         3905  +          && pRbu->rc==SQLITE_OK
  3899   3906         ){
  3900   3907           sqlite3_file *pFd = (sqlite3_file*)pRbu->pRbuFd;
  3901   3908           rc = pFd->pMethods->xRead(pFd, zBuf, iAmt, iOfst);
  3902   3909           if( rc==SQLITE_OK ){
  3903   3910             u8 *aBuf = (u8*)zBuf;
  3904   3911             u32 iRoot = rbuGetU32(&aBuf[52]) ? 1 : 0;
  3905   3912             rbuPutU32(&aBuf[52], iRoot);      /* largest root page number */