/ Changes On Branch rbu-opt
Login

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

Changes In Branch rbu-opt Excluding Merge-Ins

This is equivalent to a diff from c2e439bc to 7b051698

2019-05-07
16:37
Optimize the restarting of an RBU vacuum. (check-in: 82062351 user: dan tags: trunk)
16:28
Remove some redundant code from sqlite3rbu.c. Add test cases for RBU vacuum. (Closed-Leaf check-in: 7b051698 user: dan tags: rbu-opt)
2019-05-06
20:40
Optimize further cases of restarting an RBU vacuum. (check-in: 6b3261bf user: dan tags: rbu-opt)
16:15
Fix a problem with renaming an INTEGER PRIMARY KEY column of a WITHOUT ROWID table using ALTER TABLE. (check-in: 91f701d3 user: dan tags: trunk)
2019-05-04
20:04
Optimize some cases of restarting an RBU vacuum. (check-in: cdc09867 user: dan tags: rbu-opt)
17:32
Fix the NOT NULL logic in the theorem prover that determines when a partial index can be used. Ticket [5c6955204c392ae763a95]. (check-in: c2e439bc user: drh tags: trunk)
03:56
Fix harmless compiler warning seen with MSVC. (check-in: 5862b83e user: mistachkin tags: trunk)

Changes to ext/rbu/rbu_common.tcl.

    85     85     }
    86     86     set rc
    87     87   }
    88     88   
    89     89   proc do_rbu_vacuum_test {tn step {statedb state.db}} {
    90     90     forcedelete $statedb
    91     91     if {$statedb=="" && $step==1} breakpoint
    92         -  uplevel [list do_test $tn.1 [string map [list %state% $statedb] {
    93         -    if {$step==0} { sqlite3rbu_vacuum rbu test.db {%state%}}
           92  +  uplevel [list do_test $tn.1 [string map [list %state% $statedb %step% $step] {
           93  +    if {%step%==0} { sqlite3rbu_vacuum rbu test.db {%state%}}
    94     94       while 1 {
    95         -      if {$step==1} { sqlite3rbu_vacuum rbu test.db {%state%}}
           95  +      if {%step%==1} { sqlite3rbu_vacuum rbu test.db {%state%}}
    96     96         set state [rbu state]
    97     97         check_prestep_state test.db $state
    98     98         set rc [rbu step]
    99     99         check_poststep_state $rc test.db $state
   100    100         if {$rc!="SQLITE_OK"} break
   101         -      if {$step==1} { rbu close }
          101  +      if {%step%==1} { rbu close }
   102    102       }
   103    103       rbu close
   104    104     }] {SQLITE_DONE}]
   105    105   
   106    106     uplevel [list do_execsql_test $tn.2 {
   107    107       PRAGMA integrity_check
   108    108     } ok]
   109    109   }
   110    110   

Changes to ext/rbu/rbupartial.test.

    76     76       SELECT * FROM t1 ORDER BY %A%;
    77     77     } {
    78     78       1 10 {} b   7 8 4 d   10 11 12 e   13 14 {} f
    79     79     }
    80     80   
    81     81     set step 0
    82     82     do_rbu_vacuum_test $tn.1.5 0
           83  +
           84  +  do_test $tn.1.6 {
           85  +    execsql { PRAGMA integrity_check }
           86  +  } {ok}
    83     87     }]
    84     88   }
    85     89   
    86     90   finish_test

