AryaWu/sqlite
0
1/*2** 2002 January 153**4** The author disclaims copyright to this source code. In place of5** a legal notice, here is a blessing:6**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.10**11*************************************************************************12** This file implements a simple standalone program used to test whether13** or not the SQLite library is threadsafe.14**15** Testing the thread safety of SQLite is difficult because there are very16** few places in the code that are even potentially unsafe, and those17** places execute for very short periods of time. So even if the library18** is compiled with its mutexes disabled, it is likely to work correctly19** in a multi-threaded program most of the time. 20**21** This file is NOT part of the standard SQLite library. It is used for22** testing only.23*/24#include "sqlite.h"25#include <pthread.h>26#include <sched.h>27#include <stdio.h>28#include <stdlib.h>29#include <string.h>30#include <unistd.h>31 32/*33** Enable for tracing34*/35static int verbose = 0;36 37/*38** Come here to die.39*/40static void Exit(int rc){41 exit(rc);42}43 44extern char *sqlite3_mprintf(const char *zFormat, ...);45extern char *sqlite3_vmprintf(const char *zFormat, va_list);46 47/*48** When a lock occurs, yield.49*/50static int db_is_locked(void *NotUsed, int iCount){51 /* sched_yield(); */52 if( verbose ) printf("BUSY %s #%d\n", (char*)NotUsed, iCount);53 usleep(100);54 return iCount<25;55}56 57/*58** Used to accumulate query results by db_query()59*/60struct QueryResult {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 */65};66 67/*68** The callback function for db_query69*/70static 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 */75){76 struct QueryResult *pResult = (struct QueryResult*)pUser;77 int i;78 if( pResult->nElem + nArg >= pResult->nAlloc ){79 if( pResult->nAlloc==0 ){80 pResult->nAlloc = nArg+1;81 }else{82 pResult->nAlloc = pResult->nAlloc*2 + nArg + 1;83 }84 pResult->azElem = realloc( pResult->azElem, pResult->nAlloc*sizeof(char*));85 if( pResult->azElem==0 ){86 fprintf(stdout,"%s: malloc failed\n", pResult->zFile);87 return 1;88 }89 }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] : ""); 94 }95 return 0;96}97 98/*99** Execute a query against the database. NULL values are returned100** as an empty string. The list is terminated by a single NULL pointer.101*/102char **db_query(sqlite *db, const char *zFile, const char *zFormat, ...){103 char *zSql;104 int rc;105 char *zErrMsg = 0;106 va_list ap;107 struct QueryResult sResult;108 va_start(ap, zFormat);109 zSql = sqlite3_vmprintf(zFormat, ap);110 va_end(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);118 }119 if( verbose ) printf("DONE %s %s\n", zFile, zSql);120 if( zErrMsg ){121 fprintf(stdout,"%s: query failed: %s - %s\n", zFile, zSql, zErrMsg);122 free(zErrMsg);123 free(zSql);124 Exit(1);125 }126 sqlite3_free(zSql);127 if( sResult.azElem==0 ){128 db_query_callback(&sResult, 0, 0, 0);129 }130 sResult.azElem[sResult.nElem] = 0;131 return sResult.azElem;132}133 134/*135** Execute an SQL statement.136*/137void db_execute(sqlite *db, const char *zFile, const char *zFormat, ...){138 char *zSql;139 int rc;140 char *zErrMsg = 0;141 va_list ap;142 va_start(ap, zFormat);143 zSql = sqlite3_vmprintf(zFormat, ap);144 va_end(ap);145 if( verbose ) printf("EXEC %s: %s\n", zFile, zSql);146 do{147 rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);148 }while( rc==SQLITE_BUSY );149 if( verbose ) printf("DONE %s: %s\n", zFile, zSql);150 if( zErrMsg ){151 fprintf(stdout,"%s: command failed: %s - %s\n", zFile, zSql, zErrMsg);152 free(zErrMsg);153 sqlite3_free(zSql);154 Exit(1);155 }156 sqlite3_free(zSql);157}158 159/*160** Free the results of a db_query() call.161*/162void db_query_free(char **az){163 int i;164 for(i=0; az[i]; i++){165 sqlite3_free(az[i]);166 }167 free(az);168}169 170/*171** Check results172*/173void db_check(const char *zFile, const char *zMsg, char **az, ...){174 va_list ap;175 int i;176 char *z;177 va_start(ap, 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]);182 db_query_free(az);183 Exit(1);184 }185 }186 va_end(ap);187 db_query_free(az);188}189 190pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;191pthread_cond_t sig = PTHREAD_COND_INITIALIZER;192int thread_cnt = 0;193 194static void *worker_bee(void *pArg){195 const char *zFilename = (char*)pArg;196 char *azErr;197 int i, cnt;198 int t = atoi(zFilename);199 char **az;200 sqlite *db;201 202 pthread_mutex_lock(&lock);203 thread_cnt++;204 pthread_mutex_unlock(&lock);205 printf("%s: START\n", zFilename);206 fflush(stdout);207 for(cnt=0; cnt<10; cnt++){208 sqlite3_open(&zFilename[2], &db);209 if( db==0 ){210 fprintf(stdout,"%s: can't open\n", zFilename);211 Exit(1);212 }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);",217 t, i, i*2, i*i);218 }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++){227 char z1[30], z2[30];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);232 }233 db_execute(db, zFilename, "DROP TABLE t%d;", t);234 sqlite3_close(db);235 }236 printf("%s: END\n", zFilename);237 /* unlink(zFilename); */238 fflush(stdout);239 pthread_mutex_lock(&lock);240 thread_cnt--;241 if( thread_cnt<=0 ){242 pthread_cond_signal(&sig);243 }244 pthread_mutex_unlock(&lock);245 return 0;246}247 248int main(int argc, char **argv){249 char *zFile;250 int i, n;251 pthread_t id;252 if( argc>2 && strcmp(argv[1], "-v")==0 ){253 verbose = 1;254 argc--;255 argv++;256 }257 if( argc<2 || (n=atoi(argv[1]))<1 ) n = 10;258 for(i=0; i<n; i++){259 char zBuf[200];260 sprintf(zBuf, "testdb-%d", (i+1)/2);261 unlink(zBuf);262 }263 for(i=0; i<n; i++){264 zFile = sqlite3_mprintf("%d.testdb-%d", i%2+1, (i+2)/2);265 if( (i%2)==0 ){266 /* Remove both the database file and any old journal for the file267 ** being used by this thread and the next one. */268 char *zDb = &zFile[2];269 char *zJournal = sqlite3_mprintf("%s-journal", zDb);270 unlink(zDb);271 unlink(zJournal);272 free(zJournal);273 }274 275 pthread_create(&id, 0, worker_bee, (void*)zFile);276 pthread_detach(id);277 }278 pthread_mutex_lock(&lock);279 while( thread_cnt>0 ){280 pthread_cond_wait(&sig, &lock);281 }282 pthread_mutex_unlock(&lock);283 for(i=0; i<n; i++){284 char zBuf[200];285 sprintf(zBuf, "testdb-%d", (i+1)/2);286 unlink(zBuf);287 }288 return 0;289}290 