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