/ Check-in [0216b48f]
Login

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

Overview
Comment:Add the experimental sqlite3rbu_vacuum() API function. For opening an RBU handle that rebuilds a database from scratch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rbu-vacuum
Files: files | file ages | folders
SHA1:0216b48f28042ad86711e00802c2da8ce9be3044
User & Date: dan 2016-04-15 20:46:41
Context
2016-04-16
15:03
Fix a couple of assert() statements that were failing with OOM error tests. check-in: 8eb3d7d8 user: dan tags: rbu-vacuum
2016-04-15
20:46
Add the experimental sqlite3rbu_vacuum() API function. For opening an RBU handle that rebuilds a database from scratch. check-in: 0216b48f user: dan tags: rbu-vacuum
15:03
CLI enhancement: Add the ".eqp full" option, that shows both the EXPLAIN QUERY PLAN and the EXPLAIN output for each command run. Also disable any ".wheretrace" and ".selecttrace" when showing EQP output. check-in: 3e217d62 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Added ext/rbu/rbuvacuum.test.

            1  +# 2016 April 15
            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 tests for the RBU module. More specifically, it
           13  +# contains tests to ensure that the sqlite3rbu_vacuum() API works as
           14  +# expected.
           15  +#
           16  +
           17  +source [file join [file dirname [info script]] rbu_common.tcl]
           18  +set ::testprefix rbuvacuum
           19  +
           20  +proc do_rbu_vacuum_test {tn} {
           21  +  uplevel [list do_test $tn.1 {
           22  +    forcedelete state.db
           23  +    if {$::step==0} { sqlite3rbu_vacuum rbu test.db state.db }
           24  +    while 1 {
           25  +      if {$::step==1} { sqlite3rbu_vacuum rbu test.db state.db }
           26  +      set rc [rbu step]
           27  +      if {$rc!="SQLITE_OK"} break
           28  +      if {$::step==1} { rbu close }
           29  +    }
           30  +    rbu close
           31  +  } {SQLITE_DONE}]
           32  +
           33  +  uplevel [list do_execsql_test $tn.2 {
           34  +    PRAGMA integrity_check
           35  +  } ok]
           36  +}
           37  +
           38  +foreach step {0 1} {
           39  +
           40  +  set ::testprefix rbuvacuum-step=$step
           41  +  reset_db
           42  +
           43  +  # Simplest possible vacuum.
           44  +  do_execsql_test 1.0 {
           45  +    PRAGMA page_size = 1024;
           46  +    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
           47  +    INSERT INTO t1 VALUES(1, 2, 3);
           48  +    INSERT INTO t1 VALUES(4, 5, 6);
           49  +    INSERT INTO t1 VALUES(7, 8, 9);
           50  +    PRAGMA integrity_check;
           51  +  } {ok}
           52  +  do_rbu_vacuum_test 1.1
           53  +
           54  +  # A vacuum that actually reclaims space.
           55  +  do_execsql_test 1.2.1 {
           56  +    INSERT INTO t1 VALUES(8, randomblob(900), randomblob(900));
           57  +    INSERT INTO t1 VALUES(9, randomblob(900), randomblob(900));
           58  +    INSERT INTO t1 VALUES(10, randomblob(900), randomblob(900));
           59  +    INSERT INTO t1 VALUES(11, randomblob(900), randomblob(900));
           60  +    INSERT INTO t1 VALUES(12, randomblob(900), randomblob(900));
           61  +    PRAGMA page_count;
           62  +  } {12}
           63  +  do_execsql_test 1.2.2 {
           64  +    DELETE FROM t1 WHERE rowid BETWEEN 8 AND 11;
           65  +    PRAGMA page_count;
           66  +  } {12}
           67  +  do_rbu_vacuum_test 1.2.3
           68  +  do_execsql_test 1.2.4 {
           69  +    PRAGMA page_count;
           70  +  } {3}
           71  +  
           72  +  # Add an index to the table.
           73  +  do_execsql_test 1.3.1 {
           74  +    CREATE INDEX t1b ON t1(b);
           75  +    INSERT INTO t1 VALUES(13, randomblob(900), randomblob(900));
           76  +    INSERT INTO t1 VALUES(14, randomblob(900), randomblob(900));
           77  +    INSERT INTO t1 VALUES(15, randomblob(900), randomblob(900));
           78  +    INSERT INTO t1 VALUES(16, randomblob(900), randomblob(900));
           79  +    PRAGMA page_count;
           80  +  } {18}
           81  +  do_execsql_test 1.3.2 {
           82  +    DELETE FROM t1 WHERE rowid BETWEEN 12 AND 15;
           83  +    PRAGMA page_count;
           84  +  } {18}
           85  +  do_rbu_vacuum_test 1.3.3
           86  +  do_execsql_test 1.3.4 {
           87  +    PRAGMA page_count;
           88  +  } {5}
           89  +
           90  +  # WITHOUT ROWID table.
           91  +  do_execsql_test 1.4.1 {
           92  +    CREATE TABLE t2(a, b, c, PRIMARY KEY(a, b)) WITHOUT ROWID;
           93  +
           94  +    INSERT INTO t2 VALUES(randomblob(900), 1, randomblob(900));
           95  +    INSERT INTO t2 VALUES(randomblob(900), 2, randomblob(900));
           96  +    INSERT INTO t2 VALUES(randomblob(900), 3, randomblob(900));
           97  +    INSERT INTO t2 VALUES(randomblob(900), 4, randomblob(900));
           98  +    INSERT INTO t2 VALUES(randomblob(900), 6, randomblob(900));
           99  +    INSERT INTO t2 VALUES(randomblob(900), 7, randomblob(900));
          100  +    INSERT INTO t2 VALUES(randomblob(900), 8, randomblob(900));
          101  +
          102  +    DELETE FROM t2 WHERE b BETWEEN 2 AND 7;
          103  +    PRAGMA page_count;
          104  +  } {20}
          105  +  do_rbu_vacuum_test 1.4.2
          106  +  do_execsql_test 1.4.3 {
          107  +    PRAGMA page_count;
          108  +  } {10}
          109  +  
          110  +  # WITHOUT ROWID table with an index.
          111  +  do_execsql_test 1.4.1 {
          112  +    CREATE INDEX t2c ON t2(c);
          113  +
          114  +    INSERT INTO t2 VALUES(randomblob(900), 9, randomblob(900));
          115  +    INSERT INTO t2 VALUES(randomblob(900), 10, randomblob(900));
          116  +    INSERT INTO t2 VALUES(randomblob(900), 11, randomblob(900));
          117  +    INSERT INTO t2 VALUES(randomblob(900), 12, randomblob(900));
          118  +    INSERT INTO t2 VALUES(randomblob(900), 13, randomblob(900));
          119  +
          120  +    DELETE FROM t2 WHERE b BETWEEN 8 AND 12;
          121  +    PRAGMA page_count;
          122  +  } {35}
          123  +  do_rbu_vacuum_test 1.4.2
          124  +  do_execsql_test 1.4.3 {
          125  +    PRAGMA page_count;
          126  +  } {15}
          127  +  do_execsql_test 1.4.4 {
          128  +    VACUUM;
          129  +    PRAGMA page_count;
          130  +  } {15}
          131  +
          132  +  do_execsql_test 1.5.1 {
          133  +    CREATE TABLE t3(a, b, c);
          134  +    INSERT INTO t3 VALUES('a', 'b', 'c');
          135  +    INSERT INTO t3 VALUES('d', 'e', 'f');
          136  +    INSERT INTO t3 VALUES('g', 'h', 'i');
          137  +  }
          138  +  do_rbu_vacuum_test 1.5.2
          139  +  do_execsql_test 1.5.3 {
          140  +    SELECT * FROM t3
          141  +  } {a b c d e f g h i}
          142  +  do_execsql_test 1.5.4 {
          143  +    CREATE INDEX t3a ON t3(a);
          144  +    CREATE INDEX t3b ON t3(b);
          145  +    CREATE INDEX t3c ON t3(c);
          146  +    INSERT INTO t3 VALUES('j', 'k', 'l');
          147  +    DELETE FROM t3 WHERE a = 'g';
          148  +  }
          149  +  do_rbu_vacuum_test 1.5.5
          150  +  do_execsql_test 1.5.6 {
          151  +    SELECT rowid, * FROM t3 ORDER BY b
          152  +  } {1 a b c 2 d e f 4 j k l}
          153  +
          154  +  do_execsql_test 1.6.1 {
          155  +    CREATE TABLE t4(a PRIMARY KEY, b, c);
          156  +    INSERT INTO t4 VALUES('a', 'b', 'c');
          157  +    INSERT INTO t4 VALUES('d', 'e', 'f');
          158  +    INSERT INTO t4 VALUES('g', 'h', 'i');
          159  +  }
          160  +  do_rbu_vacuum_test 1.6.2
          161  +  do_execsql_test 1.6.3 {
          162  +    SELECT * FROM t4
          163  +  } {a b c d e f g h i}
          164  +  do_execsql_test 1.6.4 {
          165  +    CREATE INDEX t4a ON t4(a);
          166  +    CREATE INDEX t4b ON t4(b);
          167  +    CREATE INDEX t4c ON t4(c);
          168  +    
          169  +    INSERT INTO t4 VALUES('j', 'k', 'l');
          170  +    DELETE FROM t4 WHERE a='g';
          171  +  }
          172  +  do_rbu_vacuum_test 1.6.5
          173  +  do_execsql_test 1.6.6 {
          174  +    SELECT * FROM t4 ORDER BY b
          175  +  } {a b c d e f j k l}
          176  +
          177  +}
          178  +
          179  +catch { db close }
          180  +finish_test
          181  +

