SQLite

Check-in [c878d74173]
Login

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

Overview
Comment:Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c878d741733a897d47a1a0b57b454bc9095ce302b48cc01089336d5d20e2a24c
User & Date: dan 2018-12-10 08:41:32.074
Context
2018-12-12
11:54
Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum. (check-in: 6b7567fc0c user: drh tags: branch-3.26)
2018-12-10
18:15
Refactor the sqlite3_normalized_sql() implementation. (check-in: 06e955e5d2 user: drh tags: trunk)
08:41
Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum. (check-in: c878d74173 user: dan tags: trunk)
01:48
Add support for the VACUUM INTO command. (check-in: 77f150b8b4 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/rbu/rbu_common.tcl.
82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    sqlite3 tmpdb $rbu
    tmpdb eval { DELETE FROM rbu_state WHERE k==10 }
    tmpdb close
  }
  set rc
}

proc do_rbu_vacuum_test {tn step} {
  forcedelete state.db

  uplevel [list do_test $tn.1 {
    if {$step==0} { sqlite3rbu_vacuum rbu test.db state.db }
    while 1 {
      if {$step==1} { sqlite3rbu_vacuum rbu test.db state.db }
      set state [rbu state]
      check_prestep_state test.db $state
      set rc [rbu step]
      check_poststep_state $rc test.db $state
      if {$rc!="SQLITE_OK"} break
      if {$step==1} { rbu close }
    }
    rbu close
  } {SQLITE_DONE}]

  uplevel [list do_execsql_test $tn.2 {
    PRAGMA integrity_check
  } ok]
}








|
|
>
|
|

|








|






82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    sqlite3 tmpdb $rbu
    tmpdb eval { DELETE FROM rbu_state WHERE k==10 }
    tmpdb close
  }
  set rc
}

proc do_rbu_vacuum_test {tn step {statedb state.db}} {
  forcedelete $statedb
  if {$statedb=="" && $step==1} breakpoint
  uplevel [list do_test $tn.1 [string map [list %state% $statedb] {
    if {$step==0} { sqlite3rbu_vacuum rbu test.db {%state%}}
    while 1 {
      if {$step==1} { sqlite3rbu_vacuum rbu test.db {%state%}}
      set state [rbu state]
      check_prestep_state test.db $state
      set rc [rbu step]
      check_poststep_state $rc test.db $state
      if {$rc!="SQLITE_OK"} break
      if {$step==1} { rbu close }
    }
    rbu close
  }] {SQLITE_DONE}]

  uplevel [list do_execsql_test $tn.2 {
    PRAGMA integrity_check
  } ok]
}

Changes to ext/rbu/rbuvacuum2.test.
12
13
14
15
16
17
18

19


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# This file contains tests for the RBU module. More specifically, it
# contains tests to ensure that the sqlite3rbu_vacuum() API works as
# expected.
#

source [file join [file dirname [info script]] rbu_common.tcl]


