xref: /sqlite-3.40.0/test/thread001.test (revision d9b5b117)
1# 2007 September 7
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: thread001.test,v 1.3 2007/09/10 06:23:54 danielk1977 Exp $
13
14set testdir [file dirname $argv0]
15source $testdir/tester.tcl
16
17if {[info commands sqlthread] eq ""} {
18  puts -nonewline "Skipping thread-safety tests - "
19  puts            " not running a threadsafe sqlite/tcl build"
20  puts -nonewline "Both SQLITE_THREADSAFE and TCL_THREADS must be defined when"
21  puts            " building testfixture"
22  finish_test
23  return
24}
25
26set ::NTHREAD 10
27
28# The following script is sourced by every thread spawned using
29# [sqlthread spawn]:
30set thread_procs {
31
32  # Execute the supplied SQL using database handle $::DB.
33  #
34  proc execsql {sql} {
35    set res [list]
36    set ::STMT [sqlite3_prepare $::DB $sql -1 dummy_tail]
37    while {[sqlite3_step $::STMT] eq "SQLITE_ROW"} {
38      for {set i 0} {$i < [sqlite3_column_count $::STMT]} {incr i} {
39        lappend res [sqlite3_column_text $::STMT 0]
40      }
41    }
42    set rc [sqlite3_finalize $::STMT]
43    if {$rc ne "SQLITE_OK"} {
44      error [sqlite3_errmsg $::DB]
45    }
46    set res
47  }
48
49  proc do_test {name script result} {
50    set res [eval $script]
51    if {$res ne $result} {
52      error "$name failed: expected \"$result\" got \"$res\""
53    }
54  }
55}
56
57proc thread_spawn {varname args} {
58  sqlthread spawn $varname [join $args ;]
59}
60
61#########################################################################
62# End of infrastruture. Start of test cases.
63#########################################################################
64
65
66# Run this test twice: Once with all threads using the same database
67# connection, and once with each using it's own connection.
68#
69foreach {dbconfig tn} [list "set ::DB $::DB" 1 "" 2] {
70
71  # Empty the database.
72  #
73  catchsql { DROP TABLE ab; }
74
75  # Set up a database and a schema. The database contains a single
76  # table with two columns. The first column ("a") is an INTEGER PRIMARY
77  # KEY. The second contains the md5sum of all rows in the table with
78  # a smaller value stored in column "a".
79  #
80  do_test thread001.$tn.1 {
81    execsql {
82      CREATE TABLE ab(a INTEGER PRIMARY KEY, b);
83      CREATE INDEX ab_i ON ab(b);
84      INSERT INTO ab SELECT NULL, md5sum(a, b) FROM ab;
85      SELECT count(*) FROM ab;
86    }
87  } {1}
88  do_test thread001.$tn.2 {
89    execsql {
90      SELECT
91        (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
92        (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
93    }
94  } {1}
95  do_test thread001.$tn.3 {
96    execsql { PRAGMA integrity_check }
97  } {ok}
98
99  set thread_program {
100    set needToClose 0
101    if {![info exists ::DB]} {
102      set ::DB [sqlthread open test.db]
103      set needToClose 1
104    }
105
106    for {set i 0} {$i < 100} {incr i} {
107      # Test that the invariant is true.
108      do_test t1 {
109        execsql {
110          SELECT
111            (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
112            (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
113        }
114      } {1}
115
116      # Add another row to the database.
117      execsql { INSERT INTO ab SELECT NULL, md5sum(a, b) FROM ab }
118    }
119
120    if {$needToClose} {
121      sqlite3_close $::DB
122    }
123
124    list OK
125  }
126
127  # Kick off $::NTHREAD threads:
128  #
129  array unset finished
130  for {set i 0} {$i < $::NTHREAD} {incr i} {
131    thread_spawn finished($i) $dbconfig $thread_procs $thread_program
132  }
133
134  # Wait for all threads to finish,  then check they all returned "OK".
135  #
136  for {set i 0} {$i < $::NTHREAD} {incr i} {
137    if {![info exists finished($i)]} {
138      vwait finished($i)
139    }
140    do_test thread001.$tn.4.$i {
141      set ::finished($i)
142    } OK
143  }
144
145  # Check the database still looks Ok.
146  #
147  do_test thread001.$tn.5 {
148    execsql { SELECT count(*) FROM ab; }
149  } [expr {1 + $::NTHREAD*100}]
150  do_test thread001.$tn.6 {
151    execsql {
152      SELECT
153        (SELECT md5sum(a, b) FROM ab WHERE a < (SELECT max(a) FROM ab)) ==
154        (SELECT b FROM ab WHERE a = (SELECT max(a) FROM ab))
155    }
156  } {1}
157  do_test thread001.$tn.7 {
158    execsql { PRAGMA integrity_check }
159  } {ok}
160}
161
162finish_test
163
164