Changes to ext/rbu/sqlite3rbu.c.

   172    172   typedef struct RbuState RbuState;
   173    173   typedef struct rbu_vfs rbu_vfs;
   174    174   typedef struct rbu_file rbu_file;
   175    175   typedef struct RbuUpdateStmt RbuUpdateStmt;
   176    176   
   177    177   #if !defined(SQLITE_AMALGAMATION)
   178    178   typedef unsigned int u32;
          179  +typedef unsigned short u16;
   179    180   typedef unsigned char u8;
   180    181   typedef sqlite3_int64 i64;
   181    182   #endif
   182    183   
   183    184   /*
   184    185   ** These values must match the values defined in wal.c for the equivalent
   185    186   ** locks. These are not magic numbers as they are part of the SQLite file
................................................................................
   398    399     char *zDel;                     /* Delete this when closing file */
   399    400   
   400    401     const char *zWal;               /* Wal filename for this main db file */
   401    402     rbu_file *pWalFd;               /* Wal file descriptor for this main db */
   402    403     rbu_file *pMainNext;            /* Next MAIN_DB file */
   403    404   };
   404    405   
          406  +/*
          407  +** True for an RBU vacuum handle, or false otherwise.
          408  +*/
          409  +#define rbuIsVacuum(p) ((p)->zTarget==0)
          410  +
   405    411   
   406    412   /*************************************************************************
   407    413   ** The following three functions, found below:
   408    414   **
   409    415   **   rbuDeltaGetInt()
   410    416   **   rbuDeltaChecksum()
   411    417   **   rbuDeltaApply()
................................................................................
   846    852     }
   847    853     return rc;
   848    854   }
   849    855   
   850    856   
   851    857   /*
   852    858   ** The implementation of the rbu_target_name() SQL function. This function
   853         -** accepts one argument - the name of a table in the RBU database. If the
   854         -** table name matches the pattern:
          859  +** accepts one or two arguments. The first argument is the name of a table -
          860  +** the name of a table in the RBU database.  The second, if it is present, is 1
          861  +** for a view or 0 for a table. 
          862  +**
          863  +** For a non-vacuum RBU handle, if the table name matches the pattern:
   855    864   **
   856    865   **     data[0-9]_<name>
   857    866   **
   858    867   ** where <name> is any sequence of 1 or more characters, <name> is returned.
   859    868   ** Otherwise, if the only argument does not match the above pattern, an SQL
   860    869   ** NULL is returned.
   861    870   **
   862    871   **     "data_t1"     -> "t1"
   863    872   **     "data0123_t2" -> "t2"
   864    873   **     "dataAB_t3"   -> NULL
          874  +**
          875  +** For an rbu vacuum handle, a copy of the first argument is returned if
          876  +** the second argument is either missing or 0 (not a view).
   865    877   */
   866    878   static void rbuTargetNameFunc(
   867         -  sqlite3_context *context,
          879  +  sqlite3_context *pCtx,
   868    880     int argc,
   869    881     sqlite3_value **argv
   870    882   ){
          883  +  sqlite3rbu *p = sqlite3_user_data(pCtx);
   871    884     const char *zIn;
   872         -  assert( argc==1 );
          885  +  assert( argc==1 || argc==2 );
   873    886   
   874    887     zIn = (const char*)sqlite3_value_text(argv[0]);
   875         -  if( zIn && strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
          888  +  if( zIn ){
          889  +    if( rbuIsVacuum(p) ){
          890  +      if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
          891  +        sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
          892  +      }
          893  +    }else{
          894  +      if( strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
   876    895       int i;
   877    896       for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
   878    897       if( zIn[i]=='_' && zIn[i+1] ){
   879         -      sqlite3_result_text(context, &zIn[i+1], -1, SQLITE_STATIC);
          898  +          sqlite3_result_text(pCtx, &zIn[i+1], -1, SQLITE_STATIC);
          899  +        }
          900  +      }
   880    901       }
   881    902     }
   882    903   }
   883    904   
   884    905   /*
   885    906   ** Initialize the iterator structure passed as the second argument.
   886    907   **
................................................................................
   890    911   ** error code is returned.
   891    912   */
   892    913   static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
   893    914     int rc;
   894    915     memset(pIter, 0, sizeof(RbuObjIter));
   895    916   
   896    917     rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, 
   897         -      "SELECT rbu_target_name(name) AS target, name FROM sqlite_master "
          918  +      "SELECT rbu_target_name(name, type='view') AS target, name "
          919  +      "FROM sqlite_master "
   898    920         "WHERE type IN ('table', 'view') AND target IS NOT NULL "
   899    921         "ORDER BY name"
   900    922     );
   901    923   
   902    924     if( rc==SQLITE_OK ){
   903    925       rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
   904    926           "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
................................................................................
  1266   1288           bRbuRowid = 1;
  1267   1289         }
  1268   1290       }
  1269   1291       sqlite3_finalize(pStmt);
  1270   1292       pStmt = 0;
  1271   1293   
  1272   1294       if( p->rc==SQLITE_OK
         1295  +     && rbuIsVacuum(p)==0
  1273   1296        && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
  1274   1297       ){
  1275   1298         p->rc = SQLITE_ERROR;
  1276   1299         p->zErrmsg = sqlite3_mprintf(
  1277   1300             "table %q %s rbu_rowid column", pIter->zDataTbl,
  1278   1301             (bRbuRowid ? "may not have" : "requires")
  1279   1302         );
................................................................................
  1405   1428         /* An integer primary key. If the table has an explicit IPK, use
  1406   1429         ** its name. Otherwise, use "rbu_rowid".  */
  1407   1430         if( pIter->eType==RBU_PK_IPK ){
  1408   1431           int i;
  1409   1432           for(i=0; pIter->abTblPk[i]==0; i++);
  1410   1433           assert( i<pIter->nTblCol );
  1411   1434           zCol = pIter->azTblCol[i];
         1435  +      }else if( rbuIsVacuum(p) ){
         1436  +        zCol = "_rowid_";
  1412   1437         }else{
  1413   1438           zCol = "rbu_rowid";
  1414   1439         }
  1415   1440         zType = "INTEGER";
  1416   1441       }else{
  1417   1442         zCol = pIter->azTblCol[iCid];
  1418   1443         zType = pIter->azTblType[iCid];
................................................................................
  1945   1970           p->rc = prepareFreeAndCollectError(
  1946   1971               p->dbMain, &pIter->pInsert, &p->zErrmsg,
  1947   1972             sqlite3_mprintf("INSERT INTO \"rbu_imp_%w\" VALUES(%s)", zTbl, zBind)
  1948   1973           );
  1949   1974         }
  1950   1975   
  1951   1976         /* And to delete index entries */
  1952         -      if( p->rc==SQLITE_OK ){
         1977  +      if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
  1953   1978           p->rc = prepareFreeAndCollectError(
  1954   1979               p->dbMain, &pIter->pDelete, &p->zErrmsg,
  1955   1980             sqlite3_mprintf("DELETE FROM \"rbu_imp_%w\" WHERE %s", zTbl, zWhere)
  1956   1981           );
  1957   1982         }
  1958   1983   
  1959   1984         /* Create the SELECT statement to read keys in sorted order */
  1960   1985         if( p->rc==SQLITE_OK ){
  1961   1986           char *zSql;
         1987  +        if( rbuIsVacuum(p) ){
         1988  +          zSql = sqlite3_mprintf(
         1989  +              "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
         1990  +              zCollist, 
         1991  +              pIter->zDataTbl,
         1992  +              zCollist, zLimit
         1993  +          );
         1994  +        }else
         1995  +
  1962   1996           if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
  1963   1997             zSql = sqlite3_mprintf(
  1964   1998                 "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
  1965   1999                 zCollist, p->zStateDb, pIter->zDataTbl,
  1966   2000                 zCollist, zLimit
  1967   2001             );
  1968   2002           }else{
................................................................................
  1981   2015         }
  1982   2016   
  1983   2017         sqlite3_free(zImposterCols);
  1984   2018         sqlite3_free(zImposterPK);
  1985   2019         sqlite3_free(zWhere);
  1986   2020         sqlite3_free(zBind);
  1987   2021       }else{
  1988         -      int bRbuRowid = (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE);
         2022  +      int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
         2023  +                    ||(pIter->eType==RBU_PK_NONE)
         2024  +                    ||(pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p));
  1989   2025         const char *zTbl = pIter->zTbl;       /* Table this step applies to */
  1990   2026         const char *zWrite;                   /* Imposter table name */
  1991   2027   
  1992   2028         char *zBindings = rbuObjIterGetBindlist(p, pIter->nTblCol + bRbuRowid);
  1993   2029         char *zWhere = rbuObjIterGetWhere(p, pIter);
  1994   2030         char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old");
  1995   2031         char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new");