foreach step {0 1} {


  set ::testprefix rbuvacuum2-$step
  
  #-------------------------------------------------------------------------
  # Test that a database that contains fts3 tables can be vacuumed.
  #
  ifcapable fts3 {
    reset_db
    do_execsql_test 1.1 {
      CREATE VIRTUAL TABLE t1 USING fts3(z, y);
      INSERT INTO t1 VALUES('fix this issue', 'at some point');
    }
  
    do_rbu_vacuum_test 1.2 $step
  
    do_execsql_test 1.3 {
      SELECT * FROM t1;
    } {{fix this issue} {at some point}}
  
    do_execsql_test 1.4 {
      SELECT rowid FROM t1 WHERE t1 MATCH 'fix';
    } {1}

    do_execsql_test 1.5 {
      INSERT INTO t1 VALUES('a b c', 'd e f');
      INSERT INTO t1 VALUES('l h i', 'd e f');
      DELETE FROM t1 WHERE docid = 2;
      INSERT INTO t1 VALUES('a b c', 'x y z');
    }

    do_rbu_vacuum_test 1.6 $step
    do_execsql_test 1.7 {
      INSERT INTO t1(t1) VALUES('integrity-check');
      SELECT * FROM t1;
    } {
      {fix this issue} {at some point}
      {l h i} {d e f}
      {a b c} {x y z}
    }
  }
  
  #-------------------------------------------------------------------------
  # Test that a database that contains fts5 tables can be vacuumed.
  #
  ifcapable fts5 {
    reset_db
    do_execsql_test 2.1 {
      CREATE VIRTUAL TABLE t1 USING fts5(z, y);
      INSERT INTO t1 VALUES('fix this issue', 'at some point');
    }
  
    do_rbu_vacuum_test 2.2 $step
  
    do_execsql_test 2.3 {
      SELECT * FROM t1;
    } {{fix this issue} {at some point}}
  
    do_execsql_test 2.4 {
      SELECT rowid FROM t1 ('fix');
    } {1}

    do_execsql_test 2.5 {
      INSERT INTO t1 VALUES('a b c', 'd e f');
      INSERT INTO t1 VALUES('l h i', 'd e f');
      DELETE FROM t1 WHERE rowid = 2;
      INSERT INTO t1 VALUES('a b c', 'x y z');
    }

    do_rbu_vacuum_test 2.6 $step
    do_execsql_test 2.7 {
      INSERT INTO t1(t1) VALUES('integrity-check');
      SELECT * FROM t1;
    } {
      {fix this issue} {at some point}
      {l h i} {d e f}
      {a b c} {x y z}







>
|
>
>
|











|
















|




















|
















|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# This file contains tests for the RBU module. More specifically, it
# contains tests to ensure that the sqlite3rbu_vacuum() API works as
# expected.
#

source [file join [file dirname [info script]] rbu_common.tcl]

foreach {step} {0 1} {
foreach {ttt state} {
  s state.db t test.db-vacuum n {}
} {
  set ::testprefix rbuvacuum2-$step$ttt
  
  #-------------------------------------------------------------------------
  # Test that a database that contains fts3 tables can be vacuumed.
  #
  ifcapable fts3 {
    reset_db
    do_execsql_test 1.1 {
      CREATE VIRTUAL TABLE t1 USING fts3(z, y);
      INSERT INTO t1 VALUES('fix this issue', 'at some point');
    }
  
    do_rbu_vacuum_test 1.2 $step $state
  
    do_execsql_test 1.3 {
      SELECT * FROM t1;
    } {{fix this issue} {at some point}}
  
    do_execsql_test 1.4 {
      SELECT rowid FROM t1 WHERE t1 MATCH 'fix';
    } {1}

    do_execsql_test 1.5 {
      INSERT INTO t1 VALUES('a b c', 'd e f');
      INSERT INTO t1 VALUES('l h i', 'd e f');
      DELETE FROM t1 WHERE docid = 2;
      INSERT INTO t1 VALUES('a b c', 'x y z');
    }

    do_rbu_vacuum_test 1.6 $step $state
    do_execsql_test 1.7 {
      INSERT INTO t1(t1) VALUES('integrity-check');
      SELECT * FROM t1;
    } {
      {fix this issue} {at some point}
      {l h i} {d e f}
      {a b c} {x y z}
    }
  }
  
  #-------------------------------------------------------------------------
  # Test that a database that contains fts5 tables can be vacuumed.
  #
  ifcapable fts5 {
    reset_db
    do_execsql_test 2.1 {
      CREATE VIRTUAL TABLE t1 USING fts5(z, y);
      INSERT INTO t1 VALUES('fix this issue', 'at some point');
    }
  
    do_rbu_vacuum_test 2.2 $step $state
  
    do_execsql_test 2.3 {
      SELECT * FROM t1;
    } {{fix this issue} {at some point}}
  
    do_execsql_test 2.4 {
      SELECT rowid FROM t1 ('fix');
    } {1}

    do_execsql_test 2.5 {
      INSERT INTO t1 VALUES('a b c', 'd e f');
      INSERT INTO t1 VALUES('l h i', 'd e f');
      DELETE FROM t1 WHERE rowid = 2;
      INSERT INTO t1 VALUES('a b c', 'x y z');
    }

    do_rbu_vacuum_test 2.6 $step $state
    do_execsql_test 2.7 {
      INSERT INTO t1(t1) VALUES('integrity-check');
      SELECT * FROM t1;
    } {
      {fix this issue} {at some point}
      {l h i} {d e f}
      {a b c} {x y z}
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    do_execsql_test 3.1 {
      CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
      INSERT INTO rt VALUES(1, 45, 55);
      INSERT INTO rt VALUES(2, 50, 60);
      INSERT INTO rt VALUES(3, 55, 65);
    }
  
    do_rbu_vacuum_test 3.2 $step
  
    do_execsql_test 3.3 {
      SELECT * FROM rt;
    } {1 45.0 55.0 2 50.0 60.0 3 55.0 65.0}
  
    do_execsql_test 3.4.1 {
      SELECT rowid FROM rt WHERE x2>51 AND x1 < 51
    } {1 2}
    do_execsql_test 3.4.2 {
      SELECT rowid FROM rt WHERE x2>59 AND x1 < 59
    } {2 3}

    do_rbu_vacuum_test 3.5 $step

    do_execsql_test 3.6.1 {
      SELECT rowid FROM rt WHERE x2>51 AND x1 < 51
    } {1 2}
    do_execsql_test 3.6.2 {
      SELECT rowid FROM rt WHERE x2>59 AND x1 < 59
    } {2 3}







|












|







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    do_execsql_test 3.1 {
      CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
      INSERT INTO rt VALUES(1, 45, 55);
      INSERT INTO rt VALUES(2, 50, 60);
      INSERT INTO rt VALUES(3, 55, 65);
    }
  
    do_rbu_vacuum_test 3.2 $step $state
  
    do_execsql_test 3.3 {
      SELECT * FROM rt;
    } {1 45.0 55.0 2 50.0 60.0 3 55.0 65.0}
  
    do_execsql_test 3.4.1 {
      SELECT rowid FROM rt WHERE x2>51 AND x1 < 51
    } {1 2}
    do_execsql_test 3.4.2 {
      SELECT rowid FROM rt WHERE x2>59 AND x1 < 59
    } {2 3}

    do_rbu_vacuum_test 3.5 $step $state

    do_execsql_test 3.6.1 {
      SELECT rowid FROM rt WHERE x2>51 AND x1 < 51
    } {1 2}
    do_execsql_test 3.6.2 {
      SELECT rowid FROM rt WHERE x2>59 AND x1 < 59
    } {2 3}
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }

    do_rbu_vacuum_test 4.3 $step
    do_execsql_test 4.4 {
      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }
  }

}
  
#-------------------------------------------------------------------------
# Test that passing a NULL value as the second argument to 
# sqlite3rbu_vacuum() causes it to:
#
#   * Use <database>-vacuum as the state db, and







|








>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }

    do_rbu_vacuum_test 4.3 $step $state
    do_execsql_test 4.4 {
      SELECT * FROM sqlite_master;
    } {
    table t1 t1 2 {CREATE TABLE t1(a, b, c)}
    view v1 v1 0 {CREATE VIEW v1 AS SELECT * FROM t1}
    trigger tr1 t1 0 {CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END}
    }
  }
}
}
  
