/ Check-in [6b7567fc]
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 | SQL archive
Timelines: family | ancestors | descendants | both | branch-3.26
Files: files | file ages | folders
SHA3-256: 6b7567fc0c7dc143f7622733f9addbf36b931cf405e71681db5b5f1ae7905c92
User & Date: drh 2018-12-12 11:54:10
Context
2018-12-12
11:57
OSSFuzz found a case where an assert() inside sqlite3ExprCompare() can be true. Test case added to TH3. check-in: bc891ac6 user: drh tags: branch-3.26
11:54
Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum. check-in: 6b7567fc user: drh tags: branch-3.26
11:50
Do not use a partial index as a table scan in an IN operator. Fix for ticket [1d958d90596593a77420e59]. check-in: c1c735a8 user: drh tags: branch-3.26
2018-12-10
08:41
Fix a problem with using "<db>-vacuum" (the default) as the state database when resuming an RBU vacuum. check-in: c878d741 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs 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
..
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
..
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
...
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
...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
...
227
228
229
230
231
232
233





234
# 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';
................................................................................
    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}
................................................................................
  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');
................................................................................
    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}
................................................................................
    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}
................................................................................
      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
................................................................................
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







>
|
>
>
|











|







 







|







 







|







 







|







 







|












|







 







|








>







 







>
>
>
>
>

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
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
...
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
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
231
232
233
234
235
236
237
238
239
240
241
242
243
# 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';
................................................................................
    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}
................................................................................
  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');
................................................................................
    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}
................................................................................
    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}
................................................................................
      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
................................................................................
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
....
3739
3740
3741
3742
3743
3744
3745






3746
3747
3748
3749
3750
3751
3752
        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;
................................................................................
** 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.
*/







|







 







>
>
>
>
>
>







2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
....
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
        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;
................................................................................
** 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;
}