................................................................................
  2008   2044               sqlite3_mprintf(
  2009   2045                 "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)", 
  2010   2046                 zWrite, zTbl, zCollist, (bRbuRowid ? ", _rowid_" : ""), zBindings
  2011   2047               )
  2012   2048           );
  2013   2049         }
  2014   2050   
  2015         -      /* Create the DELETE statement to write to the target PK b-tree */
  2016         -      if( p->rc==SQLITE_OK ){
         2051  +      /* Create the DELETE statement to write to the target PK b-tree.
         2052  +      ** Because it only performs INSERT operations, this is not required for
         2053  +      ** an rbu vacuum handle.  */
         2054  +      if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
  2017   2055           p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pDelete, pz,
  2018   2056               sqlite3_mprintf(
  2019   2057                 "DELETE FROM \"%s%w\" WHERE %s", zWrite, zTbl, zWhere
  2020   2058               )
  2021   2059           );
  2022   2060         }
  2023   2061   
  2024         -      if( pIter->abIndexed ){
         2062  +      if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
  2025   2063           const char *zRbuRowid = "";
  2026   2064           if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
  2027   2065             zRbuRowid = ", rbu_rowid";
  2028   2066           }
  2029   2067   
  2030   2068           /* Create the rbu_tmp_xxx table and the triggers to populate it. */
  2031   2069           rbuMPrintfExec(p, p->dbRbu,
................................................................................
  2067   2105           }
  2068   2106   
  2069   2107           rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid);
  2070   2108         }
  2071   2109   
  2072   2110         /* Create the SELECT statement to read keys from data_xxx */
  2073   2111         if( p->rc==SQLITE_OK ){
         2112  +        const char *zRbuRowid = "";
         2113  +        if( bRbuRowid ){
         2114  +          zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
         2115  +        }
  2074   2116           p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
  2075   2117               sqlite3_mprintf(
  2076         -              "SELECT %s, rbu_control%s FROM '%q'%s", 
  2077         -              zCollist, (bRbuRowid ? ", rbu_rowid" : ""), 
         2118  +              "SELECT %s,%s rbu_control%s FROM '%q'%s", 
         2119  +              zCollist, 
         2120  +              (rbuIsVacuum(p) ? "0 AS " : ""),
         2121  +              zRbuRowid,
  2078   2122                 pIter->zDataTbl, zLimit
  2079   2123               )
  2080   2124           );
  2081   2125         }
  2082   2126   
  2083   2127         sqlite3_free(zWhere);
  2084   2128         sqlite3_free(zOldlist);
