1# 2010 September 1
2#
3# The author disclaims copyright to this source code.  In place of
4# a legal notice, here is a blessing:
5#
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.
9#
10#***********************************************************************
11#
12
13set testdir [file dirname $argv0]
14source $testdir/tester.tcl
15
16# If SQLITE_CURDIR is not defined, omit this file.
17ifcapable !curdir {
18  finish_test
19  return
20}
21
22source $testdir/malloc_common.tcl
23
24forcedelete bak.db
25unset -nocomplain defaultVfs
26set defaultVfs [file_control_vfsname db]
27db close
28
29do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR}
30do_test quota-1.2 { sqlite3_quota_initialize "" 1 }        {SQLITE_OK}
31do_test quota-1.3 { sqlite3_quota_initialize "" 1 }        {SQLITE_MISUSE}
32do_test quota-1.4 { sqlite3_quota_shutdown }               {SQLITE_OK}
33
34do_test quota-1.5 { sqlite3_quota_initialize "" 0 }        {SQLITE_OK}
35do_test quota-1.6 { sqlite3_quota_shutdown }               {SQLITE_OK}
36do_test quota-1.7 { sqlite3_quota_initialize "" 1 }        {SQLITE_OK}
37do_test quota-1.8 { sqlite3_quota_shutdown }               {SQLITE_OK}
38
39
40#-------------------------------------------------------------------------
41# Some simple warm-body tests with a single database file in rollback
42# mode:
43#
44#   quota-2.1.*: Test that SQLITE_FULL is returned if the database would
45#                exceed the configured quota.
46#
47#   quota-2.2.*: Test that SQLITE_FULL is not returned and the database
48#                grows if the callback extends the quota when the database
49#                attempts to grow beyond the configured quota.
50#
51#   quota-2.3.*: Open and close a db that is not part of any quota group. At
52#                one point this was causing mutex refs to be leaked.
53#
54#   quota-2.4.*: Try to shutdown the quota system before closing the db
55#                file. Check that this fails and the quota system still works
56#                afterwards. Then close the database and successfully shut
57#                down the quota system.
58#
59sqlite3_quota_initialize "" 1
60
61unset -nocomplain quota_request_ok
62proc quota_check {filename limitvar size} {
63  upvar $limitvar limit
64
65  lappend ::quota [set limit] $size
66  if {[info exists ::quota_request_ok]} { set limit $size }
67}
68
69do_test quota-2.1.1 {
70  sqlite3_quota_set *test.db 4096 quota_check
71} {SQLITE_OK}
72do_test quota-2.1.2 {
73  sqlite3 db test.db
74  execsql {
75    PRAGMA page_size=1024;
76    PRAGMA auto_vacuum=OFF;
77    PRAGMA journal_mode=DELETE;
78  }
79  set ::quota [list]
80  execsql {
81    CREATE TABLE t1(a, b);
82    INSERT INTO t1 VALUES(1, randomblob(1100));
83    INSERT INTO t1 VALUES(2, randomblob(1100));
84  }
85  set ::quota
86} {}
87do_test quota-2.1.2.1 {
88  file_control_vfsname db
89} quota/$defaultVfs
90do_test quota-2.1.3 { file size test.db } {4096}
91do_test quota-2.1.4 {
92  catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
93} {1 {database or disk is full}}
94do_test quota-2.1.5 { set ::quota } {4096 5120}
95
96set ::quota_request_ok 1
97set ::quota [list]
98do_test quota-2.2.1 {
99  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
100} {}
101do_test quota-2.2.2 { set ::quota } {4096 5120}
102do_test quota-2.2.3 { file size test.db } {5120}
103unset ::quota_request_ok
104
105do_test quota-2.3.1 {
106  sqlite3 db2 bak.db
107  db2 close
108} {}
109
110do_test quota-2.4.1 {
111  sqlite3_quota_shutdown
112} {SQLITE_MISUSE}
113set ::quota [list]
114do_test quota-2.4.2 {
115  catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
116} {1 {database or disk is full}}
117do_test quota-2.4.3 { set ::quota } {5120 6144}
118do_test quota-2.4.4 { file size test.db } {5120}
119do_test quota-2.4.99 {
120  db close
121  sqlite3_quota_shutdown
122} {SQLITE_OK}
123
124#-------------------------------------------------------------------------
125# Try some tests with more than one connection to a database file. Still
126# in rollback mode.
127#
128#   quota-3.1.*: Two connections to a single database file.
129#
130#   quota-3.2.*: Two connections to each of several database files (that
131#                are in the same quota group).
132#
133proc quota_check {filename limitvar size} {
134  upvar $limitvar limit
135  lappend ::quota [set limit] $size
136  if {[info exists ::quota_request_ok]} { set limit $size }
137}
138
139do_test quota-3.1.1 {
140  forcedelete test.db
141  sqlite3_quota_initialize "" 1
142  sqlite3_quota_set *test.db 4096 quota_check
143} {SQLITE_OK}
144do_test quota-3.1.2 {
145  sqlite3 db test.db
146  execsql {
147    PRAGMA page_size = 1024;
148    PRAGMA journal_mode = delete;
149    PRAGMA auto_vacuum = off;
150    CREATE TABLE t1(a PRIMARY KEY, b);
151    INSERT INTO t1 VALUES(1, 'one');
152  }
153  file size test.db
154} {3072}
155do_test quota-3.1.3 {
156  sqlite3 db2 test.db
157  set ::quota [list]
158  execsql { CREATE TABLE t2(a, b) } db2
159  set ::quota
160} {}
161do_test quota-3.1.4 {
162  catchsql { CREATE TABLE t3(a, b) }
163} {1 {database or disk is full}}
164do_test quota-3.1.5 {
165  set ::quota_request_ok 1
166  execsql { CREATE TABLE t3(a, b) }
167} {}
168do_test quota-3.1.6 {
169  db close
170  db2 close
171  sqlite3_quota_set *test.db 0 {}
172} {SQLITE_OK}
173
174do_test quota-3.2.1 {
175  delete_file force test.db test2.db
176
177  sqlite3_quota_set * 4096 {}
178  sqlite3 db1a test.db
179  sqlite3 db2a test2.db
180
181  foreach db {db1a db2a} {
182    execsql {
183      PRAGMA page_size = 1024;
184      PRAGMA journal_mode = delete;
185      PRAGMA auto_vacuum = off;
186      CREATE TABLE t1(a, b);
187    } $db
188  }
189
190  sqlite3 db1b test.db
191  sqlite3 db2b test2.db
192
193  list [file size test.db] [file size test2.db]
194} {2048 2048}
195
196catch { unset ::quota_request_ok }
197
198do_test quota-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {}
199do_test quota-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {}
200do_test quota-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {}
201do_test quota-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {}
202
203do_test quota-3.2.6 {
204  catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
205} {1 {database or disk is full}}
206do_test quota-3.2.7 {
207  catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
208} {1 {database or disk is full}}
209do_test quota-3.2.8 {
210  catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
211} {1 {database or disk is full}}
212do_test quota-3.2.9 {
213  catchsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
214} {1 {database or disk is full}}
215
216set ::quota [list]
217proc quota_callback {file limitvar size} {
218  upvar $limitvar limit
219  if {$::tcl_platform(platform)=="windows"} {
220    set file [ lindex [string map {\\ \/} $file] 0 ]
221  }
222  lappend ::quota $file $size
223  set limit 0
224}
225sqlite3_quota_set * 4096 quota_callback
226do_test quota-3.3.1 {
227  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
228  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
229  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
230  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
231  set ::quota
232} [list [file join [get_pwd] test.db] 5120]
233
234do_test quota-3.2.X {
235  foreach db {db1a db2a db2b db1b} { catch { $db close } }
236  sqlite3_quota_set * 0 {}
237} {SQLITE_OK}
238
239#-------------------------------------------------------------------------
240# Quotas are deleted when unused and when their limit is set to zero
241#
242
243# Return a list of all currently defined quotas.  Each quota is identified
244# by its pattern.
245proc quota_list {} {
246  set allq {}
247  foreach q [sqlite3_quota_dump] {
248    lappend allq [lindex $q 0]
249  }
250  return [lsort $allq]
251}
252proc quota_size {name} {
253  set allq {}
254  foreach q [sqlite3_quota_dump] {
255    if {[lindex $q 0]==$name} {return [lindex $q 2]}
256  }
257  return 0
258}
259
260do_test quota-4.1.1 {
261  sqlite3_quota_set *test.db 0 {}
262  quota_list
263} {}
264do_test quota-4.1.2 {
265  sqlite3_quota_set *test.db 4096 {}
266  quota_list
267} {*test.db}
268do_test quota-4.1.3 {
269  sqlite3_quota_set *test2.db 0 {}
270  quota_list
271} {*test.db}
272do_test quota-4.1.4 {
273  sqlite3_quota_set *test2.db 100000 {}
274  quota_list
275} {*test.db *test2.db}
276do_test quota-4.1.5 {
277  sqlite3_quota_set *test.db 0 {}
278  quota_list
279} {*test2.db}
280do_test quota-4.1.6 {
281  forcedelete test2.db test2.db-journal test2.db-wal
282  sqlite3 db test2.db
283  db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');}
284  quota_list
285} {*test2.db}
286do_test quota-4.1.7 {
287  catchsql {INSERT INTO t2 VALUES(zeroblob(200000))}
288} {1 {database or disk is full}}
289do_test quota-4.1.8 {
290  sqlite3 db2 test2.db
291  db2 eval {SELECT * FROM t2}
292} {tab-t2}
293do_test quota-4.1.9 {
294  sqlite3_quota_set *test2.db 0 {}
295  catchsql {INSERT INTO t2 VALUES(zeroblob(200000))}
296} {0 {}}
297do_test quota-4.1.10 {
298  quota_list
299} {*test2.db}
300do_test quota-4.1.11 {
301  db2 close
302  quota_list
303} {*test2.db}
304do_test quota-4.1.12 {
305  db close
306  quota_list
307} {}
308
309do_test quota-4.2.1 {
310  sqlite3_quota_set A 1000 {}
311  sqlite3_quota_set B 1000 {}
312  sqlite3_quota_set C 1000 {}
313  sqlite3_quota_set D 1000 {}
314  quota_list
315} {A B C D}
316do_test quota-4.2.2 {
317  sqlite3_quota_set C 0 {}
318  sqlite3_quota_set B 0 {}
319  quota_list
320} {A D}
321do_test quota-4.2.3 {
322  sqlite3_quota_set A 0 {}
323  sqlite3_quota_set D 0 {}
324  quota_list
325} {}
326do_test quota-4.2.4 {
327  sqlite3_quota_set A 1000 {}
328  sqlite3_quota_set B 1000 {}
329  sqlite3_quota_set C 1000 {}
330  sqlite3_quota_set A 0 {}
331  sqlite3_quota_set B 0 {}
332  sqlite3_quota_set C 0 {}
333  quota_list
334} {}
335do_test quota-4.2.5 {
336  sqlite3_quota_set A 1000 {}
337  sqlite3_quota_set B 1000 {}
338  sqlite3_quota_set C 1000 {}
339  sqlite3_quota_set C 0 {}
340  sqlite3_quota_set B 0 {}
341  sqlite3_quota_set A 0 {}
342  quota_list
343} {}
344
345do_test quota-4.3.1 {
346  sqlite3_quota_set A 1000 quota_callback
347  sqlite3 db A
348  sqlite3_quota_set A 0 quota_callback
349  db close
350  quota_list
351} {}
352
353unset -nocomplain quotagroup
354if {$tcl_platform(platform)=="windows"} {
355  set quotagroup *\\quota-test-A?.db
356} else {
357  set quotagroup */quota-test-A?.db
358}
359foreach file [glob -nocomplain quota-test-A*] {
360  forcedelete $file
361}
362do_test quota-4.4.1 {
363  set ::quota {}
364  sqlite3_quota_set $::quotagroup 10000 quota_callback
365  forcedelete ./quota-test-A1.db ./quota-test-A2.db
366  sqlite3 db ./quota-test-A1.db
367  db eval {
368     CREATE TABLE t1(x);
369     INSERT INTO t1 VALUES(randomblob(5000));
370  }
371  quota_list
372} [list $quotagroup]
373do_test quota-4.4.2 {
374  expr {$::quota==""}
375} {1}
376do_test quota-4.4.3 {
377  db close
378  sqlite3 db ./quota-test-A2.db
379  db eval {
380     CREATE TABLE t1(x);
381     INSERT INTO t1 VALUES(randomblob(5000));
382  }
383  quota_list
384} [list $quotagroup]
385do_test quota-4.4.4 {
386  expr {$::quota!=""}
387} {1}
388do_test quota-4.4.5 {
389  db close
390  sqlite3_quota_set $::quotagroup 0 {}
391  sqlite3_quota_dump
392} {}
393do_test quota-4.4.6 {
394  sqlite3_quota_set $quotagroup 10000 quota_callback
395  sqlite3 db quota-test-A1.db
396  db eval {SELECT count(*) FROM sqlite_master}
397  quota_size $quotagroup
398} [file size quota-test-A1.db]
399do_test quota-4.4.7 {
400  sqlite3_quota_file quota-test-A2.db
401  quota_size $::quotagroup
402} [expr {[file size quota-test-A1.db]+[file size quota-test-A2.db]}]
403
404unset -nocomplain quotagroup
405if {$tcl_platform(platform)=="windows"} {
406  set quotagroup *\\quota-test-B*
407} else {
408  set quotagroup */quota-test-B*
409}
410foreach file [glob -nocomplain quota-test-B*] {
411  forcedelete $file
412}
413do_test quota-4.5.1 {
414  sqlite3_quota_set $::quotagroup 100000 quota_callback
415  quota_size $::quotagroup
416} {0}
417do_test quota-4.5.2 {
418  sqlite3_quota_file quota-test-B1.txt
419  quota_size $::quotagroup
420} {0}
421proc add_to_file {name n} {
422  set out [open $name a]
423  fconfigure $out -translation binary
424  puts -nonewline $out [string repeat x $n]
425  close $out
426}
427do_test quota-4.5.3 {
428  add_to_file quota-test-B1.txt 123
429  sqlite3_quota_file quota-test-B1.txt
430  quota_size $::quotagroup
431} {123}
432do_test quota-4.5.4 {
433  add_to_file quota-test-B2.txt 234
434  sqlite3_quota_file quota-test-B2.txt
435  quota_size $::quotagroup
436} {357}
437do_test quota-4.5.5 {
438  add_to_file quota-test-B1.txt 2000
439  sqlite3_quota_file quota-test-B1.txt
440  quota_size $::quotagroup
441} {2357}
442do_test quota-4.5.6 {
443  forcedelete quota-test-B1.txt
444  sqlite3_quota_file quota-test-B1.txt
445  quota_size $::quotagroup
446} {234}
447do_test quota-4.5.7 {
448  forcedelete quota-test-B2.txt
449  sqlite3_quota_file quota-test-B2.txt
450  quota_size $::quotagroup
451} {0}
452do_test quota-4.5.8 {
453  add_to_file quota-test-B3.txt 1234
454  sqlite3_quota_file quota-test-B3.txt
455  quota_size $::quotagroup
456} {1234}
457do_test quota-4.5.9 {
458  sqlite3_quota_set $quotagroup 0 {}
459  quota_size $::quotagroup
460} {0}
461
462do_test quota-4.9.1 {
463  db close
464  sqlite3_quota_set A 1000 quota_callback
465  sqlite3_quota_shutdown
466} {SQLITE_OK}
467do_test quota-4.9.2 {
468  quota_list
469} {}
470
471#-------------------------------------------------------------------------
472# The following tests test that the quota VFS handles malloc and IO
473# errors.
474#
475
476sqlite3_quota_initialize "" 1
477sqlite3_quota_set *test.db 4096 {}
478
479do_faultsim_test quota-5.1 -prep {
480  catch {db close}
481} -body {
482  sqlite3 db test2.db
483}
484do_faultsim_test quota-5.2 -prep {
485  catch {db close}
486} -body {
487  sqlite3 db test.db
488}
489
490catch { db close }
491forcedelete test.db
492
493do_test quota-5.3.prep {
494  sqlite3 db test.db
495  execsql {
496    PRAGMA auto_vacuum = 1;
497    PRAGMA page_size = 1024;
498    CREATE TABLE t1(a, b);
499    INSERT INTO t1 VALUES(10, zeroblob(1200));
500  }
501  faultsim_save_and_close
502} {}
503do_faultsim_test quota-5.3 -prep {
504  faultsim_restore_and_reopen
505} -body {
506  execsql { DELETE FROM t1 }
507}
508
509do_test quota-5.4.1 {
510  catch { db close }
511  forcedelete test.db
512  file mkdir test.db
513  list [catch { sqlite3 db test.db } msg] $msg
514} {1 {unable to open database file}}
515
516do_faultsim_test quota-5.5 -prep {
517  catch { sqlite3_quota_shutdown }
518} -body {
519  sqlite3_quota_initialize "" 1
520}
521
522do_faultsim_test quota-5.6 -prep {
523  catch { sqlite3_quota_shutdown }
524  sqlite3_quota_initialize "" 1
525} -body {
526  sqlite3_quota_set * 4096 {}
527}
528
529catch { sqlite3_quota_shutdown }
530catch { db close }
531forcedelete test.db
532finish_test
533