summaryrefslogtreecommitdiffstats
path: root/ext/rbu/rbufault.test
blob: d35c389d7be165672d03981f0b1084fb6b718e8c (plain)
1
2
3
4
5
6
7
8
9
10
11
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
98
99
100
101
102
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# 2014 October 22
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#

source [file join [file dirname [info script]] rbu_common.tcl]
if_no_rbu_support { finish_test ; return }
source $testdir/malloc_common.tcl
set ::testprefix rbufault

proc copy_if_exists {src target} {
  if {[file exists $src]} {
    forcecopy $src $target
  } else {
    forcedelete $target
  }
}

foreach {tn2 setup sql expect} {
  1 {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    CREATE INDEX t1cb ON t1(c, b);
    INSERT INTO t1 VALUES(1, 1, 1);
    INSERT INTO t1 VALUES(2, 2, 2);
    INSERT INTO t1 VALUES(3, 3, 3);

    CREATE TABLE rbu.data_t1(a, b, c, rbu_control);
    INSERT INTO data_t1 VALUES(2, NULL, NULL, 1);
    INSERT INTO data_t1 VALUES(3, 'three', NULL, '.x.');
    INSERT INTO data_t1 VALUES(4, 4, 4, 0);
  } {
    SELECT * FROM t1
  } {1 1 1   3 three 3   4 4 4}

  2 {
    CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
    CREATE INDEX t2cb ON t2(c, b);
    INSERT INTO t2 VALUES('a', 'a', 'a');
    INSERT INTO t2 VALUES('b', 'b', 'b');
    INSERT INTO t2 VALUES('c', 'c', 'c');

    CREATE TABLE rbu.data_t2(a, b, c, rbu_control);
    INSERT INTO data_t2 VALUES('b', NULL, NULL, 1);
    INSERT INTO data_t2 VALUES('c', 'see', NULL, '.x.');
    INSERT INTO data_t2 VALUES('d', 'd', 'd', 0);
  } {
    SELECT * FROM t2
  } {a a a   c see c     d d d}

  3 {
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    CREATE TABLE t2(a PRIMARY KEY, b, c) WITHOUT ROWID;
    CREATE INDEX t1cb ON t1(c, b);
    CREATE INDEX t2cb ON t2(c, b);

    CREATE TABLE rbu.data_t1(a, b, c, rbu_control);
    CREATE TABLE rbu.data_t2(a, b, c, rbu_control);
    INSERT INTO data_t1 VALUES(1, 2, 3, 0);
    INSERT INTO data_t2 VALUES(4, 5, 6, 0);
  } {
    SELECT * FROM t1 UNION ALL SELECT * FROM t2
  } {1 2 3 4 5 6}

  4 {
    CREATE TABLE t1(a PRIMARY KEY, b, c);
    CREATE INDEX t1c ON t1(c);
    INSERT INTO t1 VALUES('A', 'B', 'C');
    INSERT INTO t1 VALUES('D', 'E', 'F');

    CREATE TABLE rbu.data_t1(a, b, c, rbu_control);
    INSERT INTO data_t1 VALUES('D', NULL, NULL, 1);
    INSERT INTO data_t1 VALUES('A', 'Z', NULL, '.x.');
    INSERT INTO data_t1 VALUES('G', 'H', 'I', 0);
  } {
    SELECT * FROM t1 ORDER BY a;
  } {A Z C G H I}

  5 {
    CREATE TABLE t1(a, b, c);
    CREATE INDEX t1c ON t1(c, b);

    CREATE TABLE rbu.data_t1(a, b, c, rbu_rowid, rbu_control);
    INSERT INTO data_t1 VALUES('a', 'b', 'c', 1, 0);
    INSERT INTO data_t1 VALUES('d', 'e', 'f', '2', 0);
  } {
    SELECT * FROM t1 ORDER BY a;
  } {a b c d e f}

} {
  catch {db close}
  forcedelete rbu.db test.db
  sqlite3 db test.db
  execsql {
    PRAGMA encoding = utf16;
    ATTACH 'rbu.db' AS rbu;
  }
  execsql $setup
  db close

  forcecopy test.db test.db.bak
  forcecopy rbu.db rbu.db.bak

  foreach {tn f reslist} {
    1 oom-tra*  {
      {0 SQLITE_DONE} 
      {1 {SQLITE_NOMEM - out of memory}} 
      {1 SQLITE_NOMEM} 
      {1 SQLITE_IOERR_NOMEM} 
      {1 {SQLITE_NOMEM - unable to open a temporary database file for storing temporary tables}}
    }
  
    2 ioerr-*  {
      {0 SQLITE_DONE} 
      {1 {SQLITE_IOERR - disk I/O error}}
      {1 SQLITE_IOERR}
      {1 SQLITE_IOERR_WRITE}
      {1 SQLITE_IOERR_READ}
      {1 SQLITE_IOERR_FSYNC}
      {1 {SQLITE_ERROR - SQL logic error}}
      {1 {SQLITE_ERROR - unable to open database: rbu.db}}
      {1 {SQLITE_IOERR - unable to open database: rbu.db}}
    }

    3 shmerr-*  {
      {0 SQLITE_DONE} 
      {1 {SQLITE_IOERR - disk I/O error}}
      {1 SQLITE_IOERR}
    }
  } {

    catch {db close}
    sqlite3_shutdown
    set lookaside_config [sqlite3_config_lookaside 0 0]
    sqlite3_initialize
    autoinstall_test_functions

    do_faultsim_test 2.$tn2 -faults $::f -prep {
      catch { db close }
      forcedelete test.db-journal test.db-wal rbu.db-journal rbu.db-wal
      forcecopy test.db.bak test.db
      forcecopy rbu.db.bak  rbu.db
    } -body {
      sqlite3rbu rbu test.db rbu.db
      while {[rbu step]=="SQLITE_OK"} {}
      rbu close
    } -test {
      faultsim_test_result {*}$::reslist
      if {$testrc==0} {
        sqlite3 db test.db
        faultsim_integrity_check
        set res [db eval $::sql]
        if {$res != [list {*}$::expect]} {
          puts ""
          puts "res: $res"
          puts "exp: $expect"
          error "data not as expected!"
        }
      }
    }

    catch {db close}
    sqlite3_shutdown
    sqlite3_config_lookaside {*}$lookaside_config
    sqlite3_initialize
    autoinstall_test_functions


    for {set iStep 0} {$iStep<=21} {incr iStep} {
    
      forcedelete test.db-journal test.db-wal rbu.db-journal rbu.db-wal
    
      copy_if_exists test.db.bak test.db
      copy_if_exists rbu.db.bak rbu.db
    
      sqlite3rbu rbu test.db rbu.db
      for {set x 0} {$x < $::iStep} {incr x} { rbu step }
      rbu close
  
# sqlite3 x rbu.db ; puts "XYZ [x eval { SELECT * FROM rbu_state } ]" ; x close
    
      copy_if_exists test.db     test.db.bak.2
      copy_if_exists test.db-wal test.db.bak.2-wal
      copy_if_exists test.db-oal test.db.bak.2-oal
      copy_if_exists rbu.db      rbu.db.bak.2
    
      do_faultsim_test 3.$tn.$iStep -faults $::f -prep {
        catch { db close }
        forcedelete test.db-journal test.db-wal rbu.db-journal rbu.db-wal
        copy_if_exists test.db.bak.2 test.db
        copy_if_exists test.db.bak.2-wal test.db-wal
        copy_if_exists test.db.bak.2-oal test.db-oal
        copy_if_exists rbu.db.bak.2  rbu.db
      } -body {
        sqlite3rbu rbu test.db rbu.db
        rbu step
        rbu close
      } -test {

        if {$testresult=="SQLITE_OK"} {set testresult "SQLITE_DONE"}
        faultsim_test_result {*}$::reslist
      
        if {$testrc==0} {
          # No error occurred. If the RBU has not already been fully applied,
          # apply the rest of it now. Then ensure that the final state of the
          # target db is as expected. And that "PRAGMA integrity_check"
          # passes.
          sqlite3rbu rbu test.db rbu.db
          while {[rbu step] == "SQLITE_OK"} {}
          rbu close

          sqlite3 db test.db
          faultsim_integrity_check

          set res [db eval $::sql]
          if {$res != [list {*}$::expect]} {
            puts ""
            puts "res: $res"
            puts "exp: $::expect"
            error "data not as expected!"
          }
        }
      }
    }
  }
}

finish_test