4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
12 ** This file implements a simple standalone program used to test whether
13 ** or not the SQLite library is threadsafe.
15 ** Testing the thread safety of SQLite is difficult because there are very
16 ** few places in the code that are even potentially unsafe, and those
17 ** places execute for very short periods of time. So even if the library
18 ** is compiled with its mutexes disabled, it is likely to work correctly
19 ** in a multi-threaded program most of the time.
21 ** This file is NOT part of the standard SQLite library. It is used for
35 static int verbose
= 0;
40 static void Exit(int rc
){
44 extern char *sqlite_mprintf(const char *zFormat
, ...);
45 extern char *sqlite_vmprintf(const char *zFormat
, va_list);
48 ** When a lock occurs, yield.
50 static int db_is_locked(void *NotUsed
, const char *zNotUsed
, int iNotUsed
){
52 if( verbose
) printf("BUSY %s\n", (char*)NotUsed
);
58 ** Used to accumulate query results by db_query()
61 const char *zFile
; /* Filename - used for error reporting */
62 int nElem
; /* Number of used entries in azElem[] */
63 int nAlloc
; /* Number of slots allocated for azElem[] */
64 char **azElem
; /* The result of the query */
68 ** The callback function for db_query
70 static int db_query_callback(
71 void *pUser
, /* Pointer to the QueryResult structure */
72 int nArg
, /* Number of columns in this result row */
73 char **azArg
, /* Text of data in all columns */
74 char **NotUsed
/* Names of the columns */
76 struct QueryResult
*pResult
= (struct QueryResult
*)pUser
;
78 if( pResult
->nElem
+ nArg
>= pResult
->nAlloc
){
79 if( pResult
->nAlloc
==0 ){
80 pResult
->nAlloc
= nArg
+1;
82 pResult
->nAlloc
= pResult
->nAlloc
*2 + nArg
+ 1;
84 pResult
->azElem
= reallocarray(pResult
->azElem
, pResult
->nAlloc
,
86 if( pResult
->azElem
==0 ){
87 fprintf(stdout
,"%s: malloc failed\n", pResult
->zFile
);
91 if( azArg
==0 ) return 0;
92 for(i
=0; i
<nArg
; i
++){
93 pResult
->azElem
[pResult
->nElem
++] =
94 sqlite_mprintf("%s",azArg
[i
] ? azArg
[i
] : "");
100 ** Execute a query against the database. NULL values are returned
101 ** as an empty string. The list is terminated by a single NULL pointer.
103 char **db_query(sqlite
*db
, const char *zFile
, const char *zFormat
, ...){
108 struct QueryResult sResult
;
109 va_start(ap
, zFormat
);
110 zSql
= sqlite_vmprintf(zFormat
, ap
);
112 memset(&sResult
, 0, sizeof(sResult
));
113 sResult
.zFile
= zFile
;
114 if( verbose
) printf("QUERY %s: %s\n", zFile
, zSql
);
115 rc
= sqlite_exec(db
, zSql
, db_query_callback
, &sResult
, &zErrMsg
);
116 if( rc
==SQLITE_SCHEMA
){
118 rc
= sqlite_exec(db
, zSql
, db_query_callback
, &sResult
, &zErrMsg
);
120 if( verbose
) printf("DONE %s %s\n", zFile
, zSql
);
122 fprintf(stdout
,"%s: query failed: %s - %s\n", zFile
, zSql
, zErrMsg
);
127 sqlite_freemem(zSql
);
128 if( sResult
.azElem
==0 ){
129 db_query_callback(&sResult
, 0, 0, 0);
131 sResult
.azElem
[sResult
.nElem
] = 0;
132 return sResult
.azElem
;
136 ** Execute an SQL statement.
138 void db_execute(sqlite
*db
, const char *zFile
, const char *zFormat
, ...){
143 va_start(ap
, zFormat
);
144 zSql
= sqlite_vmprintf(zFormat
, ap
);
146 if( verbose
) printf("EXEC %s: %s\n", zFile
, zSql
);
147 rc
= sqlite_exec(db
, zSql
, 0, 0, &zErrMsg
);
148 while( rc
==SQLITE_SCHEMA
){
150 rc
= sqlite_exec(db
, zSql
, 0, 0, &zErrMsg
);
152 if( verbose
) printf("DONE %s: %s\n", zFile
, zSql
);
154 fprintf(stdout
,"%s: command failed: %s - %s\n", zFile
, zSql
, zErrMsg
);
156 sqlite_freemem(zSql
);
159 sqlite_freemem(zSql
);
163 ** Free the results of a db_query() call.
165 void db_query_free(char **az
){
167 for(i
=0; az
[i
]; i
++){
168 sqlite_freemem(az
[i
]);
176 void db_check(const char *zFile
, const char *zMsg
, char **az
, ...){
181 for(i
=0; (z
= va_arg(ap
, char*))!=0; i
++){
182 if( az
[i
]==0 || strcmp(az
[i
],z
)!=0 ){
183 fprintf(stdout
,"%s: %s: bad result in column %d: %s\n",
184 zFile
, zMsg
, i
+1, az
[i
]);
193 pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
194 pthread_cond_t sig
= PTHREAD_COND_INITIALIZER
;
197 static void *worker_bee(void *pArg
){
198 const char *zFilename
= (char*)pArg
;
201 int t
= atoi(zFilename
);
205 pthread_mutex_lock(&lock
);
207 pthread_mutex_unlock(&lock
);
208 printf("%s: START\n", zFilename
);
210 for(cnt
=0; cnt
<10; cnt
++){
211 db
= sqlite_open(&zFilename
[2], 0, &azErr
);
213 fprintf(stdout
,"%s: can't open\n", zFilename
);
216 sqlite_busy_handler(db
, db_is_locked
, zFilename
);
217 db_execute(db
, zFilename
, "CREATE TABLE t%d(a,b,c);", t
);
218 for(i
=1; i
<=100; i
++){
219 db_execute(db
, zFilename
, "INSERT INTO t%d VALUES(%d,%d,%d);",
222 az
= db_query(db
, zFilename
, "SELECT count(*) FROM t%d", t
);
223 db_check(zFilename
, "tX size", az
, "100", 0);
224 az
= db_query(db
, zFilename
, "SELECT avg(b) FROM t%d", t
);
225 db_check(zFilename
, "tX avg", az
, "101", 0);
226 db_execute(db
, zFilename
, "DELETE FROM t%d WHERE a>50", t
);
227 az
= db_query(db
, zFilename
, "SELECT avg(b) FROM t%d", t
);
228 db_check(zFilename
, "tX avg2", az
, "51", 0);
229 for(i
=1; i
<=50; i
++){
231 az
= db_query(db
, zFilename
, "SELECT b, c FROM t%d WHERE a=%d", t
, i
);
232 sprintf(z1
, "%d", i
*2);
233 sprintf(z2
, "%d", i
*i
);
234 db_check(zFilename
, "readback", az
, z1
, z2
, 0);
236 db_execute(db
, zFilename
, "DROP TABLE t%d;", t
);
239 printf("%s: END\n", zFilename
);
240 /* unlink(zFilename); */
242 pthread_mutex_lock(&lock
);
245 pthread_cond_signal(&sig
);
247 pthread_mutex_unlock(&lock
);
251 int main(int argc
, char **argv
){
255 if( argc
>2 && strcmp(argv
[1], "-v")==0 ){
260 if( argc
<2 || (n
=atoi(argv
[1]))<1 ) n
= 10;
263 sprintf(zBuf
, "testdb-%d", (i
+1)/2);
267 zFile
= sqlite_mprintf("%d.testdb-%d", i
%2+1, (i
+2)/2);
269 pthread_create(&id
, 0, worker_bee
, (void*)zFile
);
272 pthread_mutex_lock(&lock
);
273 while( thread_cnt
>0 ){
274 pthread_cond_wait(&sig
, &lock
);
276 pthread_mutex_unlock(&lock
);
279 sprintf(zBuf
, "testdb-%d", (i
+1)/2);