1c713bdc7Sdrh /*
2c713bdc7Sdrh ** 2002 January 15
3c713bdc7Sdrh **
4c713bdc7Sdrh ** The author disclaims copyright to this source code. In place of
5c713bdc7Sdrh ** a legal notice, here is a blessing:
6c713bdc7Sdrh **
7c713bdc7Sdrh ** May you do good and not evil.
8c713bdc7Sdrh ** May you find forgiveness for yourself and forgive others.
9c713bdc7Sdrh ** May you share freely, never taking more than you give.
10c713bdc7Sdrh **
11c713bdc7Sdrh *************************************************************************
12c713bdc7Sdrh ** This file implements a simple standalone program used to test whether
13c713bdc7Sdrh ** or not the SQLite library is threadsafe.
14c713bdc7Sdrh **
15c713bdc7Sdrh ** Testing the thread safety of SQLite is difficult because there are very
16c713bdc7Sdrh ** few places in the code that are even potentially unsafe, and those
17c713bdc7Sdrh ** places execute for very short periods of time. So even if the library
18c713bdc7Sdrh ** is compiled with its mutexes disabled, it is likely to work correctly
19c713bdc7Sdrh ** in a multi-threaded program most of the time.
20c713bdc7Sdrh **
21c713bdc7Sdrh ** This file is NOT part of the standard SQLite library. It is used for
22c713bdc7Sdrh ** testing only.
23c713bdc7Sdrh */
24c713bdc7Sdrh #include "sqlite.h"
25c713bdc7Sdrh #include <pthread.h>
26c713bdc7Sdrh #include <sched.h>
27c713bdc7Sdrh #include <stdio.h>
28c713bdc7Sdrh #include <stdlib.h>
29c713bdc7Sdrh #include <string.h>
30c713bdc7Sdrh #include <unistd.h>
31c713bdc7Sdrh
32c713bdc7Sdrh /*
33c713bdc7Sdrh ** Enable for tracing
34c713bdc7Sdrh */
35c713bdc7Sdrh static int verbose = 0;
36c713bdc7Sdrh
37c713bdc7Sdrh /*
38c713bdc7Sdrh ** Come here to die.
39c713bdc7Sdrh */
Exit(int rc)40c713bdc7Sdrh static void Exit(int rc){
41c713bdc7Sdrh exit(rc);
42c713bdc7Sdrh }
43c713bdc7Sdrh
445fdae771Sdrh extern char *sqlite3_mprintf(const char *zFormat, ...);
455fdae771Sdrh extern char *sqlite3_vmprintf(const char *zFormat, va_list);
46c713bdc7Sdrh
47c713bdc7Sdrh /*
48c713bdc7Sdrh ** When a lock occurs, yield.
49c713bdc7Sdrh */
db_is_locked(void * NotUsed,int iCount)505fdae771Sdrh static int db_is_locked(void *NotUsed, int iCount){
51c713bdc7Sdrh /* sched_yield(); */
525fdae771Sdrh if( verbose ) printf("BUSY %s #%d\n", (char*)NotUsed, iCount);
53c713bdc7Sdrh usleep(100);
545fdae771Sdrh return iCount<25;
55c713bdc7Sdrh }
56c713bdc7Sdrh
57c713bdc7Sdrh /*
58c713bdc7Sdrh ** Used to accumulate query results by db_query()
59c713bdc7Sdrh */
60c713bdc7Sdrh struct QueryResult {
61c713bdc7Sdrh const char *zFile; /* Filename - used for error reporting */
62c713bdc7Sdrh int nElem; /* Number of used entries in azElem[] */
63c713bdc7Sdrh int nAlloc; /* Number of slots allocated for azElem[] */
64c713bdc7Sdrh char **azElem; /* The result of the query */
65c713bdc7Sdrh };
66c713bdc7Sdrh
67c713bdc7Sdrh /*
68c713bdc7Sdrh ** The callback function for db_query
69c713bdc7Sdrh */
db_query_callback(void * pUser,int nArg,char ** azArg,char ** NotUsed)70c713bdc7Sdrh static int db_query_callback(
71c713bdc7Sdrh void *pUser, /* Pointer to the QueryResult structure */
72c713bdc7Sdrh int nArg, /* Number of columns in this result row */
73c713bdc7Sdrh char **azArg, /* Text of data in all columns */
74c713bdc7Sdrh char **NotUsed /* Names of the columns */
75c713bdc7Sdrh ){
76c713bdc7Sdrh struct QueryResult *pResult = (struct QueryResult*)pUser;
77c713bdc7Sdrh int i;
78c713bdc7Sdrh if( pResult->nElem + nArg >= pResult->nAlloc ){
79c713bdc7Sdrh if( pResult->nAlloc==0 ){
80c713bdc7Sdrh pResult->nAlloc = nArg+1;
81c713bdc7Sdrh }else{
82c713bdc7Sdrh pResult->nAlloc = pResult->nAlloc*2 + nArg + 1;
83c713bdc7Sdrh }
84c713bdc7Sdrh pResult->azElem = realloc( pResult->azElem, pResult->nAlloc*sizeof(char*));
85c713bdc7Sdrh if( pResult->azElem==0 ){
86c713bdc7Sdrh fprintf(stdout,"%s: malloc failed\n", pResult->zFile);
87c713bdc7Sdrh return 1;
88c713bdc7Sdrh }
89c713bdc7Sdrh }
90c713bdc7Sdrh if( azArg==0 ) return 0;
91c713bdc7Sdrh for(i=0; i<nArg; i++){
92c713bdc7Sdrh pResult->azElem[pResult->nElem++] =
935fdae771Sdrh sqlite3_mprintf("%s",azArg[i] ? azArg[i] : "");
94c713bdc7Sdrh }
95c713bdc7Sdrh return 0;
96c713bdc7Sdrh }
97c713bdc7Sdrh
98c713bdc7Sdrh /*
99c713bdc7Sdrh ** Execute a query against the database. NULL values are returned
100c713bdc7Sdrh ** as an empty string. The list is terminated by a single NULL pointer.
101c713bdc7Sdrh */
db_query(sqlite * db,const char * zFile,const char * zFormat,...)102c713bdc7Sdrh char **db_query(sqlite *db, const char *zFile, const char *zFormat, ...){
103c713bdc7Sdrh char *zSql;
104c713bdc7Sdrh int rc;
105c713bdc7Sdrh char *zErrMsg = 0;
106c713bdc7Sdrh va_list ap;
107c713bdc7Sdrh struct QueryResult sResult;
108c713bdc7Sdrh va_start(ap, zFormat);
1095fdae771Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
110c713bdc7Sdrh va_end(ap);
111c713bdc7Sdrh memset(&sResult, 0, sizeof(sResult));
112c713bdc7Sdrh sResult.zFile = zFile;
113c713bdc7Sdrh if( verbose ) printf("QUERY %s: %s\n", zFile, zSql);
1145fdae771Sdrh rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
115c713bdc7Sdrh if( rc==SQLITE_SCHEMA ){
116c713bdc7Sdrh if( zErrMsg ) free(zErrMsg);
1175fdae771Sdrh rc = sqlite3_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
118c713bdc7Sdrh }
119c713bdc7Sdrh if( verbose ) printf("DONE %s %s\n", zFile, zSql);
120c713bdc7Sdrh if( zErrMsg ){
121c713bdc7Sdrh fprintf(stdout,"%s: query failed: %s - %s\n", zFile, zSql, zErrMsg);
122c713bdc7Sdrh free(zErrMsg);
123c713bdc7Sdrh free(zSql);
124c713bdc7Sdrh Exit(1);
125c713bdc7Sdrh }
1265fdae771Sdrh sqlite3_free(zSql);
127c713bdc7Sdrh if( sResult.azElem==0 ){
128c713bdc7Sdrh db_query_callback(&sResult, 0, 0, 0);
129c713bdc7Sdrh }
130c713bdc7Sdrh sResult.azElem[sResult.nElem] = 0;
131c713bdc7Sdrh return sResult.azElem;
132c713bdc7Sdrh }
133c713bdc7Sdrh
134c713bdc7Sdrh /*
135c713bdc7Sdrh ** Execute an SQL statement.
136c713bdc7Sdrh */
db_execute(sqlite * db,const char * zFile,const char * zFormat,...)137c713bdc7Sdrh void db_execute(sqlite *db, const char *zFile, const char *zFormat, ...){
138c713bdc7Sdrh char *zSql;
139c713bdc7Sdrh int rc;
140c713bdc7Sdrh char *zErrMsg = 0;
141c713bdc7Sdrh va_list ap;
142c713bdc7Sdrh va_start(ap, zFormat);
1435fdae771Sdrh zSql = sqlite3_vmprintf(zFormat, ap);
144c713bdc7Sdrh va_end(ap);
145c713bdc7Sdrh if( verbose ) printf("EXEC %s: %s\n", zFile, zSql);
1465fdae771Sdrh do{
1475fdae771Sdrh rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
1485fdae771Sdrh }while( rc==SQLITE_BUSY );
149c713bdc7Sdrh if( verbose ) printf("DONE %s: %s\n", zFile, zSql);
150c713bdc7Sdrh if( zErrMsg ){
151c713bdc7Sdrh fprintf(stdout,"%s: command failed: %s - %s\n", zFile, zSql, zErrMsg);
152c713bdc7Sdrh free(zErrMsg);
1535fdae771Sdrh sqlite3_free(zSql);
154c713bdc7Sdrh Exit(1);
155c713bdc7Sdrh }
1565fdae771Sdrh sqlite3_free(zSql);
157c713bdc7Sdrh }
158c713bdc7Sdrh
159c713bdc7Sdrh /*
160c713bdc7Sdrh ** Free the results of a db_query() call.
161c713bdc7Sdrh */
db_query_free(char ** az)162c713bdc7Sdrh void db_query_free(char **az){
163c713bdc7Sdrh int i;
164c713bdc7Sdrh for(i=0; az[i]; i++){
1655fdae771Sdrh sqlite3_free(az[i]);
166c713bdc7Sdrh }
167c713bdc7Sdrh free(az);
168c713bdc7Sdrh }
169c713bdc7Sdrh
170c713bdc7Sdrh /*
171c713bdc7Sdrh ** Check results
172c713bdc7Sdrh */
db_check(const char * zFile,const char * zMsg,char ** az,...)173c713bdc7Sdrh void db_check(const char *zFile, const char *zMsg, char **az, ...){
174c713bdc7Sdrh va_list ap;
175c713bdc7Sdrh int i;
176c713bdc7Sdrh char *z;
177c713bdc7Sdrh va_start(ap, az);
178c713bdc7Sdrh for(i=0; (z = va_arg(ap, char*))!=0; i++){
179c713bdc7Sdrh if( az[i]==0 || strcmp(az[i],z)!=0 ){
180c713bdc7Sdrh fprintf(stdout,"%s: %s: bad result in column %d: %s\n",
181c713bdc7Sdrh zFile, zMsg, i+1, az[i]);
182c713bdc7Sdrh db_query_free(az);
183c713bdc7Sdrh Exit(1);
184c713bdc7Sdrh }
185c713bdc7Sdrh }
186c713bdc7Sdrh va_end(ap);
187c713bdc7Sdrh db_query_free(az);
188c713bdc7Sdrh }
189c713bdc7Sdrh
190c713bdc7Sdrh pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
191c713bdc7Sdrh pthread_cond_t sig = PTHREAD_COND_INITIALIZER;
192c713bdc7Sdrh int thread_cnt = 0;
193c713bdc7Sdrh
worker_bee(void * pArg)194c713bdc7Sdrh static void *worker_bee(void *pArg){
195c713bdc7Sdrh const char *zFilename = (char*)pArg;
196c713bdc7Sdrh char *azErr;
197c713bdc7Sdrh int i, cnt;
198c713bdc7Sdrh int t = atoi(zFilename);
199c713bdc7Sdrh char **az;
200c713bdc7Sdrh sqlite *db;
201c713bdc7Sdrh
202c713bdc7Sdrh pthread_mutex_lock(&lock);
203c713bdc7Sdrh thread_cnt++;
204c713bdc7Sdrh pthread_mutex_unlock(&lock);
205c713bdc7Sdrh printf("%s: START\n", zFilename);
206c713bdc7Sdrh fflush(stdout);
207c713bdc7Sdrh for(cnt=0; cnt<10; cnt++){
2085fdae771Sdrh sqlite3_open(&zFilename[2], &db);
209c713bdc7Sdrh if( db==0 ){
210c713bdc7Sdrh fprintf(stdout,"%s: can't open\n", zFilename);
211c713bdc7Sdrh Exit(1);
212c713bdc7Sdrh }
2135fdae771Sdrh sqlite3_busy_handler(db, db_is_locked, zFilename);
214c713bdc7Sdrh db_execute(db, zFilename, "CREATE TABLE t%d(a,b,c);", t);
215c713bdc7Sdrh for(i=1; i<=100; i++){
216c713bdc7Sdrh db_execute(db, zFilename, "INSERT INTO t%d VALUES(%d,%d,%d);",
217c713bdc7Sdrh t, i, i*2, i*i);
218c713bdc7Sdrh }
219c713bdc7Sdrh az = db_query(db, zFilename, "SELECT count(*) FROM t%d", t);
220c713bdc7Sdrh db_check(zFilename, "tX size", az, "100", 0);
221c713bdc7Sdrh az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
222c713bdc7Sdrh db_check(zFilename, "tX avg", az, "101", 0);
223c713bdc7Sdrh db_execute(db, zFilename, "DELETE FROM t%d WHERE a>50", t);
224c713bdc7Sdrh az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
225c713bdc7Sdrh db_check(zFilename, "tX avg2", az, "51", 0);
226c713bdc7Sdrh for(i=1; i<=50; i++){
227c713bdc7Sdrh char z1[30], z2[30];
228c713bdc7Sdrh az = db_query(db, zFilename, "SELECT b, c FROM t%d WHERE a=%d", t, i);
229c713bdc7Sdrh sprintf(z1, "%d", i*2);
230c713bdc7Sdrh sprintf(z2, "%d", i*i);
231c713bdc7Sdrh db_check(zFilename, "readback", az, z1, z2, 0);
232c713bdc7Sdrh }
233c713bdc7Sdrh db_execute(db, zFilename, "DROP TABLE t%d;", t);
2345fdae771Sdrh sqlite3_close(db);
235c713bdc7Sdrh }
236c713bdc7Sdrh printf("%s: END\n", zFilename);
237c713bdc7Sdrh /* unlink(zFilename); */
238c713bdc7Sdrh fflush(stdout);
239c713bdc7Sdrh pthread_mutex_lock(&lock);
240c713bdc7Sdrh thread_cnt--;
241c713bdc7Sdrh if( thread_cnt<=0 ){
242c713bdc7Sdrh pthread_cond_signal(&sig);
243c713bdc7Sdrh }
244c713bdc7Sdrh pthread_mutex_unlock(&lock);
245c713bdc7Sdrh return 0;
246c713bdc7Sdrh }
247c713bdc7Sdrh
main(int argc,char ** argv)248c713bdc7Sdrh int main(int argc, char **argv){
249c713bdc7Sdrh char *zFile;
250c713bdc7Sdrh int i, n;
251c713bdc7Sdrh pthread_t id;
252c713bdc7Sdrh if( argc>2 && strcmp(argv[1], "-v")==0 ){
253c713bdc7Sdrh verbose = 1;
254c713bdc7Sdrh argc--;
255c713bdc7Sdrh argv++;
256c713bdc7Sdrh }
257c713bdc7Sdrh if( argc<2 || (n=atoi(argv[1]))<1 ) n = 10;
258c713bdc7Sdrh for(i=0; i<n; i++){
259c713bdc7Sdrh char zBuf[200];
260c713bdc7Sdrh sprintf(zBuf, "testdb-%d", (i+1)/2);
261c713bdc7Sdrh unlink(zBuf);
262c713bdc7Sdrh }
263c713bdc7Sdrh for(i=0; i<n; i++){
2645fdae771Sdrh zFile = sqlite3_mprintf("%d.testdb-%d", i%2+1, (i+2)/2);
265*2b444853Sdanielk1977 if( (i%2)==0 ){
266*2b444853Sdanielk1977 /* Remove both the database file and any old journal for the file
267*2b444853Sdanielk1977 ** being used by this thread and the next one. */
268*2b444853Sdanielk1977 char *zDb = &zFile[2];
269*2b444853Sdanielk1977 char *zJournal = sqlite3_mprintf("%s-journal", zDb);
270*2b444853Sdanielk1977 unlink(zDb);
271*2b444853Sdanielk1977 unlink(zJournal);
272*2b444853Sdanielk1977 free(zJournal);
273*2b444853Sdanielk1977 }
274*2b444853Sdanielk1977
275c713bdc7Sdrh pthread_create(&id, 0, worker_bee, (void*)zFile);
276c713bdc7Sdrh pthread_detach(id);
277c713bdc7Sdrh }
278c713bdc7Sdrh pthread_mutex_lock(&lock);
279c713bdc7Sdrh while( thread_cnt>0 ){
280c713bdc7Sdrh pthread_cond_wait(&sig, &lock);
281c713bdc7Sdrh }
282c713bdc7Sdrh pthread_mutex_unlock(&lock);
283c713bdc7Sdrh for(i=0; i<n; i++){
284c713bdc7Sdrh char zBuf[200];
285c713bdc7Sdrh sprintf(zBuf, "testdb-%d", (i+1)/2);
286c713bdc7Sdrh unlink(zBuf);
287c713bdc7Sdrh }
288c713bdc7Sdrh return 0;
289c713bdc7Sdrh }
290