#-------------------------------------------------------------------------
# Test that passing a NULL value as the second argument to 
# sqlite3rbu_vacuum() causes it to:
#
#   * Use <database>-vacuum as the state db, and
227
228
229
230
231
232
233





234
do_test 6.3 {
  sqlite3rbu_vacuum rbu test.db test.db2
  while {[rbu step]!="SQLITE_DONE"} { rbu step }
  rbu close
  execsql { PRAGMA integrity_check }
} {ok}






finish_test







>
>
>
>
>

231
232
233
234
235
236
237
238
239
240
241
242
243
do_test 6.3 {
  sqlite3rbu_vacuum rbu test.db test.db2
  while {[rbu step]!="SQLITE_DONE"} { rbu step }
  rbu close
  execsql { PRAGMA integrity_check }
} {ok}

do_test 6.4 {
  sqlite3rbu_vacuum rbu test.db test.db-vactmp
  list [catch { rbu close } msg] $msg
} {1 SQLITE_MISUSE}

finish_test
Changes to ext/rbu/sqlite3rbu.c.
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
        zExtra = &p->zRbu[5];
        while( *zExtra ){
          if( *zExtra++=='?' ) break;
        }
        if( *zExtra=='\0' ) zExtra = 0;
      }

      zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s", 
          sqlite3_db_filename(p->dbRbu, "main"),
          (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
      );

      if( zTarget==0 ){
        p->rc = SQLITE_NOMEM;
        return;







|







2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
        zExtra = &p->zRbu[5];
        while( *zExtra ){
          if( *zExtra++=='?' ) break;
        }
        if( *zExtra=='\0' ) zExtra = 0;
      }

      zTarget = sqlite3_mprintf("file:%s-vactmp?rbu_memory=1%s%s", 
          sqlite3_db_filename(p->dbRbu, "main"),
          (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
      );

      if( zTarget==0 ){
        p->rc = SQLITE_NOMEM;
        return;
3739
3740
3741
3742
3743
3744
3745






3746
3747
3748
3749
3750
3751
3752
** Open a handle to begin or resume an RBU VACUUM operation.
*/
sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget, 
  const char *zState
){
  if( zTarget==0 ){ return rbuMisuseError(); }






  /* TODO: Check that both arguments are non-NULL */
  return openRbuHandle(0, zTarget, zState);
}

/*
** Return the database handle used by pRbu.
*/







>
>
>
>
>
>







3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
** Open a handle to begin or resume an RBU VACUUM operation.
*/
sqlite3rbu *sqlite3rbu_vacuum(
  const char *zTarget, 
  const char *zState
){
  if( zTarget==0 ){ return rbuMisuseError(); }
  if( zState ){
    int n = strlen(zState);
    if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){
      return rbuMisuseError();
    }
  }
  /* TODO: Check that both arguments are non-NULL */
  return openRbuHandle(0, zTarget, zState);
}