Added ext/rbu/rbuvacuum4.test.

            1  +# 2019 Jan 3
            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 rbuvacuum4
           19  +
           20  +set step 1
           21  +
           22  +do_execsql_test 1.0 {
           23  +  CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
           24  +  INSERT INTO t1 VALUES(1, 2, 3);
           25  +  INSERT INTO t1 VALUES(4, 5, 6);
           26  +  INSERT INTO t1 VALUES(7, 8, 9);
           27  +}
           28  +do_rbu_vacuum_test 1.1 1
           29  +
           30  +#-------------------------------------------------------------------------
           31  +reset_db
           32  +
           33  +do_execsql_test 2.0 {
           34  +  CREATE TABLE t1(a, b, c, PRIMARY KEY(a, b, c)) WITHOUT ROWID;
           35  +  INSERT INTO t1 VALUES(1, 2, 3);
           36  +  INSERT INTO t1 VALUES(4, 5, 6);
           37  +  INSERT INTO t1 VALUES(7, 8, 9);
           38  +}
           39  +do_rbu_vacuum_test 2.1 1
           40  +do_execsql_test 2.2 {
           41  +  SELECT * FROM t1;
           42  +} {1 2 3 4 5 6 7 8 9}
           43  +
           44  +#-------------------------------------------------------------------------
           45  +reset_db
           46  +
           47  +do_execsql_test 3.0 {
           48  +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
           49  +  CREATE INDEX i1 oN t1(b, c);
           50  +  INSERT INTO t1 VALUES(1, 2, 3);
           51  +  INSERT INTO t1 VALUES(4, 5, 6);
           52  +  INSERT INTO t1 VALUES(7, 8, 9);
           53  +}
           54  +
           55  +do_rbu_vacuum_test 3.1 1
           56  +
           57  +do_execsql_test 3.2 {
           58  +  SELECT * FROM t1;
           59  +} {1 2 3 4 5 6 7 8 9}
           60  +
           61  +#-------------------------------------------------------------------------
           62  +reset_db
           63  +do_execsql_test 4.0 {
           64  +  CREATE TABLE x1(a, b, c, d, PRIMARY KEY(c, b)) WITHOUT ROWID;
           65  +  INSERT INTO x1 VALUES(1, 1, 1, 1);
           66  +  INSERT INTO x1 VALUES(1, 1, 2, 1);
           67  +  INSERT INTO x1 VALUES(1, 2, 2, 1);
           68  +
           69  +  INSERT INTO x1 VALUES(NULL, 2, 3, NULL);
           70  +  INSERT INTO x1 VALUES(NULL, 2, 4, NULL);
           71  +  INSERT INTO x1 VALUES(NULL, 2, 5, NULL);
           72  +
           73  +  CREATE INDEX x1ad ON x1(d, a);
           74  +  CREATE INDEX x1null ON x1(d, a) WHERE d>15;
           75  +}
           76  +
           77  +do_rbu_vacuum_test 4.1.1 1
           78  +
           79  +do_execsql_test 4.2 {
           80  +  SELECT count(*) fROM x1
           81  +} 6
           82  +
           83  +do_rbu_vacuum_test 4.1.2 0
           84  +
           85  +#-------------------------------------------------------------------------
           86  +reset_db
           87  +do_execsql_test 5.0 {
           88  +  CREATE TABLE "a b c"(a, "b b" PRIMARY KEY, "c c");
           89  +  CREATE INDEX abc1 ON "a b c"(a, "c c");
           90  +
           91  +  INSERT INTO "a b c" VALUES(NULL, 'a', NULL);
           92  +  INSERT INTO "a b c" VALUES(NULL, 'b', NULL);
           93  +  INSERT INTO "a b c" VALUES(NULL, 'c', NULL);
           94  +
           95  +  INSERT INTO "a b c" VALUES(1, 2, 3);
           96  +  INSERT INTO "a b c" VALUES(3, 9, 1);
           97  +  INSERT INTO "a b c" VALUES('aaa', 'bbb', 'ccc');
           98  +
           99  +  CREATE TABLE x(a);
          100  +  INSERT INTO x VALUES('a'), ('b'), ('d');
          101  +  CREATE UNIQUE INDEX y ON x(a);
          102  +}
          103  +
          104  +do_rbu_vacuum_test 5.1 1
          105  +
          106  +finish_test
          107  +

