SQLite

Check-in [e4db3db3a6]
Login

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

Overview
Comment:Add new test file e_walckpt.test. Still some tests to come.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e4db3db3a65ecfd4069a40d436aa7a5512d61a30
User & Date: dan 2014-12-05 20:46:19.108
Context
2014-12-05
21:04
Fix a buffer overread that might occur in analyze.c if SQLITE_ENABLE_STAT4 was defined. (check-in: c1ae1268b9 user: dan tags: trunk)
20:46
Add new test file e_walckpt.test. Still some tests to come. (check-in: e4db3db3a6 user: dan tags: trunk)
19:50
Make sure the WhereTerm objects are fully zeroed when they are allocated. (check-in: fdb667335c user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/test1.c.
5700
5701
5702
5703
5704
5705
5706
5707

5708
5709
5710
5711
5712
5713
5714

5715
5716
5717
5718
5719
5720
5721
5722
    Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?");
    return TCL_ERROR;
  }

  if( objc==4 ){
    zDb = Tcl_GetString(objv[3]);
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db)

   || Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) 
  ){
    return TCL_ERROR;
  }

  rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt);
  if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){

    Tcl_SetResult(interp, (char *)sqlite3_errmsg(db), TCL_VOLATILE);
    return TCL_ERROR;
  }

  pRet = Tcl_NewObj();
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0));
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog));
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt));







|
>
|
|





>
|







5700
5701
5702
5703
5704
5705
5706
5707
5708
5709
5710
5711
5712
5713
5714
5715
5716
5717
5718
5719
5720
5721
5722
5723
5724
    Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?");
    return TCL_ERROR;
  }

  if( objc==4 ){
    zDb = Tcl_GetString(objv[3]);
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) || (
      TCL_OK!=Tcl_GetIntFromObj(0, objv[2], &eMode)
   && TCL_OK!=Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) 
  )){
    return TCL_ERROR;
  }

  rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt);
  if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
    const char *zErrCode = sqlite3ErrName(rc);
    Tcl_AppendResult(interp, zErrCode, " - ", (char *)sqlite3_errmsg(db), 0);
    return TCL_ERROR;
  }

  pRet = Tcl_NewObj();
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0));
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog));
  Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt));
Added test/e_walckpt.test.




































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# 2014 December 04
#
# 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.
#
#***********************************************************************
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
set testprefix e_walckpt

# The following two commands are used to determine if any of the files
# "test.db", "test.db2" and "test.db3" are modified by a test case.
#
# The [save_db_hashes] command saves a hash of the current contents of
# all three files in global variables. The [compare_db_hashes] compares
# the current contents with the saved hashes and returns a list of the
# files that have changed.
#
proc save_db_hashes {} {
  global H
  foreach f {test.db test.db2 test.db3} {
    set H($f) 0
    catch { set H($f) [md5file $f] }
  }
}
proc compare_db_hashes {} {
  global H
  set ret [list]
  foreach f {test.db test.db2 test.db3} {
    set expect 0
    catch { set expect [md5file $f] }
    if {$H($f) != $expect} { lappend ret $f }
  }
  set ret
}


