/ Check-in [92e7df0f]
Login

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

Overview
Comment:Add the sqlite3rbu_state() API. Used to determine the current state (creating OAL, ready to move OAL, incremental-checkpoint, finished or error) of an RBU operation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | rbu-state-api
Files: files | file ages | folders
SHA1: 92e7df0ff5c4c118c63d92a767dc82700438a310
User & Date: dan 2016-07-01 12:39:58
Context
2016-07-04
11:47
Add the sqlite3rbu_state() API. Used to determine the current state (creating OAL, ready to move OAL, incremental-checkpoint, finished or error) of an RBU operation. check-in: 0357875f user: dan tags: trunk
2016-07-01
12:39
Add the sqlite3rbu_state() API. Used to determine the current state (creating OAL, ready to move OAL, incremental-checkpoint, finished or error) of an RBU operation. Closed-Leaf check-in: 92e7df0f user: dan tags: rbu-state-api
2016-06-26
04:06
Prevent the WhereLoop.rSetup cost estimate from going negative on complex queries. check-in: f8105085 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/rbu/rbu1.test.

     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12     12   
    13         -if {![info exists testdir]} {
    14         -  set testdir [file join [file dirname [info script]] .. .. test]
    15         -}
    16         -source $testdir/tester.tcl
           13  +source [file join [file dirname [info script]] rbu_common.tcl]
    17     14   set ::testprefix rbu1
    18     15   
    19     16   db close
    20     17   sqlite3_shutdown
    21     18   sqlite3_config_uri 1
    22     19   
    23     20   # Create a simple RBU database. That expects to write to a table:
................................................................................
    92     89       INSERT INTO data_t1 VALUES(2, NULL, 10, 5, '..xx');    -- SET c=10, d = 5
    93     90       INSERT INTO data_t1 VALUES(3, 11, NULL, NULL, '.x..'); -- SET b=11
    94     91     }
    95     92     rbu5 close
    96     93     return $filename
    97     94   }
    98     95   
    99         -# Run the RBU in file $rbu on target database $target until completion.
   100         -#
   101         -proc run_rbu {target rbu} {
   102         -  sqlite3rbu rbu $target $rbu
   103         -  while 1 {
   104         -    set rc [rbu step]
   105         -    if {$rc!="SQLITE_OK"} break
   106         -  }
   107         -  rbu close
   108         -}
   109         -
   110         -proc step_rbu {target rbu} {
   111         -  while 1 {
   112         -    sqlite3rbu rbu $target $rbu
   113         -    set rc [rbu step]
   114         -    rbu close
   115         -    if {$rc != "SQLITE_OK"} break
   116         -  }
   117         -  set rc
   118         -}
   119     96   
   120     97   # Same as [step_rbu], except using a URI to open the target db.
   121     98   #
   122     99   proc step_rbu_uri {target rbu} {
   123    100     while 1 {
   124    101       sqlite3rbu rbu file:$target?xyz=&abc=123 $rbu
   125    102       set rc [rbu step]
................................................................................
   637    614       }
   638    615     }
   639    616   
   640    617     # Test that an RBU database containing no input tables is handled
   641    618     # correctly.
   642    619     reset_db
   643    620     forcedelete rbu.db
   644         -  do_test $tn3.8 {
          621  +  do_test $tn3.8.1 {
          622  +    list [catch { run_rbu test.db rbu.db } msg] $msg
          623  +  } {0 SQLITE_DONE}
          624  +
          625  +  # Test that an RBU database containing only empty data_xxx tables is
          626  +  # also handled correctly.
          627  +  reset_db
          628  +  forcedelete rbu.db
          629  +  do_execsql_test $tn3.8.2.1 {
          630  +    CREATE TABLE t1(a PRIMARY KEY, b);
          631  +    INSERT INTO t1 VALUES(1, 2);
          632  +    ATTACH 'rbu.db' AS rbu;
          633  +    CREATE TABLE data_t1(a, b, rbu_control);
          634  +    DETACH rbu;
          635  +  }
          636  +  do_test $tn3.8.2.1 {
   645    637       list [catch { run_rbu test.db rbu.db } msg] $msg
   646    638     } {0 SQLITE_DONE}
   647         -  
          639  +
   648    640     # Test that RBU can update indexes containing NULL values.
   649    641     #
   650    642     reset_db
   651    643     forcedelete rbu.db
   652    644     do_execsql_test $tn3.9.1 {
   653    645       CREATE TABLE t1(a PRIMARY KEY, b, c);
   654    646       CREATE INDEX i1 ON t1(b, c);

Changes to ext/rbu/rbu5.test.

     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12     12   # Test some properties of the pager_rbu_mode and rbu_mode pragmas.
    13     13   #
    14     14   
    15         -if {![info exists testdir]} {
    16         -  set testdir [file join [file dirname [info script]] .. .. test]
    17         -}
    18         -source $testdir/tester.tcl
           15  +source [file join [file dirname [info script]] rbu_common.tcl]
    19     16   set ::testprefix rbu5
    20     17   
    21     18   
    22         -# Run the RBU in file $rbu on target database $target until completion.
    23         -#
    24         -proc run_rbu {target rbu} {
    25         -  sqlite3rbu rbu $target $rbu
    26         -  while { [rbu step]=="SQLITE_OK" } {}
    27         -  rbu close
    28         -}
    29         -
    30         -
    31         -# Run the RBU in file $rbu on target database $target one step at a
    32         -# time until completion.
    33         -#
    34         -proc step_rbu {target rbu} {
    35         -  while 1 {
    36         -    sqlite3rbu rbu $target $rbu
    37         -    set rc [rbu step]
    38         -    rbu close
    39         -    if {$rc != "SQLITE_OK"} break
    40         -  }
    41         -  set rc
    42         -}
    43         -
    44     19   # Return a list of the primary key columns for table $tbl in the database
    45     20   # opened by database handle $db.
    46     21   #
    47     22   proc pkcols {db tbl} {
    48     23     set ret [list]
    49     24     $db eval "PRAGMA table_info = '$tbl'" {
    50     25       if {$pk} { lappend ret $name }

Changes to ext/rbu/rbu_common.tcl.

    10     10   #***********************************************************************
    11     11   #
    12     12   
    13     13   if {![info exists testdir]} {
    14     14     set testdir [file join [file dirname [info script]] .. .. test]
    15     15   }
    16     16   source $testdir/tester.tcl
           17  +
           18  +proc check_prestep_state {target state} {
           19  +  set oal_exists [file exists $target-oal]
           20  +  set wal_exists [file exists $target-wal]
           21  +  set progress [rbu progress]
           22  +
           23  +  if {($progress==0 && $state!="oal" && $state!="done")
           24  +   || ($oal_exists && $wal_exists)
           25  +   || ($progress>0 && $state=="oal" && (!$oal_exists || $wal_exists))
           26  +   || ($state=="move" && (!$oal_exists || $wal_exists))
           27  +   || ($state=="checkpoint" && ($oal_exists || !$wal_exists))
           28  +   || ($state=="done" && ($oal_exists && $progress!=0))
           29  +  } {
           30  +    error "B: state=$state progress=$progress oal=$oal_exists wal=$wal_exists"
           31  +  }
           32  +}
           33  +
           34  +proc check_poststep_state {rc target state} {
           35  +  if {$rc=="SQLITE_OK" || $rc=="SQLITE_DONE"} {
           36  +    set oal_exists [file exists $target-oal]
           37  +    set wal_exists [file exists $target-wal]
           38  +    if {$state=="move" && ($oal_exists || !$wal_exists)} {
           39  +      error "A: state=$state progress=$progress oal=$oal_exists wal=$wal_exists"
           40  +    }
           41  +  }
           42  +}
    17     43   
    18     44   # Run the RBU in file $rbu on target database $target until completion.
    19     45   #
    20     46   proc run_rbu {target rbu} {
    21     47     sqlite3rbu rbu $target $rbu
    22     48     while 1 {
           49  +    set state [rbu state]
           50  +
           51  +    check_prestep_state $target $state
    23     52       set rc [rbu step]
           53  +    check_poststep_state $rc $target $state
           54  +
    24     55       if {$rc!="SQLITE_OK"} break
    25     56     }
    26     57     rbu close
    27     58   }
    28     59   
    29     60   proc step_rbu {target rbu} {
    30     61     while 1 {
    31     62       sqlite3rbu rbu $target $rbu
           63  +    set state [rbu state]
           64  +    check_prestep_state $target $state
    32     65       set rc [rbu step]
           66  +    check_poststep_state $rc $target $state
    33     67       rbu close
    34     68       if {$rc != "SQLITE_OK"} break
    35     69     }
    36     70     set rc
    37     71   }
    38     72   
    39     73   proc do_rbu_vacuum_test {tn step} {
    40     74     uplevel [list do_test $tn.1 {
    41     75       if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db }
    42     76       while 1 {
    43     77         if {$step==1} { sqlite3rbu_vacuum rbu test.db state.db }
           78  +      set state [rbu state]
           79  +      check_prestep_state test.db $state
    44     80         set rc [rbu step]
           81  +      check_poststep_state $rc test.db $state
    45     82         if {$rc!="SQLITE_OK"} break
    46     83         if {$step==1} { rbu close }
    47     84       }
    48     85       rbu close
    49     86     } {SQLITE_DONE}]
    50     87   
    51     88     uplevel [list do_execsql_test $tn.2 {
    52     89       PRAGMA integrity_check
    53     90     } ok]
    54     91   }
    55     92   

Changes to ext/rbu/sqlite3rbu.c.

  3551   3551           );
  3552   3552         }
  3553   3553       }
  3554   3554   
  3555   3555       if( p->rc==SQLITE_OK ){
  3556   3556         if( p->eStage==RBU_STAGE_OAL ){
  3557   3557           sqlite3 *db = p->dbMain;
  3558         -
  3559         -        if( pState->eStage==0 && rbuIsVacuum(p) ){
  3560         -          rbuCopyPragma(p, "page_size");
  3561         -          rbuCopyPragma(p, "auto_vacuum");
  3562         -        }
  3563         -
  3564         -        /* Open transactions both databases. The *-oal file is opened or
  3565         -        ** created at this point. */
  3566         -        if( p->rc==SQLITE_OK ){
  3567         -          p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
  3568         -        }
  3569         -        if( p->rc==SQLITE_OK ){
  3570         -          p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
  3571         -        }
  3572         -
  3573         -        /* Check if the main database is a zipvfs db. If it is, set the upper
  3574         -        ** level pager to use "journal_mode=off". This prevents it from 
  3575         -        ** generating a large journal using a temp file.  */
  3576         -        if( p->rc==SQLITE_OK ){
  3577         -          int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
  3578         -          if( frc==SQLITE_OK ){
  3579         -            p->rc = sqlite3_exec(db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
  3580         -          }
  3581         -        }
         3558  +        p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
  3582   3559   
  3583   3560           /* Point the object iterator at the first object */
  3584   3561           if( p->rc==SQLITE_OK ){
  3585   3562             p->rc = rbuObjIterFirst(p, &p->objiter);
  3586   3563           }
  3587   3564   
  3588   3565           /* If the RBU database contains no data_xxx tables, declare the RBU
  3589   3566           ** update finished.  */
  3590   3567           if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){
  3591   3568             p->rc = SQLITE_DONE;
         3569  +          p->eStage = RBU_STAGE_DONE;
         3570  +        }else{
         3571  +          if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
         3572  +            rbuCopyPragma(p, "page_size");
         3573  +            rbuCopyPragma(p, "auto_vacuum");
         3574  +          }
         3575  +
         3576  +          /* Open transactions both databases. The *-oal file is opened or
         3577  +          ** created at this point. */
         3578  +          if( p->rc==SQLITE_OK ){
         3579  +            p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
         3580  +          }
         3581  +
         3582  +          /* Check if the main database is a zipvfs db. If it is, set the upper
         3583  +          ** level pager to use "journal_mode=off". This prevents it from 
         3584  +          ** generating a large journal using a temp file.  */
         3585  +          if( p->rc==SQLITE_OK ){
         3586  +            int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
         3587  +            if( frc==SQLITE_OK ){
         3588  +              p->rc = sqlite3_exec(
         3589  +                db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
         3590  +            }
         3591  +          }
         3592  +
         3593  +          if( p->rc==SQLITE_OK ){
         3594  +            rbuSetupOal(p, pState);
         3595  +          }
  3592   3596           }
  3593         -
  3594         -        if( p->rc==SQLITE_OK ){
  3595         -          rbuSetupOal(p, pState);
  3596         -        }
  3597         -
  3598   3597         }else if( p->eStage==RBU_STAGE_MOVE ){
  3599   3598           /* no-op */
  3600   3599         }else if( p->eStage==RBU_STAGE_CKPT ){
  3601   3600           rbuSetupCheckpoint(p, pState);
  3602   3601         }else if( p->eStage==RBU_STAGE_DONE ){
  3603   3602           p->rc = SQLITE_DONE;
  3604   3603         }else{
................................................................................
  3756   3755         *pnTwo = MAX_PROGRESS;
  3757   3756         break;
  3758   3757   
  3759   3758       default:
  3760   3759         assert( 0 );
  3761   3760     }
  3762   3761   }
         3762  +
         3763  +/*
         3764  +** Return the current state of the RBU vacuum or update operation.
         3765  +*/
         3766  +int sqlite3rbu_state(sqlite3rbu *p){
         3767  +  int aRes[] = {
         3768  +    0, SQLITE_RBU_STATE_OAL, SQLITE_RBU_STATE_MOVE,
         3769  +    0, SQLITE_RBU_STATE_CHECKPOINT, SQLITE_RBU_STATE_DONE
         3770  +  };
         3771  +
         3772  +  assert( RBU_STAGE_OAL==1 );
         3773  +  assert( RBU_STAGE_MOVE==2 );
         3774  +  assert( RBU_STAGE_CKPT==4 );
         3775  +  assert( RBU_STAGE_DONE==5 );
         3776  +  assert( aRes[RBU_STAGE_OAL]==SQLITE_RBU_STATE_OAL );
         3777  +  assert( aRes[RBU_STAGE_MOVE]==SQLITE_RBU_STATE_MOVE );
         3778  +  assert( aRes[RBU_STAGE_CKPT]==SQLITE_RBU_STATE_CHECKPOINT );
         3779  +  assert( aRes[RBU_STAGE_DONE]==SQLITE_RBU_STATE_DONE );
         3780  +
         3781  +  if( p->rc!=SQLITE_OK && p->rc!=SQLITE_DONE ){
         3782  +    return SQLITE_RBU_STATE_ERROR;
         3783  +  }else{
         3784  +    assert( p->rc!=SQLITE_DONE || p->eStage==RBU_STAGE_DONE );
         3785  +    assert( p->eStage==RBU_STAGE_OAL
         3786  +         || p->eStage==RBU_STAGE_MOVE
         3787  +         || p->eStage==RBU_STAGE_CKPT
         3788  +         || p->eStage==RBU_STAGE_DONE
         3789  +    );
         3790  +    return aRes[p->eStage];
         3791  +  }
         3792  +}
  3763   3793   
  3764   3794   int sqlite3rbu_savestate(sqlite3rbu *p){
  3765   3795     int rc = p->rc;
  3766         -  
  3767   3796     if( rc==SQLITE_DONE ) return SQLITE_OK;
  3768   3797   
  3769   3798     assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE );
  3770   3799     if( p->eStage==RBU_STAGE_OAL ){
  3771   3800       assert( rc!=SQLITE_DONE );
  3772   3801       if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0);
  3773   3802     }