Changes to ext/rbu/sqlite3rbu.c.

   926    926     sqlite3rbu *p = sqlite3_user_data(pCtx);
   927    927     const char *zIn;
   928    928     assert( argc==1 || argc==2 );
   929    929   
   930    930     zIn = (const char*)sqlite3_value_text(argv[0]);
   931    931     if( zIn ){
   932    932       if( rbuIsVacuum(p) ){
   933         -      if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
          933  +      assert( argc==2 );
          934  +      if( 0==sqlite3_value_int(argv[1]) ){
   934    935           sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
   935    936         }
   936    937       }else{
   937    938         if( strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
   938    939           int i;
   939    940           for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
   940    941           if( zIn[i]=='_' && zIn[i+1] ){
................................................................................
  1377   1378   
  1378   1379           if( i!=iOrder ){
  1379   1380             SWAP(int, pIter->aiSrcOrder[i], pIter->aiSrcOrder[iOrder]);
  1380   1381             SWAP(char*, pIter->azTblCol[i], pIter->azTblCol[iOrder]);
  1381   1382           }
  1382   1383   
  1383   1384           pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
  1384         -        pIter->abTblPk[iOrder] = (iPk!=0);
         1385  +        assert( iPk>=0 );
         1386  +        pIter->abTblPk[iOrder] = (u8)iPk;
  1385   1387           pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
  1386   1388           iOrder++;
  1387   1389         }
  1388   1390       }
  1389   1391   
  1390   1392       rbuFinalize(p, pStmt);
  1391   1393       rbuObjIterCacheIndexedCols(p, pIter);
................................................................................
  1411   1413     for(i=0; i<pIter->nTblCol; i++){
  1412   1414       const char *z = pIter->azTblCol[i];
  1413   1415       zList = rbuMPrintf(p, "%z%s\"%w\"", zList, zSep, z);
  1414   1416       zSep = ", ";
  1415   1417     }
  1416   1418     return zList;
  1417   1419   }
         1420  +
         1421  +/*
         1422  +** Return a comma separated list of the quoted PRIMARY KEY column names,
         1423  +** in order, for the current table. Before each column name, add the text
         1424  +** zPre. After each column name, add the zPost text. Use zSeparator as
         1425  +** the separator text (usually ", ").
         1426  +*/
         1427  +static char *rbuObjIterGetPkList(
         1428  +  sqlite3rbu *p,                  /* RBU object */
         1429  +  RbuObjIter *pIter,              /* Object iterator for column names */
         1430  +  const char *zPre,               /* Before each quoted column name */
         1431  +  const char *zSeparator,         /* Separator to use between columns */
         1432  +  const char *zPost               /* After each quoted column name */
         1433  +){
         1434  +  int iPk = 1;
         1435  +  char *zRet = 0;
         1436  +  const char *zSep = "";
         1437  +  while( 1 ){
         1438  +    int i;
         1439  +    for(i=0; i<pIter->nTblCol; i++){
         1440  +      if( (int)pIter->abTblPk[i]==iPk ){
         1441  +        const char *zCol = pIter->azTblCol[i];
         1442  +        zRet = rbuMPrintf(p, "%z%s%s\"%w\"%s", zRet, zSep, zPre, zCol, zPost);
         1443  +        zSep = zSeparator;
         1444  +        break;
         1445  +      }
         1446  +    }
         1447  +    if( i==pIter->nTblCol ) break;
         1448  +    iPk++;
         1449  +  }
         1450  +  return zRet;
         1451  +}
         1452  +
         1453  +/*
         1454  +** This function is called as part of restarting an RBU vacuum within 
         1455  +** stage 1 of the process (while the *-oal file is being built) while
         1456  +** updating a table (not an index). The table may be a rowid table or
         1457  +** a WITHOUT ROWID table. It queries the target database to find the 
         1458  +** largest key that has already been written to the target table and
         1459  +** constructs a WHERE clause that can be used to extract the remaining
         1460  +** rows from the source table. For a rowid table, the WHERE clause
         1461  +** is of the form:
         1462  +**
         1463  +**     "WHERE _rowid_ > ?"
         1464  +**
         1465  +** and for WITHOUT ROWID tables:
         1466  +**
         1467  +**     "WHERE (key1, key2) > (?, ?)"
         1468  +**
         1469  +** Instead of "?" placeholders, the actual WHERE clauses created by
         1470  +** this function contain literal SQL values.
         1471  +*/
         1472  +static char *rbuVacuumTableStart(
         1473  +  sqlite3rbu *p,                  /* RBU handle */
         1474  +  RbuObjIter *pIter,              /* RBU iterator object */
         1475  +  int bRowid,                     /* True for a rowid table */
         1476  +  const char *zWrite              /* Target table name prefix */
         1477  +){
         1478  +  sqlite3_stmt *pMax = 0;
         1479  +  char *zRet = 0;
         1480  +  if( bRowid ){
         1481  +    p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg, 
         1482  +        sqlite3_mprintf(
         1483  +          "SELECT max(_rowid_) FROM \"%s%w\"", zWrite, pIter->zTbl
         1484  +        )
         1485  +    );
         1486  +    if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
         1487  +      sqlite3_int64 iMax = sqlite3_column_int64(pMax, 0);
         1488  +      zRet = rbuMPrintf(p, " WHERE _rowid_ > %lld ", iMax);
         1489  +    }
         1490  +    rbuFinalize(p, pMax);
         1491  +  }else{
         1492  +    char *zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", " DESC");
         1493  +    char *zSelect = rbuObjIterGetPkList(p, pIter, "quote(", "||','||", ")");
         1494  +    char *zList = rbuObjIterGetPkList(p, pIter, "", ", ", "");
         1495  +
         1496  +    if( p->rc==SQLITE_OK ){
         1497  +      p->rc = prepareFreeAndCollectError(p->dbMain, &pMax, &p->zErrmsg, 
         1498  +          sqlite3_mprintf(
         1499  +            "SELECT %s FROM \"%s%w\" ORDER BY %s LIMIT 1", 
         1500  +                zSelect, zWrite, pIter->zTbl, zOrder
         1501  +          )
         1502  +      );
         1503  +      if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pMax) ){
         1504  +        const char *zVal = (const char*)sqlite3_column_text(pMax, 0);
         1505  +        zRet = rbuMPrintf(p, " WHERE (%s) > (%s) ", zList, zVal);
         1506  +      }
         1507  +      rbuFinalize(p, pMax);
         1508  +    }
         1509  +
         1510  +    sqlite3_free(zOrder);
         1511  +    sqlite3_free(zSelect);
         1512  +    sqlite3_free(zList);
         1513  +  }
         1514  +  return zRet;
         1515  +}
         1516  +
         1517  +/*
         1518  +** This function is called as part of restating an RBU vacuum when the
         1519  +** current operation is writing content to an index. If possible, it
         1520  +** queries the target index b-tree for the largest key already written to
         1521  +** it, then composes and returns an expression that can be used in a WHERE 
         1522  +** clause to select the remaining required rows from the source table. 
         1523  +** It is only possible to return such an expression if:
         1524  +**
         1525  +**   * The index contains no DESC columns, and
         1526  +**   * The last key written to the index before the operation was 
         1527  +**     suspended does not contain any NULL values.
         1528  +**
         1529  +** The expression is of the form:
         1530  +**
         1531  +**   (index-field1, index-field2, ...) > (?, ?, ...)
         1532  +**
         1533  +** except that the "?" placeholders are replaced with literal values.
         1534  +**
         1535  +** If the expression cannot be created, NULL is returned. In this case,
         1536  +** the caller has to use an OFFSET clause to extract only the required 
         1537  +** rows from the sourct table, just as it does for an RBU update operation.
         1538  +*/
         1539  +char *rbuVacuumIndexStart(
         1540  +  sqlite3rbu *p,                  /* RBU handle */
         1541  +  RbuObjIter *pIter               /* RBU iterator object */
         1542  +){
         1543  +  char *zOrder = 0;
         1544  +  char *zLhs = 0;
         1545  +  char *zSelect = 0;
         1546  +  char *zVector = 0;
         1547  +  char *zRet = 0;
         1548  +  int bFailed = 0;
         1549  +
         1550  +  if( p->rc==SQLITE_OK ){
         1551  +    const char *zSep = "";
         1552  +    int iCol = 0;
         1553  +    sqlite3_stmt *pXInfo = 0;
         1554  +    p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
         1555  +        sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
         1556  +    );
         1557  +    while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
         1558  +      int iCid = sqlite3_column_int(pXInfo, 1);
         1559  +      const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
         1560  +      const char *zCol;
         1561  +      if( sqlite3_column_int(pXInfo, 3) ){
         1562  +        bFailed = 1;
         1563  +        break;
         1564  +      }
         1565  +
         1566  +      if( iCid<0 ){
         1567  +        if( pIter->eType==RBU_PK_IPK ){
         1568  +          int i;
         1569  +          for(i=0; pIter->abTblPk[i]==0; i++);
         1570  +          assert( i<pIter->nTblCol );
         1571  +          zCol = pIter->azTblCol[i];
         1572  +        }else{
         1573  +          zCol = "_rowid_";
         1574  +        }
         1575  +      }else{
         1576  +        zCol = pIter->azTblCol[iCid];
         1577  +      }
         1578  +
         1579  +      zLhs = rbuMPrintf(p, "%z%s \"%w\" COLLATE %Q",
         1580  +          zLhs, zSep, zCol, zCollate
         1581  +      );
         1582  +      zOrder = rbuMPrintf(p, "%z%s \"rbu_imp_%d%w\" COLLATE %Q DESC",
         1583  +          zOrder, zSep, iCol, zCol, zCollate
         1584  +      );
         1585  +      zSelect = rbuMPrintf(p, "%z%s quote(\"rbu_imp_%d%w\")",
         1586  +          zSelect, zSep, iCol, zCol
         1587  +      );
         1588  +      zSep = ", ";
         1589  +      iCol++;
         1590  +    }
         1591  +    rbuFinalize(p, pXInfo);
         1592  +  }
         1593  +  if( bFailed ) goto index_start_out;
         1594  +
         1595  +  if( p->rc==SQLITE_OK ){
         1596  +    int iCol;
         1597  +    sqlite3_stmt *pSel = 0;
         1598  +
         1599  +    if( p->rc==SQLITE_OK ){
         1600  +      p->rc = prepareFreeAndCollectError(p->dbMain, &pSel, &p->zErrmsg,
         1601  +          sqlite3_mprintf("SELECT %s FROM \"rbu_imp_%w\" ORDER BY %s LIMIT 1",
         1602  +            zSelect, pIter->zTbl, zOrder
         1603  +          )
         1604  +      );
         1605  +    }
         1606  +    if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSel) ){
         1607  +      const char *zSep = "";
         1608  +      for(iCol=0; iCol<pIter->nCol; iCol++){
         1609  +        const char *zQuoted = (const char*)sqlite3_column_text(pSel, iCol);
         1610  +        if( zQuoted[0]=='N' ){
         1611  +          bFailed = 1;
         1612  +          break;
         1613  +        }
         1614  +        zVector = rbuMPrintf(p, "%z%s%s", zVector, zSep, zQuoted);
         1615  +        zSep = ", ";
         1616  +      }
         1617  +
         1618  +      if( !bFailed ){
         1619  +        zRet = rbuMPrintf(p, "(%s) > (%s)", zLhs, zVector);
         1620  +      }
         1621  +    }
         1622  +    rbuFinalize(p, pSel);
         1623  +  }
         1624  +
         1625  + index_start_out:
         1626  +  sqlite3_free(zOrder);
         1627  +  sqlite3_free(zSelect);
         1628  +  sqlite3_free(zVector);
         1629  +  sqlite3_free(zLhs);
         1630  +  return zRet;
         1631  +}
  1418   1632   
  1419   1633   /*
  1420   1634   ** This function is used to create a SELECT list (the list of SQL 
  1421   1635   ** expressions that follows a SELECT keyword) for a SELECT statement 
  1422   1636   ** used to read from an data_xxx or rbu_tmp_xxx table while updating the 
  1423   1637   ** index object currently indicated by the iterator object passed as the 
  1424   1638   ** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used 
................................................................................
  2088   2302           );
  2089   2303         }
  2090   2304   
  2091   2305         /* Create the SELECT statement to read keys in sorted order */
  2092   2306         if( p->rc==SQLITE_OK ){
  2093   2307           char *zSql;
  2094   2308           if( rbuIsVacuum(p) ){
         2309  +          char *zStart = 0;
         2310  +          if( nOffset ){
         2311  +            zStart = rbuVacuumIndexStart(p, pIter);
         2312  +            if( zStart ){
         2313  +              sqlite3_free(zLimit);
         2314  +              zLimit = 0;
         2315  +            }
         2316  +          }
         2317  +
  2095   2318             zSql = sqlite3_mprintf(
  2096         -              "SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s",
         2319  +              "SELECT %s, 0 AS rbu_control FROM '%q' %s %s %s ORDER BY %s%s",
  2097   2320                 zCollist, 
  2098   2321                 pIter->zDataTbl,
  2099         -              zPart, zCollist, zLimit
         2322  +              zPart, 
         2323  +              (zStart ? (zPart ? "AND" : "WHERE") : ""), zStart,
         2324  +              zCollist, zLimit
  2100   2325             );
         2326  +          sqlite3_free(zStart);
  2101   2327           }else
  2102   2328   
  2103   2329           if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
  2104   2330             zSql = sqlite3_mprintf(
  2105   2331                 "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s",
  2106   2332                 zCollist, p->zStateDb, pIter->zDataTbl,
  2107   2333                 zPart, zCollist, zLimit
................................................................................
  2116   2342                 zCollist, p->zStateDb, pIter->zDataTbl, zPart,
  2117   2343                 zCollist, pIter->zDataTbl, 
  2118   2344                 zPart,
  2119   2345                 (zPart ? "AND" : "WHERE"),
  2120   2346                 zCollist, zLimit
  2121   2347             );
  2122   2348           }
  2123         -        p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
         2349  +        if( p->rc==SQLITE_OK ){
         2350  +          p->rc = prepareFreeAndCollectError(p->dbRbu,&pIter->pSelect,pz,zSql);
         2351  +        }else{
         2352  +          sqlite3_free(zSql);
         2353  +        }
  2124   2354         }
  2125   2355   
  2126   2356         sqlite3_free(zImposterCols);
  2127   2357         sqlite3_free(zImposterPK);
  2128   2358         sqlite3_free(zWhere);
  2129   2359         sqlite3_free(zBind);
  2130   2360         sqlite3_free(zPart);
