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 *sqlite3_mprintf(const char *zFormat
, ...);
45 extern char *sqlite3_vmprintf(const char *zFormat
, va_list);
48 ** When a lock occurs, yield.
50 static int db_is_locked(void *NotUsed
, int iCount
){
52 if( verbose
) printf("BUSY %s #%d\n", (char*)NotUsed
, iCount
);
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
= realloc( pResult
->azElem
, pResult
->nAlloc
*sizeof(char*));
85 if( pResult
->azElem
==0 ){
86 fprintf(stdout
,"%s: malloc failed\n", pResult
->zFile
);
90 if( azArg
==0 ) return 0;
91 for(i
=0; i
<nArg
; i
++){
92 pResult
->azElem
[pResult
->nElem
++] =
93 sqlite3_mprintf("%s",azArg
[i
] ? azArg
[i
] : "");
99 ** Execute a query against the database. NULL values are returned
100 ** as an empty string. The list is terminated by a single NULL pointer.
102 char **db_query(sqlite
*db
, const char *zFile
, const char *zFormat
, ...){
107 struct QueryResult sResult
;
108 va_start(ap
, zFormat
);
109 zSql
= sqlite3_vmprintf(zFormat
, ap
);
111 memset(&sResult
, 0, sizeof(sResult
));
112 sResult
.zFile
= zFile
;
113 if( verbose
) printf("QUERY %s: %s\n", zFile
, zSql
);
114 rc
= sqlite3_exec(db
, zSql
, db_query_callback
, &sResult
, &zErrMsg
);
115 if( rc
==SQLITE_SCHEMA
){
116 if( zErrMsg
) free(zErrMsg
);
117 rc
= sqlite3_exec(db
, zSql
, db_query_callback
, &sResult
, &zErrMsg
);
119 if( verbose
) printf("DONE %s %s\n", zFile
, zSql
);
121 fprintf(stdout
,"%s: query failed: %s - %s\n", zFile
, zSql
, zErrMsg
);
127 if( sResult
.azElem
==0 ){
128 db_query_callback(&sResult
, 0, 0, 0);
130 sResult
.azElem
[sResult
.nElem
] = 0;
131 return sResult
.azElem
;
135 ** Execute an SQL statement.
137 void db_execute(sqlite
*db
, const char *zFile
, const char *zFormat
, ...){
142 va_start(ap
, zFormat
);
143 zSql
= sqlite3_vmprintf(zFormat
, ap
);
145 if( verbose
) printf("EXEC %s: %s\n", zFile
, zSql
);
147 rc
= sqlite3_exec(db
, zSql
, 0, 0, &zErrMsg
);
148 }while( rc
==SQLITE_BUSY
);
149 if( verbose
) printf("DONE %s: %s\n", zFile
, zSql
);
151 fprintf(stdout
,"%s: command failed: %s - %s\n", zFile
, zSql
, zErrMsg
);
160 ** Free the results of a db_query() call.
162 void db_query_free(char **az
){
164 for(i
=0; az
[i
]; i
++){
173 void db_check(const char *zFile
, const char *zMsg
, char **az
, ...){
178 for(i
=0; (z
= va_arg(ap
, char*))!=0; i
++){
179 if( az
[i
]==0 || strcmp(az
[i
],z
)!=0 ){
180 fprintf(stdout
,"%s: %s: bad result in column %d: %s\n",
181 zFile
, zMsg
, i
+1, az
[i
]);
190 pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
191 pthread_cond_t sig
= PTHREAD_COND_INITIALIZER
;
194 static void *worker_bee(void *pArg
){
195 const char *zFilename
= (char*)pArg
;
198 int t
= atoi(zFilename
);
202 pthread_mutex_lock(&lock
);
204 pthread_mutex_unlock(&lock
);
205 printf("%s: START\n", zFilename
);
207 for(cnt
=0; cnt
<10; cnt
++){
208 sqlite3_open(&zFilename
[2], &db
);
210 fprintf(stdout
,"%s: can't open\n", zFilename
);
213 sqlite3_busy_handler(db
, db_is_locked
, zFilename
);
214 db_execute(db
, zFilename
, "CREATE TABLE t%d(a,b,c);", t
);
215 for(i
=1; i
<=100; i
++){
216 db_execute(db
, zFilename
, "INSERT INTO t%d VALUES(%d,%d,%d);",
219 az
= db_query(db
, zFilename
, "SELECT count(*) FROM t%d", t
);
220 db_check(zFilename
, "tX size", az
, "100", 0);
221 az
= db_query(db
, zFilename
, "SELECT avg(b) FROM t%d", t
);
222 db_check(zFilename
, "tX avg", az
, "101", 0);
223 db_execute(db
, zFilename
, "DELETE FROM t%d WHERE a>50", t
);
224 az
= db_query(db
, zFilename
, "SELECT avg(b) FROM t%d", t
);
225 db_check(zFilename
, "tX avg2", az
, "51", 0);
226 for(i
=1; i
<=50; i
++){
228 az
= db_query(db
, zFilename
, "SELECT b, c FROM t%d WHERE a=%d", t
, i
);
229 sprintf(z1
, "%d", i
*2);
230 sprintf(z2
, "%d", i
*i
);
231 db_check(zFilename
, "readback", az
, z1
, z2
, 0);
233 db_execute(db
, zFilename
, "DROP TABLE t%d;", t
);
236 printf("%s: END\n", zFilename
);
237 /* unlink(zFilename); */
239 pthread_mutex_lock(&lock
);
242 pthread_cond_signal(&sig
);
244 pthread_mutex_unlock(&lock
);
248 int main(int argc
, char **argv
){
252 if( argc
>2 && strcmp(argv
[1], "-v")==0 ){
257 if( argc
<2 || (n
=atoi(argv
[1]))<1 ) n
= 10;
260 sprintf(zBuf
, "testdb-%d", (i
+1)/2);
264 zFile
= sqlite3_mprintf("%d.testdb-%d", i
%2+1, (i
+2)/2);
266 /* Remove both the database file and any old journal for the file
267 ** being used by this thread and the next one. */
268 char *zDb
= &zFile
[2];
269 char *zJournal
= sqlite3_mprintf("%s-journal", zDb
);
275 pthread_create(&id
, 0, worker_bee
, (void*)zFile
);
278 pthread_mutex_lock(&lock
);
279 while( thread_cnt
>0 ){
280 pthread_cond_wait(&sig
, &lock
);
282 pthread_mutex_unlock(&lock
);
285 sprintf(zBuf
, "testdb-%d", (i
+1)/2);