Changes to ext/rbu/sqlite3rbu.h.

   470    470   ** permyriadage progress of the same stage. If the rbu_count table does
   471    471   ** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count
   472    472   ** table exists but is not correctly populated, the value of the *pnOne
   473    473   ** output variable during stage 1 is undefined.
   474    474   */
   475    475   void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
   476    476   
          477  +/*
          478  +** Obtain an indication as to the current stage of an RBU update or vacuum.
          479  +** This function always returns one of the SQLITE_RBU_STATE_XXX constants
          480  +** defined in this file. Return values should be interpreted as follows:
          481  +**
          482  +** SQLITE_RBU_STATE_OAL:
          483  +**   RBU is currently building a *-oal file. The next call to sqlite3rbu_step()
          484  +**   may either add further data to the *-oal file, or compute data that will
          485  +**   be added by a subsequent call.
          486  +**
          487  +** SQLITE_RBU_STATE_MOVE:
          488  +**   RBU has finished building the *-oal file. The next call to sqlite3rbu_step()
          489  +**   will move the *-oal file to the equivalent *-wal path. If the current
          490  +**   operation is an RBU update, then the updated version of the database
          491  +**   file will become visible to ordinary SQLite clients following the next
          492  +**   call to sqlite3rbu_step().
          493  +**
          494  +** SQLITE_RBU_STATE_CHECKPOINT:
          495  +**   RBU is currently performing an incremental checkpoint. The next call to
          496  +**   sqlite3rbu_step() will copy a page of data from the *-wal file into
          497  +**   the target database file.
          498  +**
          499  +** SQLITE_RBU_STATE_DONE:
          500  +**   The RBU operation has finished. Any subsequent calls to sqlite3rbu_step()
          501  +**   will immediately return SQLITE_DONE.
          502  +**
          503  +** SQLITE_RBU_STATE_ERROR:
          504  +**   An error has occurred. Any subsequent calls to sqlite3rbu_step() will
          505  +**   immediately return the SQLite error code associated with the error.
          506  +*/
          507  +#define SQLITE_RBU_STATE_OAL        1
          508  +#define SQLITE_RBU_STATE_MOVE       2
          509  +#define SQLITE_RBU_STATE_CHECKPOINT 3
          510  +#define SQLITE_RBU_STATE_DONE       4
          511  +#define SQLITE_RBU_STATE_ERROR      5
          512  +
          513  +int sqlite3rbu_state(sqlite3rbu *pRbu);
          514  +
   477    515   /*
   478    516   ** Create an RBU VFS named zName that accesses the underlying file-system
   479    517   ** via existing VFS zParent. Or, if the zParent parameter is passed NULL, 
   480    518   ** then the new RBU VFS uses the default system VFS to access the file-system.
   481    519   ** The new object is registered as a non-default VFS with SQLite before 
   482    520   ** returning.
   483    521   **

Changes to ext/rbu/test_rbu.c.

    65     65       {"step", 2, ""},              /* 0 */
    66     66       {"close", 2, ""},             /* 1 */
    67     67       {"create_rbu_delta", 2, ""},  /* 2 */
    68     68       {"savestate", 2, ""},         /* 3 */
    69     69       {"dbMain_eval", 3, "SQL"},    /* 4 */
    70     70       {"bp_progress", 2, ""},       /* 5 */
    71     71       {"db", 3, "RBU"},             /* 6 */
           72  +    {"state", 2, ""},             /* 7 */
           73  +    {"progress", 2, ""},          /* 8 */
    72     74       {0,0,0}
    73     75     };
    74     76     int iCmd;
    75     77   
    76     78     if( objc<2 ){
    77     79       Tcl_WrongNumArgs(interp, 1, objv, "METHOD");
    78     80       return TCL_ERROR;
................................................................................
   161    163           if( sqlite3TestMakePointerStr(interp, zBuf, (void*)db) ){
   162    164             ret = TCL_ERROR;
   163    165           }else{
   164    166             Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
   165    167           }
   166    168         }
   167    169         break;
          170  +    }
          171  +    case 7: /* state */ {
          172  +      const char *aRes[] = { 0, "oal", "move", "checkpoint", "done", "error" };
          173  +      int eState = sqlite3rbu_state(pRbu);
          174  +      assert( eState>0 && eState<=5 );
          175  +      Tcl_SetResult(interp, (char*)aRes[eState], TCL_STATIC);
          176  +      break;
          177  +    }
          178  +    case 8: /* progress */ {
          179  +      sqlite3_int64 nStep =  sqlite3rbu_progress(pRbu);
          180  +      Tcl_SetObjResult(interp, Tcl_NewWideIntObj(nStep));
          181  +      break;
   168    182       }
   169    183   
   170    184       default: /* seems unlikely */
   171    185         assert( !"cannot happen" );
   172    186         break;
   173    187     }
   174    188