# The following tests are run 3 times, each using a different method of 
# invoking a checkpoint:
#
#   1) Using sqlite3_wal_checkpoint_v2()
#   2) Using "PRAGMA wal_checkpoint"
#   3) Using sqlite3_wal_checkpoint() in place of checkpoint_v2(PASSIVE)
#
# Cases (2) and (3) are to show that the following statements are 
# correct, respectively:
#
# EVIDENCE-OF: R-36706-10507 The PRAGMA wal_checkpoint command can be
# used to invoke this interface from SQL.
#
# EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is
# equivalent to
# sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0).
# 
foreach {tn script} {
  1 {
    proc checkpoint {db mode args} {
      eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args
    }
  }

  2 {
    proc checkpoint {db mode args} {
      set sql "PRAGMA wal_checkpoint"
      if {[llength $args] && [lindex $args 0]!=""} {
        set sql "PRAGMA [lindex $args 0].wal_checkpoint"
      }
      set rc [catch { $db eval $sql } msg]
      if {$rc} {
        regsub {database} $msg {database:} msg
        error "[sqlite3_errcode $db] - $msg"
      }
      set msg
    }
  }

  3 {
    proc checkpoint {db mode args} {
      if {$mode == "passive"} {
        set rc [eval sqlite3_wal_checkpoint [list $db] $args]
        if {$rc != "SQLITE_OK"} {
          error "$rc - [sqlite3_errmsg $db]"
        }
      } else {
        eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args
      }
    }
  }

} {

  eval $script

  reset_db
  forcedelete test.db2 test.db3 test.db4
  execsql {
    ATTACH 'test.db2' AS aux;
    ATTACH 'test.db3' AS aux2;
    ATTACH 'test.db4' AS aux3;
    CREATE TABLE t1(x);
    CREATE TABLE aux.t2(x);
    CREATE TABLE aux2.t3(x);
    CREATE TABLE aux3.t4(x);
    PRAGMA main.journal_mode = WAL;
    PRAGMA aux.journal_mode = WAL;
    PRAGMA aux2.journal_mode = WAL;
    /* Leave aux4 in rollback mode */
  }

  # EVIDENCE-OF: R-49787-09095 The sqlite3_wal_checkpoint_v2(D,X,M,L,C)
  # interface runs a checkpoint operation on database X of database
  # connection D in mode M. Status information is written back into
  # integers pointed to by L and C.
  #
  #     Tests 1, 2 and 3 below verify the "on database X" part of the
  #     above. Other parts of this requirement are tested below.
  #
  # EVIDENCE-OF: R-00653-06026 If parameter zDb is NULL or points to a
  # zero length string, then the specified operation is attempted on all
  # WAL databases attached to database connection db.
  #
  #     Tests 4 and 5 below test this.
  #
  foreach {tn2 zDb dblist} {
    1 main  test.db
    2 aux   test.db2
    3 aux2  test.db3
    4 ""    {test.db test.db2 test.db3}
    5 -     {test.db test.db2 test.db3}
    6 temp  {}
  } {
    do_test $tn.1.$tn2 {
      execsql {
        INSERT INTO t1 VALUES(1);
        INSERT INTO t2 VALUES(2);
        INSERT INTO t3 VALUES(3);
      }
      save_db_hashes

      if {$zDb == "-"} {
        checkpoint db passive
      } else {
        checkpoint db passive $zDb
      }

      compare_db_hashes
    } $dblist
  }

  # EVIDENCE-OF: R-38207-48996 If zDb is not NULL (or a zero length
  # string) and is not the name of any attached database, SQLITE_ERROR is
  # returned to the caller.
  do_test $tn.2.1 {
    list [catch { checkpoint db passive notadb } msg] $msg
  } {1 {SQLITE_ERROR - unknown database: notadb}}

  # EVIDENCE-OF: R-14303-42483 If database zDb is the name of an attached
  # database that is not in WAL mode, SQLITE_OK is returned and both
  # *pnLog and *pnCkpt set to -1.
  #
  if {$tn==3} {
    # With sqlite3_wal_checkpoint() the two output variables cannot be 
    # tested. So just test that no error is returned when attempting to
    # checkpoint a db in rollback mode.
    do_test $tn.2.2.a { checkpoint db passive aux3 } {}
  } else {
    do_test $tn.2.2.b { checkpoint db passive aux3 } {0 -1 -1}
  }

  # EVIDENCE-OF: R-62028-47212 All calls obtain an exclusive "checkpoint"
  # lock on the database file.
  db close
  testvfs tvfs
  tvfs filter xShmLock
  tvfs script filelock
  proc filelock {method file handle details} {
    # Test for an exclusive checkpoint lock. A checkpoint lock locks a
    # single byte starting at offset 1.
    if {$details == "1 1 lock exclusive"} { set ::seen_checkpoint_lock 1 }
  }
  sqlite3 db test.db -vfs tvfs
  do_test $tn.3.1 {
    execsql { INSERT INTO t1 VALUES('xyz') }
    unset -nocomplain ::seen_checkpoint_lock
    checkpoint db passive
    set ::seen_checkpoint_lock
  } {1}
  db close
  tvfs delete
  reset_db


 

  #-----------------------------------------------------------------------
  # EVIDENCE-OF: R-10421-19736 If any other process is running a
  # checkpoint operation at the same time, the lock cannot be obtained and
  # SQLITE_BUSY is returned.
  #
  # EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
  # it will not be invoked in this case.
  #
  testvfs tvfs
  tvfs filter xWrite
  sqlite3 db test.db -vfs tvfs
  sqlite3 db2 test.db -vfs tvfs

  do_test $tn.3.2.1 {
    db2 eval {
      PRAGMA journal_mode = WAL;
      CREATE TABLE t1(x, y);
      INSERT INTO t1 VALUES(1,2);
      INSERT INTO t1 VALUES(3,4);
      INSERT INTO t1 VALUES(5,6);
    }
    file size test.db-wal
  } [wal_file_size 5 1024]


  # Connection [db] runs a checkpoint. During this checkpoint, each
  # time it calls xWrite() to write a page into the database file, we
  # attempt to start a checkpoint using [db2]. According to the 
  # first requirement being tested, this should return SQLITE_BUSY. According
  # to the second, the busy-handler belonging to [db2] should not be
  # invoked.
  #
  set ::write_count 0
  set ::write_errors [list]
  proc busy_callback {args} {
    lappend ::write_errors "busy handler called!"
  }
  proc write_callback {args} {
    set rc [catch {checkpoint db2 passive} msg]
    if {0==[regexp "database is locked" $msg] && $msg!="1 -1 -1"} {
      lappend ::write_errors "$rc $msg"
    } 
    incr ::write_count
  }
  db2 busy busy_callback
  tvfs script write_callback

  do_test $tn.3.2.2 {
    db eval {SELECT * FROM sqlite_master}
    checkpoint db full
    set ::write_count
  } {2}

  do_test $tn.3.2.3 {
    set ::write_errors
  } {}

  db close
  db2 close
  tvfs delete

}

#-----------------------------------------------------------------------
# EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
# mode:
#
#   Valid checkpoint modes are 0, 1, 2 and 3.
#
sqlite3 db test.db
foreach {tn mode res} {
  0 -1001    {1 {SQLITE_MISUSE - not an error}}
  1 -1       {1 {SQLITE_MISUSE - not an error}}
  2  0       {0 {0 -1 -1}}
  3  1       {0 {0 -1 -1}}
  4  2       {0 {0 -1 -1}}
  5  3       {0 {0 -1 -1}}
  6  4       {1 {SQLITE_MISUSE - not an error}}
  7  114     {1 {SQLITE_MISUSE - not an error}}
  8  1000000 {1 {SQLITE_MISUSE - not an error}}
} {
  do_test 4.$tn {
    list [catch "sqlite3_wal_checkpoint_v2 db $mode" msg] $msg
  } $res
}


finish_test