................................................................................
  2178   2222         p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
  2179   2223         sqlite3_close(db);
  2180   2224         db = 0;
  2181   2225       }
  2182   2226     }
  2183   2227     return db;
  2184   2228   }
         2229  +
         2230  +/*
         2231  +** Free an RbuState object allocated by rbuLoadState().
         2232  +*/
         2233  +static void rbuFreeState(RbuState *p){
         2234  +  if( p ){
         2235  +    sqlite3_free(p->zTbl);
         2236  +    sqlite3_free(p->zIdx);
         2237  +    sqlite3_free(p);
         2238  +  }
         2239  +}
         2240  +
         2241  +/*
         2242  +** Allocate an RbuState object and load the contents of the rbu_state 
         2243  +** table into it. Return a pointer to the new object. It is the 
         2244  +** responsibility of the caller to eventually free the object using
         2245  +** sqlite3_free().
         2246  +**
         2247  +** If an error occurs, leave an error code and message in the rbu handle
         2248  +** and return NULL.
         2249  +*/
         2250  +static RbuState *rbuLoadState(sqlite3rbu *p){
         2251  +  RbuState *pRet = 0;
         2252  +  sqlite3_stmt *pStmt = 0;
         2253  +  int rc;
         2254  +  int rc2;
         2255  +
         2256  +  pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
         2257  +  if( pRet==0 ) return 0;
         2258  +
         2259  +  rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, 
         2260  +      sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
         2261  +  );
         2262  +  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
         2263  +    switch( sqlite3_column_int(pStmt, 0) ){
         2264  +      case RBU_STATE_STAGE:
         2265  +        pRet->eStage = sqlite3_column_int(pStmt, 1);
         2266  +        if( pRet->eStage!=RBU_STAGE_OAL
         2267  +         && pRet->eStage!=RBU_STAGE_MOVE
         2268  +         && pRet->eStage!=RBU_STAGE_CKPT
         2269  +        ){
         2270  +          p->rc = SQLITE_CORRUPT;
         2271  +        }
         2272  +        break;
         2273  +
         2274  +      case RBU_STATE_TBL:
         2275  +        pRet->zTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
         2276  +        break;
         2277  +
         2278  +      case RBU_STATE_IDX:
         2279  +        pRet->zIdx = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
         2280  +        break;
         2281  +
         2282  +      case RBU_STATE_ROW:
         2283  +        pRet->nRow = sqlite3_column_int(pStmt, 1);
         2284  +        break;
         2285  +
         2286  +      case RBU_STATE_PROGRESS:
         2287  +        pRet->nProgress = sqlite3_column_int64(pStmt, 1);
         2288  +        break;
         2289  +
         2290  +      case RBU_STATE_CKPT:
         2291  +        pRet->iWalCksum = sqlite3_column_int64(pStmt, 1);
         2292  +        break;
         2293  +
         2294  +      case RBU_STATE_COOKIE:
         2295  +        pRet->iCookie = (u32)sqlite3_column_int64(pStmt, 1);
         2296  +        break;
         2297  +
         2298  +      case RBU_STATE_OALSZ:
         2299  +        pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
         2300  +        break;
         2301  +
         2302  +      case RBU_STATE_PHASEONESTEP:
         2303  +        pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
         2304  +        break;
         2305  +
         2306  +      default:
         2307  +        rc = SQLITE_CORRUPT;
         2308  +        break;
         2309  +    }
         2310  +  }
         2311  +  rc2 = sqlite3_finalize(pStmt);
         2312  +  if( rc==SQLITE_OK ) rc = rc2;
         2313  +
         2314  +  p->rc = rc;
         2315  +  return pRet;
         2316  +}
         2317  +
  2185   2318   
  2186   2319   /*
  2187   2320   ** Open the database handle and attach the RBU database as "rbu". If an
  2188   2321   ** error occurs, leave an error code and message in the RBU handle.
  2189   2322   */
  2190   2323   static void rbuOpenDatabase(sqlite3rbu *p){
  2191   2324     assert( p->rc==SQLITE_OK );
  2192   2325     assert( p->dbMain==0 && p->dbRbu==0 );
  2193   2326   
  2194         -  p->eStage = 0;
  2195         -  p->dbMain = rbuOpenDbhandle(p, p->zTarget);
         2327  +  /* Open the RBU database */
  2196   2328     p->dbRbu = rbuOpenDbhandle(p, p->zRbu);
  2197   2329   
  2198   2330     /* If using separate RBU and state databases, attach the state database to
  2199   2331     ** the RBU db handle now.  */
  2200   2332     if( p->zState ){
  2201   2333       rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
  2202   2334       memcpy(p->zStateDb, "stat", 4);
  2203   2335     }else{
  2204   2336       memcpy(p->zStateDb, "main", 4);
  2205   2337     }
         2338  +
         2339  +  /* If it has not already been created, create the rbu_state table */
         2340  +  rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
         2341  +
         2342  +  if( rbuIsVacuum(p) ){
         2343  +    int bOpen = 0;
         2344  +    if( p->eStage>=RBU_STAGE_MOVE ){
         2345  +      bOpen = 1;
         2346  +    }else{
         2347  +      RbuState *pState = rbuLoadState(p);
         2348  +      if( pState ){
         2349  +        bOpen = (pState->eStage>RBU_STAGE_MOVE);
         2350  +        rbuFreeState(pState);
         2351  +      }
         2352  +    }
         2353  +    if( bOpen ) p->dbMain = rbuOpenDbhandle(p, p->zRbu);
         2354  +  }
         2355  +
         2356  +  p->eStage = 0;
         2357  +  if( p->dbMain==0 ){
         2358  +    if( p->zTarget ){
         2359  +      p->dbMain = rbuOpenDbhandle(p, p->zTarget);
         2360  +    }else{
         2361  +      char *zTarget = sqlite3_mprintf("%s-vacuum", p->zRbu);
         2362  +      if( zTarget==0 ){
         2363  +        p->rc = SQLITE_NOMEM;
         2364  +        return;
         2365  +      }
         2366  +      p->dbMain = rbuOpenDbhandle(p, zTarget);
         2367  +      sqlite3_free(zTarget);
         2368  +    }
         2369  +  }
  2206   2370   
  2207   2371     if( p->rc==SQLITE_OK ){
  2208   2372       p->rc = sqlite3_create_function(p->dbMain, 
  2209   2373           "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0
  2210   2374       );
  2211   2375     }
  2212   2376   
................................................................................
  2214   2378       p->rc = sqlite3_create_function(p->dbMain, 
  2215   2379           "rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0
  2216   2380       );
  2217   2381     }
  2218   2382   
  2219   2383     if( p->rc==SQLITE_OK ){
  2220   2384       p->rc = sqlite3_create_function(p->dbRbu, 
  2221         -        "rbu_target_name", 1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
         2385  +        "rbu_target_name", -1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
  2222   2386       );
  2223   2387     }
  2224   2388   
  2225   2389     if( p->rc==SQLITE_OK ){
  2226   2390       p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
  2227   2391     }
  2228   2392     rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
