xref: /sqlite-3.40.0/test/vtab_err.test (revision 73bdf077)
1# 2006 June 10
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# $Id: vtab_err.test,v 1.3 2006/08/15 14:21:16 drh Exp $
13
14set testdir [file dirname $argv0]
15source $testdir/tester.tcl
16
17ifcapable !vtab {
18  finish_test
19  return
20}
21
22# Usage: do_malloc_test <test number> <options...>
23#
24# The first argument, <test number>, is an integer used to name the
25# tests executed by this proc. Options are as follows:
26#
27#     -tclprep          TCL script to run to prepare test.
28#     -sqlprep          SQL script to run to prepare test.
29#     -tclbody          TCL script to run with malloc failure simulation.
30#     -sqlbody          TCL script to run with malloc failure simulation.
31#     -cleanup          TCL script to run after the test.
32#
33# This command runs a series of tests to verify SQLite's ability
34# to handle an out-of-memory condition gracefully. It is assumed
35# that if this condition occurs a malloc() call will return a
36# NULL pointer. Linux, for example, doesn't do that by default. See
37# the "BUGS" section of malloc(3).
38#
39# Each iteration of a loop, the TCL commands in any argument passed
40# to the -tclbody switch, followed by the SQL commands in any argument
41# passed to the -sqlbody switch are executed. Each iteration the
42# Nth call to sqliteMalloc() is made to fail, where N is increased
43# each time the loop runs starting from 1. When all commands execute
44# successfully, the loop ends.
45#
46proc do_malloc_test {tn args} {
47  array unset ::mallocopts
48  array set ::mallocopts $args
49
50  set ::go 1
51  for {set ::n 1} {$::go && $::n < 50000} {incr ::n} {
52    do_test $tn.$::n {
53
54      # Remove all traces of database files test.db and test2.db from the files
55      # system. Then open (empty database) "test.db" with the handle [db].
56      #
57      sqlite_malloc_fail 0
58      catch {db close}
59      catch {file delete -force test.db}
60      catch {file delete -force test.db-journal}
61      catch {file delete -force test2.db}
62      catch {file delete -force test2.db-journal}
63      catch {sqlite3 db test.db}
64      set ::DB [sqlite3_connection_pointer db]
65
66      # Execute any -tclprep and -sqlprep scripts.
67      #
68      if {[info exists ::mallocopts(-tclprep)]} {
69        eval $::mallocopts(-tclprep)
70      }
71      if {[info exists ::mallocopts(-sqlprep)]} {
72        execsql $::mallocopts(-sqlprep)
73      }
74
75      # Now set the ${::n}th malloc() to fail and execute the -tclbody and
76      # -sqlbody scripts.
77      #
78      sqlite_malloc_fail $::n
79      set ::mallocbody {}
80      if {[info exists ::mallocopts(-tclbody)]} {
81        append ::mallocbody "$::mallocopts(-tclbody)\n"
82      }
83      if {[info exists ::mallocopts(-sqlbody)]} {
84        append ::mallocbody "db eval {$::mallocopts(-sqlbody)}"
85      }
86      set v [catch $::mallocbody msg]
87
88      # If the test fails (if $v!=0) and the database connection actually
89      # exists, make sure the failure code is SQLITE_NOMEM.
90      if {$v&&[info command db]=="db"&&[info exists ::mallocopts(-sqlbody)]} {
91        if {[db errorcode]!=7 && $msg!="vtable constructor failed: e"} {
92          set v 999
93        }
94      }
95
96      set leftover [lindex [sqlite_malloc_stat] 2]
97      if {$leftover>0} {
98        if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v  Message=$msg"}
99        set ::go 0
100        if {$v} {
101          puts "\nError message returned: $msg"
102        } else {
103          set v {1 1}
104        }
105      } else {
106        set v2 [expr {
107          $msg == "" || $msg == "out of memory" ||
108          $msg == "vtable constructor failed: e"
109        }]
110        if {!$v2} {puts "\nError message returned: $msg"}
111        lappend v $v2
112      }
113    } {1 1}
114
115    if {[info exists ::mallocopts(-cleanup)]} {
116      catch [list uplevel #0 $::mallocopts(-cleanup)] msg
117    }
118  }
119  unset ::mallocopts
120}
121
122unset -nocomplain echo_module_begin_fail
123do_ioerr_test vtab_err-1 -tclprep {
124  register_echo_module [sqlite3_connection_pointer db]
125} -sqlbody {
126  BEGIN;
127  CREATE TABLE r(a PRIMARY KEY, b, c);
128  CREATE VIRTUAL TABLE e USING echo(r);
129  INSERT INTO e VALUES(1, 2, 3);
130  INSERT INTO e VALUES('a', 'b', 'c');
131  UPDATE e SET c = 10;
132  DELETE FROM e WHERE a = 'a';
133  COMMIT;
134  BEGIN;
135    CREATE TABLE r2(a, b, c);
136    INSERT INTO r2 SELECT * FROM e;
137    INSERT INTO e SELECT a||'x', b, c FROM r2;
138  COMMIT;
139}
140
141
142do_malloc_test vtab_err-2 -tclprep {
143  register_echo_module [sqlite3_connection_pointer db]
144} -sqlbody {
145  BEGIN;
146  CREATE TABLE r(a PRIMARY KEY, b, c);
147  CREATE VIRTUAL TABLE e USING echo(r);
148  INSERT INTO e VALUES(1, 2, 3);
149  INSERT INTO e VALUES('a', 'b', 'c');
150  UPDATE e SET c = 10;
151  DELETE FROM e WHERE a = 'a';
152  COMMIT;
153  BEGIN;
154    CREATE TABLE r2(a, b, c);
155    INSERT INTO r2 SELECT * FROM e;
156    INSERT INTO e SELECT a||'x', b, c FROM r2;
157  COMMIT;
158}
159
160sqlite_malloc_fail 0
161finish_test
162