/*
** Return the database handle used by pRbu.
*/
Changes to ext/rbu/sqlite3rbu.h.
329
330
331
332
333
334
335
336




337
338
339
340
341
342
343
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.
**
** If the second argument passed to this function is NULL, then the
** name of the state database is "<database>-vacuum", where <database>
** is the name of the target database file. In this case, on UNIX, if the
** state database is not already present in the file-system, it is created
** with the same permissions as the target db is made.




**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a 
** new RBU vacuum operation.







|
>
>
>
>







329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
** The vacuum can be resumed by calling this function to open a new RBU
** handle specifying the same target and state databases.
**
** If the second argument passed to this function is NULL, then the
** name of the state database is "<database>-vacuum", where <database>
** is the name of the target database file. In this case, on UNIX, if the
** state database is not already present in the file-system, it is created
** with the same permissions as the target db is made. 
**
** With an RBU vacuum, it is an SQLITE_MISUSE error if the name of the 
** state database ends with "-vactmp". This name is reserved for internal 
** use.
**
** This function does not delete the state database after an RBU vacuum
** is completed, even if it created it. However, if the call to
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
** of the state tables within the state database are zeroed. This way,
** the next call to sqlite3rbu_vacuum() opens a handle that starts a 
** new RBU vacuum operation.
Changes to ext/rbu/test_rbu.c.
269
270
271
272
273
274
275

276
277
278
279
280
281
282
  if( objc!=3 && objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetString(objv[1]);
  zTarget = Tcl_GetString(objv[2]);
  if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);


  pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
  Tcl_SetObjResult(interp, objv[1]);
  return TCL_OK;
}








>







269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
  if( objc!=3 && objc!=4 ){
    Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB ?STATE-DB?");
    return TCL_ERROR;
  }
  zCmd = Tcl_GetString(objv[1]);
  zTarget = Tcl_GetString(objv[2]);
  if( objc==4 ) zStateDb = Tcl_GetString(objv[3]);
  if( zStateDb && zStateDb[0]=='\0' ) zStateDb = 0;

  pRbu = sqlite3rbu_vacuum(zTarget, zStateDb);
  Tcl_CreateObjCommand(interp, zCmd, test_sqlite3rbu_cmd, (ClientData)pRbu, 0);
  Tcl_SetObjResult(interp, objv[1]);
  return TCL_OK;
}