................................................................................
  2473   2637   ** on the database file. This proc moves the *-oal file to the *-wal path,
  2474   2638   ** then reopens the database file (this time in vanilla, non-oal, WAL mode).
  2475   2639   ** If an error occurs, leave an error code and error message in the rbu 
  2476   2640   ** handle.
  2477   2641   */
  2478   2642   static void rbuMoveOalFile(sqlite3rbu *p){
  2479   2643     const char *zBase = sqlite3_db_filename(p->dbMain, "main");
  2480         -
  2481         -  char *zWal = sqlite3_mprintf("%s-wal", zBase);
  2482   2644     char *zOal = sqlite3_mprintf("%s-oal", zBase);
         2645  +  char *zWal;
         2646  +
         2647  +  if( rbuIsVacuum(p) ){
         2648  +    zWal = sqlite3_mprintf("%s-wal", sqlite3_db_filename(p->dbRbu, "main"));
         2649  +  }else{
         2650  +    zWal = sqlite3_mprintf("%s-wal", zBase);
         2651  +  }
  2483   2652   
  2484   2653     assert( p->eStage==RBU_STAGE_MOVE );
  2485   2654     assert( p->rc==SQLITE_OK && p->zErrmsg==0 );
  2486   2655     if( zWal==0 || zOal==0 ){
  2487   2656       p->rc = SQLITE_NOMEM;
  2488   2657     }else{
  2489   2658       /* Move the *-oal file to *-wal. At this point connection p->db is
................................................................................
  2496   2665       rbuLockDatabase(p);
  2497   2666       if( p->rc==SQLITE_OK ){
  2498   2667         rbuFileSuffix3(zBase, zWal);
  2499   2668         rbuFileSuffix3(zBase, zOal);
  2500   2669   
  2501   2670         /* Re-open the databases. */
  2502   2671         rbuObjIterFinalize(&p->objiter);
  2503         -      sqlite3_close(p->dbMain);
  2504   2672         sqlite3_close(p->dbRbu);
         2673  +      sqlite3_close(p->dbMain);
  2505   2674         p->dbMain = 0;
  2506   2675         p->dbRbu = 0;
  2507   2676   
  2508   2677   #if defined(_WIN32_WCE)
  2509   2678         {
  2510   2679           LPWSTR zWideOal;
  2511   2680           LPWSTR zWideWal;
................................................................................
  2659   2828         continue;
  2660   2829       }
  2661   2830   
  2662   2831       pVal = sqlite3_column_value(pIter->pSelect, i);
  2663   2832       p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
  2664   2833       if( p->rc ) return;
  2665   2834     }
  2666         -  if( pIter->zIdx==0
  2667         -   && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE) 
         2835  +  if( pIter->zIdx==0 ){
         2836  +    if( pIter->eType==RBU_PK_VTAB 
         2837  +     || pIter->eType==RBU_PK_NONE 
         2838  +     || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p)) 
  2668   2839     ){
  2669   2840       /* For a virtual table, or a table with no primary key, the 
  2670   2841       ** SELECT statement is:
  2671   2842       **
  2672   2843       **   SELECT <cols>, rbu_control, rbu_rowid FROM ....
  2673   2844       **
  2674   2845       ** Hence column_value(pIter->nCol+1).
  2675   2846       */
  2676         -    assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
         2847  +      assertColumnName(pIter->pSelect, pIter->nCol+1, 
         2848  +          rbuIsVacuum(p) ? "rowid" : "rbu_rowid"
         2849  +      );
  2677   2850       pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
  2678   2851       p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
  2679   2852     }
         2853  +  }
  2680   2854     if( p->rc==SQLITE_OK ){
  2681   2855       sqlite3_step(pWriter);
  2682   2856       p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
  2683   2857     }
  2684   2858   }
  2685   2859   
  2686   2860   /*
................................................................................
  2836   3010           RbuObjIter *pIter = &p->objiter;
  2837   3011           while( p->rc==SQLITE_OK && pIter->zTbl ){
  2838   3012   
  2839   3013             if( pIter->bCleanup ){
  2840   3014               /* Clean up the rbu_tmp_xxx table for the previous table. It 
  2841   3015               ** cannot be dropped as there are currently active SQL statements.
  2842   3016               ** But the contents can be deleted.  */
  2843         -            if( pIter->abIndexed ){
         3017  +            if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
  2844   3018                 rbuMPrintfExec(p, p->dbRbu, 
  2845   3019                     "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
  2846   3020                 );
  2847   3021               }
  2848   3022             }else{
  2849   3023               rbuObjIterPrepareAll(p, pIter, 0);
  2850   3024   
................................................................................
  2923   3097       }
  2924   3098       return p->rc;
  2925   3099     }else{
  2926   3100       return SQLITE_NOMEM;
  2927   3101     }
  2928   3102   }
  2929   3103   
  2930         -/*
  2931         -** Free an RbuState object allocated by rbuLoadState().
  2932         -*/
  2933         -static void rbuFreeState(RbuState *p){
  2934         -  if( p ){
  2935         -    sqlite3_free(p->zTbl);
  2936         -    sqlite3_free(p->zIdx);
  2937         -    sqlite3_free(p);
  2938         -  }
  2939         -}
  2940         -
  2941         -/*
  2942         -** Allocate an RbuState object and load the contents of the rbu_state 
  2943         -** table into it. Return a pointer to the new object. It is the 
  2944         -** responsibility of the caller to eventually free the object using
  2945         -** sqlite3_free().
  2946         -**
  2947         -** If an error occurs, leave an error code and message in the rbu handle
  2948         -** and return NULL.
  2949         -*/
  2950         -static RbuState *rbuLoadState(sqlite3rbu *p){
  2951         -  RbuState *pRet = 0;
  2952         -  sqlite3_stmt *pStmt = 0;
  2953         -  int rc;
  2954         -  int rc2;
  2955         -
  2956         -  pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
  2957         -  if( pRet==0 ) return 0;
  2958         -
  2959         -  rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, 
  2960         -      sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
  2961         -  );
  2962         -  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
  2963         -    switch( sqlite3_column_int(pStmt, 0) ){
  2964         -      case RBU_STATE_STAGE:
  2965         -        pRet->eStage = sqlite3_column_int(pStmt, 1);
  2966         -        if( pRet->eStage!=RBU_STAGE_OAL
  2967         -         && pRet->eStage!=RBU_STAGE_MOVE
  2968         -         && pRet->eStage!=RBU_STAGE_CKPT
  2969         -        ){
  2970         -          p->rc = SQLITE_CORRUPT;
  2971         -        }
  2972         -        break;
  2973         -
  2974         -      case RBU_STATE_TBL:
  2975         -        pRet->zTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
  2976         -        break;
  2977         -
  2978         -      case RBU_STATE_IDX:
  2979         -        pRet->zIdx = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
  2980         -        break;
  2981         -
  2982         -      case RBU_STATE_ROW:
  2983         -        pRet->nRow = sqlite3_column_int(pStmt, 1);
  2984         -        break;
  2985         -
  2986         -      case RBU_STATE_PROGRESS:
  2987         -        pRet->nProgress = sqlite3_column_int64(pStmt, 1);
  2988         -        break;
  2989         -
  2990         -      case RBU_STATE_CKPT:
  2991         -        pRet->iWalCksum = sqlite3_column_int64(pStmt, 1);
  2992         -        break;
  2993         -
  2994         -      case RBU_STATE_COOKIE:
  2995         -        pRet->iCookie = (u32)sqlite3_column_int64(pStmt, 1);
  2996         -        break;
  2997         -
  2998         -      case RBU_STATE_OALSZ:
  2999         -        pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
  3000         -        break;
  3001         -
  3002         -      case RBU_STATE_PHASEONESTEP:
  3003         -        pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
  3004         -        break;
  3005         -
  3006         -      default:
  3007         -        rc = SQLITE_CORRUPT;
  3008         -        break;
  3009         -    }
  3010         -  }
  3011         -  rc2 = sqlite3_finalize(pStmt);
  3012         -  if( rc==SQLITE_OK ) rc = rc2;
  3013         -
  3014         -  p->rc = rc;
  3015         -  return pRet;
  3016         -}
  3017         -
  3018   3104   /*
  3019   3105   ** Compare strings z1 and z2, returning 0 if they are identical, or non-zero
  3020   3106   ** otherwise. Either or both argument may be NULL. Two NULL values are
  3021   3107   ** considered equal, and NULL is considered distinct from all other values.
  3022   3108   */
  3023   3109   static int rbuStrCompare(const char *z1, const char *z2){
  3024   3110     if( z1==0 && z2==0 ) return 0;
................................................................................
  3201   3287           p->rc = sqlite3_finalize(pStmt);
  3202   3288         }
  3203   3289       }
  3204   3290     }
  3205   3291   }
  3206   3292   
  3207   3293   /*
  3208         -** Open and return a new RBU handle. 
         3294  +** The RBU handle passed as the only argument has just been opened and 
         3295  +** the state database is empty. If this RBU handle was opened for an
         3296  +** RBU vacuum operation, create the schema in the target db.
  3209   3297   */
  3210         -sqlite3rbu *sqlite3rbu_open(
         3298  +static void rbuCreateTargetSchema(sqlite3rbu *p){
         3299  +  sqlite3_stmt *pSql = 0;
         3300  +  sqlite3_stmt *pInsert = 0;
         3301  +  int rc2;
         3302  +
         3303  +  assert( rbuIsVacuum(p) );
         3304  +
         3305  +  p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg, 
         3306  +    "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
         3307  +    " ORDER BY type DESC"
         3308  +  );
         3309  +  while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
         3310  +    const char *zSql = sqlite3_column_text(pSql, 0);
         3311  +    p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
         3312  +  }
         3313  +  rbuFinalize(p, pSql);
         3314  +  if( p->rc!=SQLITE_OK ) return;
         3315  +
         3316  +  p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
         3317  +
         3318  +  if( p->rc==SQLITE_OK ){
         3319  +    p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg, 
         3320  +        "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL" 
         3321  +    );
         3322  +  }
         3323  +
         3324  +  if( p->rc==SQLITE_OK ){
         3325  +    p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg, 
         3326  +        "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
         3327  +    );
         3328  +  }
         3329  +
         3330  +  while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
         3331  +    int i;
         3332  +    for(i=0; i<5; i++){
         3333  +      sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
         3334  +    }
         3335  +    sqlite3_step(pInsert);
         3336  +    p->rc = sqlite3_reset(pInsert);
         3337  +  }
         3338  +
         3339  +  rbuFinalize(p, pSql);
         3340  +  rbuFinalize(p, pInsert);
         3341  +}
         3342  +
         3343  +
         3344  +static sqlite3rbu *openRbuHandle(
  3211   3345     const char *zTarget, 
  3212   3346     const char *zRbu,
  3213   3347     const char *zState
  3214   3348   ){
  3215   3349     sqlite3rbu *p;
  3216         -  size_t nTarget = strlen(zTarget);
         3350  +  size_t nTarget = zTarget ? strlen(zTarget) : 0;
  3217   3351     size_t nRbu = strlen(zRbu);
  3218   3352     size_t nState = zState ? strlen(zState) : 0;
  3219   3353     size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1+ nState+1;
  3220   3354   
  3221   3355     p = (sqlite3rbu*)sqlite3_malloc64(nByte);
  3222   3356     if( p ){
  3223   3357       RbuState *pState = 0;
  3224   3358   
  3225   3359       /* Create the custom VFS. */
  3226   3360       memset(p, 0, sizeof(sqlite3rbu));
  3227   3361       rbuCreateVfs(p);
  3228   3362   
  3229         -    /* Open the target database */
         3363  +    /* Open the target, RBU and state databases */
  3230   3364       if( p->rc==SQLITE_OK ){
  3231         -      p->zTarget = (char*)&p[1];
         3365  +      char *pCsr = (char*)&p[1];
         3366  +      if( zTarget ){
         3367  +        p->zTarget = pCsr;
  3232   3368         memcpy(p->zTarget, zTarget, nTarget+1);
  3233         -      p->zRbu = &p->zTarget[nTarget+1];
         3369  +        pCsr += nTarget+1;
         3370  +      }
         3371  +      p->zRbu = pCsr;
  3234   3372         memcpy(p->zRbu, zRbu, nRbu+1);
         3373  +      pCsr += nRbu+1;
  3235   3374         if( zState ){
  3236         -        p->zState = &p->zRbu[nRbu+1];
         3375  +        p->zState = pCsr;
  3237   3376           memcpy(p->zState, zState, nState+1);
  3238   3377         }
  3239   3378         rbuOpenDatabase(p);
  3240   3379       }
  3241   3380   
  3242         -    /* If it has not already been created, create the rbu_state table */
  3243         -    rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
  3244         -
  3245   3381       if( p->rc==SQLITE_OK ){
  3246   3382         pState = rbuLoadState(p);
  3247   3383         assert( pState || p->rc!=SQLITE_OK );
  3248   3384         if( p->rc==SQLITE_OK ){
  3249   3385   
  3250   3386           if( pState->eStage==0 ){ 
  3251   3387             rbuDeleteOalFile(p);
................................................................................
  3299   3435           ** generating a large journal using a temp file.  */
  3300   3436           if( p->rc==SQLITE_OK ){
  3301   3437             int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
  3302   3438             if( frc==SQLITE_OK ){
  3303   3439               p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
  3304   3440             }
  3305   3441           }
         3442  +
         3443  +        /* If this is an RBU vacuum operation and the state table was empty
         3444  +        ** when this handle was opened, create the target database schema. */
         3445  +        if( pState->eStage==0 && rbuIsVacuum(p) ){
         3446  +          rbuCreateTargetSchema(p);
         3447  +        }
  3306   3448   
  3307   3449           /* Point the object iterator at the first object */
  3308   3450           if( p->rc==SQLITE_OK ){
  3309   3451             p->rc = rbuObjIterFirst(p, &p->objiter);
  3310   3452           }
  3311   3453   
  3312   3454           /* If the RBU database contains no data_xxx tables, declare the RBU
................................................................................
  3332   3474   
  3333   3475       rbuFreeState(pState);
  3334   3476     }
  3335   3477   
  3336   3478     return p;
  3337   3479   }
  3338   3480   
         3481  +/*
         3482  +** Open and return a new RBU handle. 
         3483  +*/
         3484  +sqlite3rbu *sqlite3rbu_open(
         3485  +  const char *zTarget, 
         3486  +  const char *zRbu,
         3487  +  const char *zState
         3488  +){
         3489  +  /* TODO: Check that zTarget and zRbu are non-NULL */
         3490  +  return openRbuHandle(zTarget, zRbu, zState);
         3491  +}
         3492  +
         3493  +/*
         3494  +** Open a handle to begin or resume an RBU VACUUM operation.
         3495  +*/
         3496  +sqlite3rbu *sqlite3rbu_vacuum(
         3497  +  const char *zTarget, 
         3498  +  const char *zState
         3499  +){
         3500  +  /* TODO: Check that both arguments are non-NULL */
         3501  +  return openRbuHandle(0, zTarget, zState);
         3502  +}
  3339   3503   
  3340   3504   /*
  3341   3505   ** Return the database handle used by pRbu.
  3342   3506   */
  3343   3507   sqlite3 *sqlite3rbu_db(sqlite3rbu *pRbu, int bRbu){
  3344   3508     sqlite3 *db = 0;
  3345   3509     if( pRbu ){
................................................................................
  3387   3551         p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
  3388   3552       }
  3389   3553   
  3390   3554       /* Close any open statement handles. */
  3391   3555       rbuObjIterFinalize(&p->objiter);
  3392   3556   
  3393   3557       /* Close the open database handle and VFS object. */
  3394         -    sqlite3_close(p->dbMain);
  3395   3558       sqlite3_close(p->dbRbu);
         3559  +    sqlite3_close(p->dbMain);
  3396   3560       rbuDeleteVfs(p);
  3397   3561       sqlite3_free(p->aBuf);
  3398   3562       sqlite3_free(p->aFrame);
  3399   3563   
  3400   3564       rbuEditErrmsg(p);
  3401   3565       rc = p->rc;
  3402   3566       *pzErrmsg = p->zErrmsg;
................................................................................
  3589   3753   */
  3590   3754   static u32 rbuGetU32(u8 *aBuf){
  3591   3755     return ((u32)aBuf[0] << 24)
  3592   3756          + ((u32)aBuf[1] << 16)
  3593   3757          + ((u32)aBuf[2] <<  8)
  3594   3758          + ((u32)aBuf[3]);
  3595   3759   }
         3760  +
         3761  +/*
         3762  +** Write an unsigned 32-bit value in big-endian format to the supplied
         3763  +** buffer.
         3764  +*/
         3765  +static void rbuPutU32(u8 *aBuf, u32 iVal){
         3766  +  aBuf[0] = (iVal >> 24) & 0xFF;
         3767  +  aBuf[1] = (iVal >> 16) & 0xFF;
         3768  +  aBuf[2] = (iVal >>  8) & 0xFF;
         3769  +  aBuf[3] = (iVal >>  0) & 0xFF;
         3770  +}
         3771  +
         3772  +/*
         3773  +** Write an unsigned 16-bit value in big-endian format to the supplied
         3774  +** buffer.
         3775  +*/
         3776  +static void rbuPutU16(u8 *aBuf, u16 iVal){
         3777  +  aBuf[0] = (iVal >>  8) & 0xFF;
         3778  +  aBuf[1] = (iVal >>  0) & 0xFF;
         3779  +}
  3596   3780   
  3597   3781   /*
  3598   3782   ** Read data from an rbuVfs-file.
  3599   3783   */
  3600   3784   static int rbuVfsRead(
  3601   3785     sqlite3_file *pFile, 
  3602   3786     void *zBuf, 
................................................................................
  3615   3799        && (p->openFlags & SQLITE_OPEN_WAL) 
  3616   3800        && iOfst>=pRbu->iOalSz 
  3617   3801       ){
  3618   3802         rc = SQLITE_OK;
  3619   3803         memset(zBuf, 0, iAmt);
  3620   3804       }else{
  3621   3805         rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
         3806  +      /* If this is being called to read the first page of the target 
         3807  +      ** database as part of an rbu vacuum operation, synthesize the 
         3808  +      ** contents of the first page if it does not yet exist. Otherwise,
         3809  +      ** SQLite will not check for a *-wal file.  */
         3810  +      if( p->pRbu && rbuIsVacuum(p->pRbu) 
         3811  +       && rc==SQLITE_IOERR_SHORT_READ && iOfst==0
         3812  +       && (p->openFlags & SQLITE_OPEN_MAIN_DB)
         3813  +      ){
         3814  +        sqlite3_file *pFd = 0;
         3815  +        rc = sqlite3_file_control(
         3816  +            p->pRbu->dbRbu, "main", SQLITE_FCNTL_FILE_POINTER, (void*)&pFd
         3817  +        );
         3818  +        if( rc==SQLITE_OK ){
         3819  +          rc = pFd->pMethods->xRead(pFd, zBuf, iAmt, iOfst);
         3820  +        }
         3821  +        if( rc==SQLITE_OK ){
         3822  +          rbuPutU32(&zBuf[52], 0);          /* largest root page number */
         3823  +          rbuPutU32(&zBuf[36], 0);          /* number of free pages */
         3824  +          rbuPutU32(&zBuf[32], 0);          /* first page on free list trunk */
         3825  +          rbuPutU32(&zBuf[28], 1);          /* size of db file in pages */
         3826  +
         3827  +          if( iAmt>100 ){
         3828  +            assert( iAmt>=101 );
         3829  +            memset(&zBuf[101], 0, iAmt-101);
         3830  +            rbuPutU16(&zBuf[105], iAmt & 0xFFFF);
         3831  +          }
         3832  +        }
         3833  +      }
  3622   3834       }
  3623   3835       if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
  3624   3836         /* These look like magic numbers. But they are stable, as they are part
  3625   3837          ** of the definition of the SQLite file format, which may not change. */
  3626   3838         u8 *pBuf = (u8*)zBuf;
  3627   3839         p->iCookie = rbuGetU32(&pBuf[24]);
  3628   3840         p->iWriteVer = pBuf[19];
................................................................................
  3689   3901   }
  3690   3902   
  3691   3903   /*
  3692   3904   ** Return the current file-size of an rbuVfs-file.
  3693   3905   */
  3694   3906   static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
  3695   3907     rbu_file *p = (rbu_file *)pFile;
  3696         -  return p->pReal->pMethods->xFileSize(p->pReal, pSize);
         3908  +  int rc;
         3909  +  rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
         3910  +
         3911  +  /* If this is an RBU vacuum operation and this is the target database,
         3912  +  ** pretend that it has at least one page. Otherwise, SQLite will not
         3913  +  ** check for the existance of a *-wal file. rbuVfsRead() contains 
         3914  +  ** similar logic.  */
         3915  +  if( rc==SQLITE_OK && *pSize==0 
         3916  +   && p->pRbu && rbuIsVacuum(p->pRbu) 
         3917  +   && (p->openFlags & SQLITE_OPEN_MAIN_DB)
         3918  +  ){
         3919  +    *pSize = 1024;
         3920  +  }
         3921  +  return rc;
  3697   3922   }
  3698   3923   
  3699   3924   /*
  3700   3925   ** Lock an rbuVfs-file.
  3701   3926   */
  3702   3927   static int rbuVfsLock(sqlite3_file *pFile, int eLock){
  3703   3928     rbu_file *p = (rbu_file*)pFile;

Changes to ext/rbu/sqlite3rbu.h.

   309    309   ** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
   310    310   */
   311    311   sqlite3rbu *sqlite3rbu_open(
   312    312     const char *zTarget, 
   313    313     const char *zRbu,
   314    314     const char *zState
   315    315   );
          316  +
          317  +/*
          318  +** Open an RBU handle to perform an RBU vacuum database file zTarget.
          319  +*/
          320  +sqlite3rbu *sqlite3rbu_vacuum(
          321  +  const char *zTarget, 
          322  +  const char *zState
          323  +);
   316    324   
   317    325   /*
   318    326   ** Internally, each RBU connection uses a separate SQLite database 
   319    327   ** connection to access the target and rbu update databases. This
   320    328   ** API allows the application direct access to these database handles.
   321    329   **
   322    330   ** The first argument passed to this function must be a valid, open, RBU

Changes to ext/rbu/test_rbu.c.

   182    182     if( objc==5 ) zStateDb = Tcl_GetString(objv[4]);
   183    183   
   184    184     pRbu = sqlite3rbu_open(zTarget, zRbu, zStateDb);
   185    185     Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
   186    186     Tcl_SetObjResult(interp, objv[1]);
   187    187     return TCL_OK;
   188    188   }
          189  +
          190  +/*
          191  +** Tclcmd: sqlite3rbu_vacuum CMD <target-db> <state-db>
          192  +*/
          193  +static int test_sqlite3rbu_vacuum(
          194  +  ClientData clientData,
          195  +  Tcl_Interp *interp,
          196  +  int objc,
          197  +  Tcl_Obj *CONST objv[]
          198  +){
          199  +  sqlite3rbu *pRbu = 0;
          200  +  const char *zCmd;
          201  +  const char *zTarget;
          202  +  const char *zStateDb = 0;
          203  +
          204  +  if( objc!=4 ){
          205  +    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB STATE-DB");
          206  +    return TCL_ERROR;
          207  +  }
          208  +  zCmd = Tcl_GetString(objv[1]);
          209  +  zTarget = Tcl_GetString(objv[2]);
          210  +  zStateDb = Tcl_GetString(objv[3]);
          211  +
          212  +  pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
          213  +  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
          214  +  Tcl_SetObjResult(interp, objv[1]);
          215  +  return TCL_OK;
          216  +}
   189    217   
   190    218   /*
   191    219   ** Tclcmd: sqlite3rbu_create_vfs ?-default? NAME PARENT
   192    220   */
   193    221   static int test_sqlite3rbu_create_vfs(
   194    222     ClientData clientData,
   195    223     Tcl_Interp *interp,
................................................................................
   270    298   
   271    299   int SqliteRbu_Init(Tcl_Interp *interp){ 
   272    300     static struct {
   273    301        char *zName;
   274    302        Tcl_ObjCmdProc *xProc;
   275    303     } aObjCmd[] = {
   276    304       { "sqlite3rbu", test_sqlite3rbu },
          305  +    { "sqlite3rbu_vacuum", test_sqlite3rbu_vacuum },
   277    306       { "sqlite3rbu_create_vfs", test_sqlite3rbu_create_vfs },
   278    307       { "sqlite3rbu_destroy_vfs", test_sqlite3rbu_destroy_vfs },
   279    308       { "sqlite3rbu_internal_test", test_sqlite3rbu_internal_test },
   280    309     };
   281    310     int i;
   282    311     for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
   283    312       Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, 0, 0);