Snapshot of upstream SQLite 3.25.0
[sqlcipher.git] / ext / rbu / rbu1.test
blob1d16c1cd0a8f131675ed3465bd62aae6e0034634
1 # 2014 August 30
3 # The author disclaims copyright to this source code.  In place of
4 # a legal notice, here is a blessing:
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.
10 #***********************************************************************
13 source [file join [file dirname [info script]] rbu_common.tcl]
14 set ::testprefix rbu1
16 db close
17 sqlite3_shutdown
18 sqlite3_config_uri 1
20 # Create a simple RBU database. That expects to write to a table:
22 #   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
24 proc create_rbu1 {filename} {
25   forcedelete $filename
26   sqlite3 rbu1 $filename  
27   rbu1 eval {
28     CREATE TABLE data_t1(a, b, c, rbu_control);
29     INSERT INTO data_t1 VALUES(1, 2, 3, 0);
30     INSERT INTO data_t1 VALUES(2, 'two', 'three', 0);
31     INSERT INTO data_t1 VALUES(3, NULL, 8.2, 0);
32   }
33   rbu1 close
34   return $filename
37 # Create a simple RBU database. That expects to write to a table:
39 #   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
41 # This RBU includes both insert and delete operations.
43 proc create_rbu4 {filename} {
44   forcedelete $filename
45   sqlite3 rbu1 $filename  
46   rbu1 eval {
47     CREATE TABLE data_t1(a, b, c, rbu_control);
48     INSERT INTO data_t1 VALUES(1, 2, 3, 0);
49     INSERT INTO data_t1 VALUES(2, NULL, 5, 1);
50     INSERT INTO data_t1 VALUES(3, 8, 9, 0);
51     INSERT INTO data_t1 VALUES(4, NULL, 11, 1);
52   }
53   rbu1 close
54   return $filename
57 # Create a simple RBU database. That expects to write to a table:
59 #   CREATE TABLE t1(c, b, '(a)' INTEGER PRIMARY KEY);
61 # This RBU includes both insert and delete operations.
63 proc create_rbu4b {filename} {
64   forcedelete $filename
65   sqlite3 rbu1 $filename  
66   rbu1 eval {
67     CREATE TABLE data_t1(c, b, '(a)', rbu_control);
68     INSERT INTO data_t1 VALUES(3, 2, 1, 0);
69     INSERT INTO data_t1 VALUES(5, NULL, 2, 1);
70     INSERT INTO data_t1 VALUES(9, 8, 3, 0);
71     INSERT INTO data_t1 VALUES(11, NULL, 4, 1);
72   }
73   rbu1 close
74   return $filename
77 # Create a simple RBU database. That expects to write to a table:
79 #   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
81 # This RBU includes update statements.
83 proc create_rbu5 {filename} {
84   forcedelete $filename
85   sqlite3 rbu5 $filename  
86   rbu5 eval {
87     CREATE TABLE data_t1(a, b, c, d, rbu_control);
88     INSERT INTO data_t1 VALUES(1, NULL, NULL, 5, '...x');  -- SET d = 5
89     INSERT INTO data_t1 VALUES(2, NULL, 10, 5, '..xx');    -- SET c=10, d = 5
90     INSERT INTO data_t1 VALUES(3, 11, NULL, NULL, '.x..'); -- SET b=11
91   }
92   rbu5 close
93   return $filename
97 # Same as [step_rbu], except using a URI to open the target db.
99 proc step_rbu_uri {target rbu} {
100   while 1 {
101     sqlite3rbu rbu file:$target?xyz=&abc=123 $rbu
102     set rc [rbu step]
103     rbu close
104     if {$rc != "SQLITE_OK"} break
105   }
106   set rc
109 # Same as [step_rbu], except using an external state database - "state.db"
111 proc step_rbu_state {target rbu} {
112   while 1 {
113     sqlite3rbu rbu $target $rbu state.db
114     set rc [rbu step]
115     rbu close
116     if {$rc != "SQLITE_OK"} break
117   }
118   set rc
121 proc dbfilecksum {file} {
122   sqlite3 ck $file
123   set cksum [dbcksum ck main]
124   ck close
125   set cksum
128 foreach {tn3 create_vfs destroy_vfs} {
129   1 {} {}
130   2 {
131     sqlite3rbu_create_vfs -default myrbu ""
132   } {
133     sqlite3rbu_destroy_vfs myrbu
134   }
135 } {
137   eval $create_vfs
139   foreach {tn2 cmd} {
140       1 run_rbu 
141       2 step_rbu 3 step_rbu_uri 4 step_rbu_state
142       5 step_rbu_legacy
143   } {
144     foreach {tn schema} {
145       1 {
146         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
147       }
148       2 { 
149         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
150         CREATE INDEX i1 ON t1(b);
151       }
152       3 { 
153         CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
154       }
155       4 { 
156         CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID;
157         CREATE INDEX i1 ON t1(b);
158       }
159       5 { 
160         CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c)) WITHOUT ROWID;
161         CREATE INDEX i1 ON t1(b);
162       }
163       6 { 
164         CREATE TABLE t1(a, b, c, PRIMARY KEY(c)) WITHOUT ROWID;
165         CREATE INDEX i1 ON t1(b, a);
166       }
167       7 { 
168         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
169         CREATE INDEX i1 ON t1(b, c);
170         CREATE INDEX i2 ON t1(c, b);
171         CREATE INDEX i3 ON t1(a, b, c, a, b, c);
172       }
174       8 { 
175         CREATE TABLE t1(a PRIMARY KEY, b, c);
176         CREATE INDEX i1 ON t1(b, c);
177         CREATE INDEX i2 ON t1(c, b);
178         CREATE INDEX i3 ON t1(a, b, c, a, b, c);
179       }
181       9 { 
182         CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c));
183         CREATE INDEX i1 ON t1(b);
184       }
186       10 { 
187         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
188         CREATE INDEX i1 ON t1(b DESC);
189       }
191       11 { 
192         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
193         CREATE INDEX i1 ON t1(b DESC, a ASC, c DESC);
194       }
196       12 { 
197         CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID; 
198       }
200       13 { 
201         CREATE TABLE t1(a INT, b, c, PRIMARY KEY(a DESC)) WITHOUT ROWID; 
202       }
204       14 { 
205         CREATE TABLE t1(a, b, c, PRIMARY KEY(a DESC, c)) WITHOUT ROWID;
206         CREATE INDEX i1 ON t1(b);
207       }
209       15 { 
210         CREATE TABLE t1(a, b, c, PRIMARY KEY(a, c DESC)) WITHOUT ROWID;
211         CREATE INDEX i1 ON t1(b);
212       }
214       16 { 
215         CREATE TABLE t1(a, b, c, PRIMARY KEY(c DESC, a)) WITHOUT ROWID;
216         CREATE INDEX i1 ON t1(b DESC, c, a);
217       }
218     } {
219       reset_db
220       execsql $schema
221       create_rbu1 rbu.db
222       set check [dbfilecksum rbu.db]
223       forcedelete state.db
225       do_test $tn3.1.$tn2.$tn.1 {
226         $cmd test.db rbu.db
227       } {SQLITE_DONE}
229       do_execsql_test $tn3.1.$tn2.$tn.2 { SELECT * FROM t1 ORDER BY a ASC } {
230         1 2 3 
231         2 two three 
232         3 {} 8.2
233       }
234       do_execsql_test $tn3.1.$tn2.$tn.3 { SELECT * FROM t1 ORDER BY b ASC } {
235         3 {} 8.2
236         1 2 3 
237         2 two three 
238       }
239       do_execsql_test $tn3.1.$tn2.$tn.4 { SELECT * FROM t1 ORDER BY c ASC } {
240         1 2 3 
241         3 {} 8.2
242         2 two three 
243       }
244    
245       do_execsql_test $tn3.1.$tn2.$tn.5 { PRAGMA integrity_check } ok
247       if {$cmd=="step_rbu_state"} {
248         do_test $tn3.1.$tn2.$tn.6 { file exists state.db } 1
249         do_test $tn3.1.$tn2.$tn.7 { expr {$check == [dbfilecksum rbu.db]} } 1
250       } else {
251         do_test $tn3.1.$tn2.$tn.8 { file exists state.db } 0
252         do_test $tn3.1.$tn2.$tn.9 { expr {$check == [dbfilecksum rbu.db]} } 0
253       }
254     }
255   }
257   #-------------------------------------------------------------------------
258   # Check that an RBU cannot be applied to a table that has no PK.
259   #
260   # UPDATE: At one point RBU required that all tables featured either
261   # explicit IPK columns or were declared WITHOUT ROWID. This has been
262   # relaxed so that external PRIMARY KEYs on tables with automatic rowids
263   # are now allowed.
264   #
265   # UPDATE 2: Tables without any PRIMARY KEY declaration are now allowed.
266   # However the input table must feature an "rbu_rowid" column.
267   #
268   reset_db
269   create_rbu1 rbu.db
270   do_execsql_test $tn3.2.1 { CREATE TABLE t1(a, b, c) }
271   do_test $tn3.2.2 {
272     sqlite3rbu rbu test.db rbu.db
273     rbu step
274   } {SQLITE_ERROR}
275   do_test $tn3.2.3 {
276     list [catch { rbu close } msg] $msg
277   } {1 {SQLITE_ERROR - table data_t1 requires rbu_rowid column}}
278   reset_db
279   do_execsql_test $tn3.2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
280   do_test $tn3.2.5 {
281     sqlite3rbu rbu test.db rbu.db
282     rbu step
283   } {SQLITE_OK}
284   do_test $tn3.2.6 {
285     list [catch { rbu close } msg] $msg
286   } {0 SQLITE_OK}
288   #-------------------------------------------------------------------------
289   # Check that if a UNIQUE constraint is violated the current and all 
290   # subsequent [rbu step] calls return SQLITE_CONSTRAINT. And that the RBU 
291   # transaction is rolled back by the [rbu close] that deletes the rbu 
292   # handle.
293   #
294   foreach {tn errcode errmsg schema} {
295     1 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
296       CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
297       INSERT INTO t1 VALUES(3, 2, 1);
298     } 
300     2 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
301       CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE);
302       INSERT INTO t1 VALUES(4, 2, 'three');
303     } 
305     3 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.a" {
306       CREATE TABLE t1(a PRIMARY KEY, b, c);
307       INSERT INTO t1 VALUES(3, 2, 1);
308     } 
310     4 SQLITE_CONSTRAINT "UNIQUE constraint failed: t1.c" {
311       CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
312       INSERT INTO t1 VALUES(4, 2, 'three');
313     } 
315   } {
316     reset_db
317     execsql $schema
318     set cksum [dbcksum db main]
320     do_test $tn3.3.$tn.1 {
321       create_rbu1 rbu.db
322       sqlite3rbu rbu test.db rbu.db
323       while {[set res [rbu step]]=="SQLITE_OK"} {}
324       set res
325     } $errcode
327     do_test $tn3.3.$tn.2 { rbu step } $errcode
329     do_test $tn3.3.$tn.3 { 
330       list [catch { rbu close } msg] $msg
331     } [list 1 "$errcode - $errmsg"]
333     do_test $tn3.3.$tn.4 { dbcksum db main } $cksum
334   }
336   #-------------------------------------------------------------------------
337   #
338   foreach {tn2 cmd} {1 run_rbu 2 step_rbu 3 step_rbu_state } {
339     foreach {tn schema} {
340       1 {
341         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
342       }
343       2 {
344         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
345         CREATE INDEX i1 ON t1(b);
346       }
347       3 {
348         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
349         CREATE INDEX i1 ON t1(b);
350         CREATE INDEX i2 ON t1(c, b);
351         CREATE INDEX i3 ON t1(c, b, c);
352       }
353       4 {
354         CREATE TABLE t1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
355         CREATE INDEX i1 ON t1(b);
356         CREATE INDEX i2 ON t1(c, b);
357         CREATE INDEX i3 ON t1(c, b, c);
358       }
359       5 {
360         CREATE TABLE t1(a INT PRIMARY KEY, b, c);
361         CREATE INDEX i1 ON t1(b);
362         CREATE INDEX i2 ON t1(c, b);
363         CREATE INDEX i3 ON t1(c, b, c);
364       }
366       6 {
367         CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c);
368         CREATE INDEX i1 ON t1(b DESC);
369         CREATE INDEX i2 ON t1(c, b);
370         CREATE INDEX i3 ON t1(c DESC, b, c);
371       }
372       7 {
373         CREATE TABLE t1(a INT PRIMARY KEY DESC, b, c) WITHOUT ROWID;
374         CREATE INDEX i1 ON t1(b);
375         CREATE INDEX i2 ON t1(c, b);
376         CREATE INDEX i3 ON t1(c, b, c);
377       }
378     } {
379       reset_db
380       execsql $schema
381       execsql {
382         INSERT INTO t1 VALUES(2, 'hello', 'world');
383         INSERT INTO t1 VALUES(4, 'hello', 'planet');
384         INSERT INTO t1 VALUES(6, 'hello', 'xyz');
385       }
387       create_rbu4 rbu.db
388       set check [dbfilecksum rbu.db]
389       forcedelete state.db
390     
391       do_test $tn3.4.$tn2.$tn.1 {
392         $cmd test.db rbu.db
393       } {SQLITE_DONE}
394       
395       do_execsql_test $tn3.4.$tn2.$tn.2 {
396         SELECT * FROM t1 ORDER BY a ASC;
397       } {
398         1 2 3 
399         3 8 9
400         6 hello xyz
401       }
402     
403       do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok
405       if {$cmd=="step_rbu_state"} {
406         do_test $tn3.4.$tn2.$tn.4 { file exists state.db } 1
407         do_test $tn3.4.$tn2.$tn.5 { expr {$check == [dbfilecksum rbu.db]} } 1
408       } else {
409         do_test $tn3.4.$tn2.$tn.6 { file exists state.db } 0
410         do_test $tn3.4.$tn2.$tn.7 { expr {$check == [dbfilecksum rbu.db]} } 0
411       }
412     }
413   }
415   foreach {tn2 cmd} {1 run_rbu 2 step_rbu 3 step_rbu_state} {
416     foreach {tn schema} {
417       1 {
418         CREATE TABLE t1(c, b, '(a)' INTEGER PRIMARY KEY);
419         CREATE INDEX i1 ON t1(c, b);
420       }
421       2 {
422         CREATE TABLE t1(c, b, '(a)' PRIMARY KEY);
423       }
424       3 {
425         CREATE TABLE t1(c, b, '(a)' PRIMARY KEY) WITHOUT ROWID;
426       }
427     } {
428       reset_db
429       execsql $schema
430       execsql {
431         INSERT INTO t1('(a)', b, c) VALUES(2, 'hello', 'world');
432         INSERT INTO t1('(a)', b, c) VALUES(4, 'hello', 'planet');
433         INSERT INTO t1('(a)', b, c) VALUES(6, 'hello', 'xyz');
434       }
436       create_rbu4b rbu.db
437       set check [dbfilecksum rbu.db]
438       forcedelete state.db
439     
440       do_test $tn3.5.$tn2.$tn.1 {
441         $cmd test.db rbu.db
442       } {SQLITE_DONE}
443       
444       do_execsql_test $tn3.5.$tn2.$tn.2 {
445         SELECT * FROM t1 ORDER BY "(a)" ASC;
446       } {
447         3 2 1
448         9 8 3
449         xyz hello 6
450       }
451     
452       do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok
454       if {$cmd=="step_rbu_state"} {
455         do_test $tn3.5.$tn2.$tn.4 { file exists state.db } 1
456         do_test $tn3.5.$tn2.$tn.5 { expr {$check == [dbfilecksum rbu.db]} } 1
457       } else {
458         do_test $tn3.5.$tn2.$tn.6 { file exists state.db } 0
459         do_test $tn3.5.$tn2.$tn.7 { expr {$check == [dbfilecksum rbu.db]} } 0
460       }
461     }
462   }
464   #-------------------------------------------------------------------------
465   #
466   foreach {tn2 cmd} {1 run_rbu 2 step_rbu 3 step_rbu_state} {
467     foreach {tn schema} {
468       1 {
469         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
470       }
471       2 {
472         CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
473         CREATE INDEX i1 ON t1(d);
474         CREATE INDEX i2 ON t1(d, c);
475         CREATE INDEX i3 ON t1(d, c, b);
476         CREATE INDEX i4 ON t1(b);
477         CREATE INDEX i5 ON t1(c);
478         CREATE INDEX i6 ON t1(c, b);
479       }
480       3 {
481         CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID;
482         CREATE INDEX i1 ON t1(d);
483         CREATE INDEX i2 ON t1(d, c);
484         CREATE INDEX i3 ON t1(d, c, b);
485         CREATE INDEX i4 ON t1(b);
486         CREATE INDEX i5 ON t1(c);
487         CREATE INDEX i6 ON t1(c, b);
488       }
489       4 {
490         CREATE TABLE t1(a PRIMARY KEY, b, c, d);
491         CREATE INDEX i1 ON t1(d);
492         CREATE INDEX i2 ON t1(d, c);
493         CREATE INDEX i3 ON t1(d, c, b);
494         CREATE INDEX i4 ON t1(b);
495         CREATE INDEX i5 ON t1(c);
496         CREATE INDEX i6 ON t1(c, b);
497       }
498     } {
499       reset_db
500       execsql $schema
501       execsql {
502         INSERT INTO t1 VALUES(1, 2, 3, 4);
503         INSERT INTO t1 VALUES(2, 5, 6, 7);
504         INSERT INTO t1 VALUES(3, 8, 9, 10);
505       }
506     
507       create_rbu5 rbu.db
508       set check [dbfilecksum rbu.db]
509       forcedelete state.db
511       do_test $tn3.5.$tn2.$tn.1 {
512         $cmd test.db rbu.db
513       } {SQLITE_DONE}
514       
515       do_execsql_test $tn3.5.$tn2.$tn.2 {
516         SELECT * FROM t1 ORDER BY a ASC;
517       } {
518         1 2 3 5
519         2 5 10 5
520         3 11 9 10
521       }
522     
523       do_execsql_test $tn3.6.$tn2.$tn.3 { PRAGMA integrity_check } ok
525       if {$cmd=="step_rbu_state"} {
526         do_test $tn3.6.$tn2.$tn.4 { file exists state.db } 1
527         do_test $tn3.6.$tn2.$tn.5 { expr {$check == [dbfilecksum rbu.db]} } 1
528       } else {
529         do_test $tn3.6.$tn2.$tn.6 { file exists state.db } 0
530         do_test $tn3.6.$tn2.$tn.7 { expr {$check == [dbfilecksum rbu.db]} } 0
531       }
532     }
533   }
535   #-------------------------------------------------------------------------
536   # Test some error cases:
537   # 
538   #   * A virtual table with no rbu_rowid column.
539   #   * A no-PK table with no rbu_rowid column.
540   #   * A PK table with an rbu_rowid column.
541   #
542   #   6: An update string of the wrong length
543   #
544   ifcapable fts3 {
545     foreach {tn schema error} {
546        1 {
547          CREATE TABLE t1(a, b);
548          CREATE TABLE rbu.data_t1(a, b, rbu_control);
549        } {SQLITE_ERROR - table data_t1 requires rbu_rowid column}
550     
551        2 {
552          CREATE VIRTUAL TABLE t1 USING fts4(a, b);
553          CREATE TABLE rbu.data_t1(a, b, rbu_control);
554        } {SQLITE_ERROR - table data_t1 requires rbu_rowid column}
555     
556        3 {
557          CREATE TABLE t1(a PRIMARY KEY, b);
558          CREATE TABLE rbu.data_t1(a, b, rbu_rowid, rbu_control);
559        } {SQLITE_ERROR - table data_t1 may not have rbu_rowid column}
560     
561        4 {
562          CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
563          CREATE TABLE rbu.data_t1(a, b, rbu_rowid, rbu_control);
564        } {SQLITE_ERROR - table data_t1 may not have rbu_rowid column}
565     
566        5 {
567          CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
568          CREATE TABLE rbu.data_t1(a, b, rbu_rowid, rbu_control);
569        } {SQLITE_ERROR - table data_t1 may not have rbu_rowid column}
571        6 {
572          CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
573          CREATE TABLE rbu.data_t1(a, b, rbu_control);
574          INSERT INTO rbu.data_t1 VALUES(1, 2, 'x.x');
575        } {SQLITE_ERROR - invalid rbu_control value}
577        7 {
578          CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
579          CREATE TABLE rbu.data_t1(a, b, rbu_control);
580          INSERT INTO rbu.data_t1 VALUES(1, 2, NULL);
581        } {SQLITE_ERROR - invalid rbu_control value}
583        8 {
584          CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
585          CREATE TABLE rbu.data_t1(a, b, rbu_control);
586          INSERT INTO rbu.data_t1 VALUES(1, 2, 4);
587        } {SQLITE_ERROR - invalid rbu_control value}
589        9 {
590          CREATE TABLE t1(a, b PRIMARY KEY) WITHOUT ROWID;
591          CREATE TABLE rbu.data_t1(a, b, rbu_control);
592          INSERT INTO rbu.data_t1 VALUES(1, 2, 3);
593        } {SQLITE_ERROR - invalid rbu_control value}
595        10 {
596          CREATE TABLE t2(a, b);
597          CREATE TABLE rbu.data_t1(a, b, rbu_control);
598          INSERT INTO rbu.data_t1 VALUES(1, 2, 2);
599        } {SQLITE_ERROR - no such table: t1}
601        11 {
602          CREATE TABLE rbu.data_t2(a, b, rbu_control);
603          INSERT INTO rbu.data_t2 VALUES(1, 2, 2);
604        } {SQLITE_ERROR - no such table: t2}
606     } {
607       reset_db
608       forcedelete rbu.db
609       execsql { ATTACH 'rbu.db' AS rbu }
610       execsql $schema
612       do_test $tn3.7.$tn {
613         list [catch { run_rbu test.db rbu.db } msg] $msg
614       } [list 1 $error]
615     }
616   }
618   # Test that an RBU database containing no input tables is handled
619   # correctly.
620   reset_db
621   forcedelete rbu.db
622   do_test $tn3.8.1 {
623     list [catch { run_rbu test.db rbu.db } msg] $msg
624   } {0 SQLITE_DONE}
626   # Test that an RBU database containing only empty data_xxx tables is
627   # also handled correctly.
628   reset_db
629   forcedelete rbu.db
630   do_execsql_test $tn3.8.2.1 {
631     CREATE TABLE t1(a PRIMARY KEY, b);
632     INSERT INTO t1 VALUES(1, 2);
633     ATTACH 'rbu.db' AS rbu;
634     CREATE TABLE data_t1(a, b, rbu_control);
635     DETACH rbu;
636   }
637   do_test $tn3.8.2.1 {
638     list [catch { run_rbu test.db rbu.db } msg] $msg
639   } {0 SQLITE_DONE}
641   # Test that RBU can update indexes containing NULL values.
642   #
643   reset_db
644   forcedelete rbu.db
645   do_execsql_test $tn3.9.1 {
646     CREATE TABLE t1(a PRIMARY KEY, b, c);
647     CREATE INDEX i1 ON t1(b, c);
648     INSERT INTO t1 VALUES(1, 1, NULL);
649     INSERT INTO t1 VALUES(2, NULL, 2);
650     INSERT INTO t1 VALUES(3, NULL, NULL);
652     ATTACH 'rbu.db' AS rbu;
653     CREATE TABLE rbu.data_t1(a, b, c, rbu_control);
654     INSERT INTO data_t1 VALUES(1, NULL, NULL, 1);
655     INSERT INTO data_t1 VALUES(3, NULL, NULL, 1);
656   } {}
658   do_test $tn3.9.2 {
659     list [catch { run_rbu test.db rbu.db } msg] $msg
660   } {0 SQLITE_DONE}
662   do_execsql_test $tn3.9.3 {
663     SELECT * FROM t1
664   } {2 {} 2}
665   do_execsql_test $tn3.9.4 { PRAGMA integrity_check } {ok}
667   catch { db close }
668   eval $destroy_vfs
672 finish_test