................................................................................
  2216   2446   
  2217   2447           rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid);
  2218   2448         }
  2219   2449   
  2220   2450         /* Create the SELECT statement to read keys from data_xxx */
  2221   2451         if( p->rc==SQLITE_OK ){
  2222   2452           const char *zRbuRowid = "";
         2453  +        char *zStart = 0;
         2454  +        char *zOrder = 0;
  2223   2455           if( bRbuRowid ){
  2224   2456             zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
  2225   2457           }
  2226         -        p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
  2227         -            sqlite3_mprintf(
  2228         -              "SELECT %s,%s rbu_control%s FROM '%q'%s", 
  2229         -              zCollist, 
  2230         -              (rbuIsVacuum(p) ? "0 AS " : ""),
  2231         -              zRbuRowid,
  2232         -              pIter->zDataTbl, zLimit
  2233         -            )
  2234         -        );
         2458  +
         2459  +        if( rbuIsVacuum(p) ){
         2460  +          if( nOffset ){
         2461  +            zStart = rbuVacuumTableStart(p, pIter, bRbuRowid, zWrite);
         2462  +            if( zStart ){
         2463  +              sqlite3_free(zLimit);
         2464  +              zLimit = 0;
         2465  +            }
         2466  +          }
         2467  +          if( bRbuRowid ){
         2468  +            zOrder = rbuMPrintf(p, "_rowid_");
         2469  +          }else{
         2470  +            zOrder = rbuObjIterGetPkList(p, pIter, "", ", ", "");
         2471  +          }
         2472  +        }
         2473  +
         2474  +        if( p->rc==SQLITE_OK ){
         2475  +          p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
         2476  +              sqlite3_mprintf(
         2477  +                "SELECT %s,%s rbu_control%s FROM '%q'%s %s %s %s",
         2478  +                zCollist, 
         2479  +                (rbuIsVacuum(p) ? "0 AS " : ""),
         2480  +                zRbuRowid,
         2481  +                pIter->zDataTbl, (zStart ? zStart : ""), 
         2482  +                (zOrder ? "ORDER BY" : ""), zOrder,
         2483  +                zLimit
         2484  +              )
         2485  +          );
         2486  +        }
         2487  +        sqlite3_free(zStart);
         2488  +        sqlite3_free(zOrder);
  2235   2489         }
  2236   2490   
  2237   2491         sqlite3_free(zWhere);
  2238   2492         sqlite3_free(zOldlist);
  2239   2493         sqlite3_free(zNewlist);
  2240   2494         sqlite3_free(zBindings);
  2241   2495       }