17c478bdstevel@tonic-gate
27c478bdstevel@tonic-gate#pragma ident	"%Z%%M%	%I%	%E% SMI"
37c478bdstevel@tonic-gate
47c478bdstevel@tonic-gate/*
57c478bdstevel@tonic-gate** 2002 January 15
67c478bdstevel@tonic-gate**
77c478bdstevel@tonic-gate** The author disclaims copyright to this source code.  In place of
87c478bdstevel@tonic-gate** a legal notice, here is a blessing:
97c478bdstevel@tonic-gate**
107c478bdstevel@tonic-gate**    May you do good and not evil.
117c478bdstevel@tonic-gate**    May you find forgiveness for yourself and forgive others.
127c478bdstevel@tonic-gate**    May you share freely, never taking more than you give.
137c478bdstevel@tonic-gate**
147c478bdstevel@tonic-gate*************************************************************************
157c478bdstevel@tonic-gate** This file implements a simple standalone program used to test whether
167c478bdstevel@tonic-gate** or not the SQLite library is threadsafe.
177c478bdstevel@tonic-gate**
187c478bdstevel@tonic-gate** Testing the thread safety of SQLite is difficult because there are very
197c478bdstevel@tonic-gate** few places in the code that are even potentially unsafe, and those
207c478bdstevel@tonic-gate** places execute for very short periods of time.  So even if the library
217c478bdstevel@tonic-gate** is compiled with its mutexes disabled, it is likely to work correctly
227c478bdstevel@tonic-gate** in a multi-threaded program most of the time.
237c478bdstevel@tonic-gate**
247c478bdstevel@tonic-gate** This file is NOT part of the standard SQLite library.  It is used for
257c478bdstevel@tonic-gate** testing only.
267c478bdstevel@tonic-gate*/
277c478bdstevel@tonic-gate#include "sqlite.h"
287c478bdstevel@tonic-gate#include <pthread.h>
297c478bdstevel@tonic-gate#include <sched.h>
307c478bdstevel@tonic-gate#include <stdio.h>
317c478bdstevel@tonic-gate#include <stdlib.h>
327c478bdstevel@tonic-gate#include <string.h>
337c478bdstevel@tonic-gate#include <unistd.h>
347c478bdstevel@tonic-gate
357c478bdstevel@tonic-gate/*
367c478bdstevel@tonic-gate** Enable for tracing
377c478bdstevel@tonic-gate*/
387c478bdstevel@tonic-gatestatic int verbose = 0;
397c478bdstevel@tonic-gate
407c478bdstevel@tonic-gate/*
417c478bdstevel@tonic-gate** Come here to die.
427c478bdstevel@tonic-gate*/
437c478bdstevel@tonic-gatestatic void Exit(int rc){
447c478bdstevel@tonic-gate  exit(rc);
457c478bdstevel@tonic-gate}
467c478bdstevel@tonic-gate
477c478bdstevel@tonic-gateextern char *sqlite_mprintf(const char *zFormat, ...);
487c478bdstevel@tonic-gateextern char *sqlite_vmprintf(const char *zFormat, va_list);
497c478bdstevel@tonic-gate
507c478bdstevel@tonic-gate/*
517c478bdstevel@tonic-gate** When a lock occurs, yield.
527c478bdstevel@tonic-gate*/
537c478bdstevel@tonic-gatestatic int db_is_locked(void *NotUsed, const char *zNotUsed, int iNotUsed){
547c478bdstevel@tonic-gate  /* sched_yield(); */
557c478bdstevel@tonic-gate  if( verbose ) printf("BUSY %s\n", (char*)NotUsed);
567c478bdstevel@tonic-gate  usleep(100);
577c478bdstevel@tonic-gate  return 1;
587c478bdstevel@tonic-gate}
597c478bdstevel@tonic-gate
607c478bdstevel@tonic-gate/*
617c478bdstevel@tonic-gate** Used to accumulate query results by db_query()
627c478bdstevel@tonic-gate*/
637c478bdstevel@tonic-gatestruct QueryResult {
647c478bdstevel@tonic-gate  const char *zFile;  /* Filename - used for error reporting */
657c478bdstevel@tonic-gate  int nElem;          /* Number of used entries in azElem[] */
667c478bdstevel@tonic-gate  int nAlloc;         /* Number of slots allocated for azElem[] */
677c478bdstevel@tonic-gate  char **azElem;      /* The result of the query */
687c478bdstevel@tonic-gate};
697c478bdstevel@tonic-gate
707c478bdstevel@tonic-gate/*
717c478bdstevel@tonic-gate** The callback function for db_query
727c478bdstevel@tonic-gate*/
737c478bdstevel@tonic-gatestatic int db_query_callback(
747c478bdstevel@tonic-gate  void *pUser,     /* Pointer to the QueryResult structure */
757c478bdstevel@tonic-gate  int nArg,        /* Number of columns in this result row */
767c478bdstevel@tonic-gate  char **azArg,    /* Text of data in all columns */
777c478bdstevel@tonic-gate  char **NotUsed   /* Names of the columns */
787c478bdstevel@tonic-gate){
797c478bdstevel@tonic-gate  struct QueryResult *pResult = (struct QueryResult*)pUser;
807c478bdstevel@tonic-gate  int i;
817c478bdstevel@tonic-gate  if( pResult->nElem + nArg >= pResult->nAlloc ){
827c478bdstevel@tonic-gate    if( pResult->nAlloc==0 ){
837c478bdstevel@tonic-gate      pResult->nAlloc = nArg+1;
847c478bdstevel@tonic-gate    }else{
857c478bdstevel@tonic-gate      pResult->nAlloc = pResult->nAlloc*2 + nArg + 1;
867c478bdstevel@tonic-gate    }
877c478bdstevel@tonic-gate    pResult->azElem = realloc( pResult->azElem, pResult->nAlloc*sizeof(char*));
887c478bdstevel@tonic-gate    if( pResult->azElem==0 ){
897c478bdstevel@tonic-gate      fprintf(stdout,"%s: malloc failed\n", pResult->zFile);
907c478bdstevel@tonic-gate      return 1;
917c478bdstevel@tonic-gate    }
927c478bdstevel@tonic-gate  }
937c478bdstevel@tonic-gate  if( azArg==0 ) return 0;
947c478bdstevel@tonic-gate  for(i=0; i<nArg; i++){
957c478bdstevel@tonic-gate    pResult->azElem[pResult->nElem++] =
967c478bdstevel@tonic-gate        sqlite_mprintf("%s",azArg[i] ? azArg[i] : "");
977c478bdstevel@tonic-gate  }
987c478bdstevel@tonic-gate  return 0;
997c478bdstevel@tonic-gate}
1007c478bdstevel@tonic-gate
1017c478bdstevel@tonic-gate/*
1027c478bdstevel@tonic-gate** Execute a query against the database.  NULL values are returned
1037c478bdstevel@tonic-gate** as an empty string.  The list is terminated by a single NULL pointer.
1047c478bdstevel@tonic-gate*/
1057c478bdstevel@tonic-gatechar **db_query(sqlite *db, const char *zFile, const char *zFormat, ...){
1067c478bdstevel@tonic-gate  char *zSql;
1077c478bdstevel@tonic-gate  int rc;
1087c478bdstevel@tonic-gate  char *zErrMsg = 0;
1097c478bdstevel@tonic-gate  va_list ap;
1107c478bdstevel@tonic-gate  struct QueryResult sResult;
1117c478bdstevel@tonic-gate  va_start(ap, zFormat);
1127c478bdstevel@tonic-gate  zSql = sqlite_vmprintf(zFormat, ap);
1137c478bdstevel@tonic-gate  va_end(ap);
1147c478bdstevel@tonic-gate  memset(&sResult, 0, sizeof(sResult));
1157c478bdstevel@tonic-gate  sResult.zFile = zFile;
1167c478bdstevel@tonic-gate  if( verbose ) printf("QUERY %s: %s\n", zFile, zSql);
1177c478bdstevel@tonic-gate  rc = sqlite_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
1187c478bdstevel@tonic-gate  if( rc==SQLITE_SCHEMA ){
1197c478bdstevel@tonic-gate    if( zErrMsg ) free(zErrMsg);
1207c478bdstevel@tonic-gate    rc = sqlite_exec(db, zSql, db_query_callback, &sResult, &zErrMsg);
1217c478bdstevel@tonic-gate  }
1227c478bdstevel@tonic-gate  if( verbose ) printf("DONE %s %s\n", zFile, zSql);
1237c478bdstevel@tonic-gate  if( zErrMsg ){
1247c478bdstevel@tonic-gate    fprintf(stdout,"%s: query failed: %s - %s\n", zFile, zSql, zErrMsg);
1257c478bdstevel@tonic-gate    free(zErrMsg);
1267c478bdstevel@tonic-gate    free(zSql);
1277c478bdstevel@tonic-gate    Exit(1);
1287c478bdstevel@tonic-gate  }
1297c478bdstevel@tonic-gate  sqlite_freemem(zSql);
1307c478bdstevel@tonic-gate  if( sResult.azElem==0 ){
1317c478bdstevel@tonic-gate    db_query_callback(&sResult, 0, 0, 0);
1327c478bdstevel@tonic-gate  }
1337c478bdstevel@tonic-gate  sResult.azElem[sResult.nElem] = 0;
1347c478bdstevel@tonic-gate  return sResult.azElem;
1357c478bdstevel@tonic-gate}
1367c478bdstevel@tonic-gate
1377c478bdstevel@tonic-gate/*
1387c478bdstevel@tonic-gate** Execute an SQL statement.
1397c478bdstevel@tonic-gate*/
1407c478bdstevel@tonic-gatevoid db_execute(sqlite *db, const char *zFile, const char *zFormat, ...){
1417c478bdstevel@tonic-gate  char *zSql;
1427c478bdstevel@tonic-gate  int rc;
1437c478bdstevel@tonic-gate  char *zErrMsg = 0;
1447c478bdstevel@tonic-gate  va_list ap;
1457c478bdstevel@tonic-gate  va_start(ap, zFormat);
1467c478bdstevel@tonic-gate  zSql = sqlite_vmprintf(zFormat, ap);
1477c478bdstevel@tonic-gate  va_end(ap);
1487c478bdstevel@tonic-gate  if( verbose ) printf("EXEC %s: %s\n", zFile, zSql);
1497c478bdstevel@tonic-gate  rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
1507c478bdstevel@tonic-gate  while( rc==SQLITE_SCHEMA ){
1517c478bdstevel@tonic-gate    if( zErrMsg ) free(zErrMsg);
1527c478bdstevel@tonic-gate    rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
1537c478bdstevel@tonic-gate  }
1547c478bdstevel@tonic-gate  if( verbose ) printf("DONE %s: %s\n", zFile, zSql);
1557c478bdstevel@tonic-gate  if( zErrMsg ){
1567c478bdstevel@tonic-gate    fprintf(stdout,"%s: command failed: %s - %s\n", zFile, zSql, zErrMsg);
1577c478bdstevel@tonic-gate    free(zErrMsg);
1587c478bdstevel@tonic-gate    sqlite_freemem(zSql);
1597c478bdstevel@tonic-gate    Exit(1);
1607c478bdstevel@tonic-gate  }
1617c478bdstevel@tonic-gate  sqlite_freemem(zSql);
1627c478bdstevel@tonic-gate}
1637c478bdstevel@tonic-gate
1647c478bdstevel@tonic-gate/*
1657c478bdstevel@tonic-gate** Free the results of a db_query() call.
1667c478bdstevel@tonic-gate*/
1677c478bdstevel@tonic-gatevoid db_query_free(char **az){
1687c478bdstevel@tonic-gate  int i;
1697c478bdstevel@tonic-gate  for(i=0; az[i]; i++){
1707c478bdstevel@tonic-gate    sqlite_freemem(az[i]);
1717c478bdstevel@tonic-gate  }
1727c478bdstevel@tonic-gate  free(az);
1737c478bdstevel@tonic-gate}
1747c478bdstevel@tonic-gate
1757c478bdstevel@tonic-gate/*
1767c478bdstevel@tonic-gate** Check results
1777c478bdstevel@tonic-gate*/
1787c478bdstevel@tonic-gatevoid db_check(const char *zFile, const char *zMsg, char **az, ...){
1797c478bdstevel@tonic-gate  va_list ap;
1807c478bdstevel@tonic-gate  int i;
1817c478bdstevel@tonic-gate  char *z;
1827c478bdstevel@tonic-gate  va_start(ap, az);
1837c478bdstevel@tonic-gate  for(i=0; (z = va_arg(ap, char*))!=0; i++){
1847c478bdstevel@tonic-gate    if( az[i]==0 || strcmp(az[i],z)!=0 ){
1857c478bdstevel@tonic-gate      fprintf(stdout,"%s: %s: bad result in column %d: %s\n",
1867c478bdstevel@tonic-gate        zFile, zMsg, i+1, az[i]);
1877c478bdstevel@tonic-gate      db_query_free(az);
1887c478bdstevel@tonic-gate      Exit(1);
1897c478bdstevel@tonic-gate    }
1907c478bdstevel@tonic-gate  }
1917c478bdstevel@tonic-gate  va_end(ap);
1927c478bdstevel@tonic-gate  db_query_free(az);
1937c478bdstevel@tonic-gate}
1947c478bdstevel@tonic-gate
1957c478bdstevel@tonic-gatepthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
1967c478bdstevel@tonic-gatepthread_cond_t sig = PTHREAD_COND_INITIALIZER;
1977c478bdstevel@tonic-gateint thread_cnt = 0;
1987c478bdstevel@tonic-gate
1997c478bdstevel@tonic-gatestatic void *worker_bee(void *pArg){
2007c478bdstevel@tonic-gate  const char *zFilename = (char*)pArg;
2017c478bdstevel@tonic-gate  char *azErr;
2027c478bdstevel@tonic-gate  int i, cnt;
2037c478bdstevel@tonic-gate  int t = atoi(zFilename);
2047c478bdstevel@tonic-gate  char **az;
2057c478bdstevel@tonic-gate  sqlite *db;
2067c478bdstevel@tonic-gate
2077c478bdstevel@tonic-gate  pthread_mutex_lock(&lock);
2087c478bdstevel@tonic-gate  thread_cnt++;
2097c478bdstevel@tonic-gate  pthread_mutex_unlock(&lock);
2107c478bdstevel@tonic-gate  printf("%s: START\n", zFilename);
2117c478bdstevel@tonic-gate  fflush(stdout);
2127c478bdstevel@tonic-gate  for(cnt=0; cnt<10; cnt++){
2137c478bdstevel@tonic-gate    db = sqlite_open(&zFilename[2], 0, &azErr);
2147c478bdstevel@tonic-gate    if( db==0 ){
2157c478bdstevel@tonic-gate      fprintf(stdout,"%s: can't open\n", zFilename);
2167c478bdstevel@tonic-gate      Exit(1);
2177c478bdstevel@tonic-gate    }
2187c478bdstevel@tonic-gate    sqlite_busy_handler(db, db_is_locked, zFilename);
2197c478bdstevel@tonic-gate    db_execute(db, zFilename, "CREATE TABLE t%d(a,b,c);", t);
2207c478bdstevel@tonic-gate    for(i=1; i<=100; i++){
2217c478bdstevel@tonic-gate      db_execute(db, zFilename, "INSERT INTO t%d VALUES(%d,%d,%d);",
2227c478bdstevel@tonic-gate         t, i, i*2, i*i);
2237c478bdstevel@tonic-gate    }
2247c478bdstevel@tonic-gate    az = db_query(db, zFilename, "SELECT count(*) FROM t%d", t);
2257c478bdstevel@tonic-gate    db_check(zFilename, "tX size", az, "100", 0);
2267c478bdstevel@tonic-gate    az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
2277c478bdstevel@tonic-gate    db_check(zFilename, "tX avg", az, "101", 0);
2287c478bdstevel@tonic-gate    db_execute(db, zFilename, "DELETE FROM t%d WHERE a>50", t);
2297c478bdstevel@tonic-gate    az = db_query(db, zFilename, "SELECT avg(b) FROM t%d", t);
2307c478bdstevel@tonic-gate    db_check(zFilename, "tX avg2", az, "51", 0);
2317c478bdstevel@tonic-gate    for(i=1; i<=50; i++){
2327c478bdstevel@tonic-gate      char z1[30], z2[30];
2337c478bdstevel@tonic-gate      az = db_query(db, zFilename, "SELECT b, c FROM t%d WHERE a=%d", t, i);
2347c478bdstevel@tonic-gate      sprintf(z1, "%d", i*2);
2357c478bdstevel@tonic-gate      sprintf(z2, "%d", i*i);
2367c478bdstevel@tonic-gate      db_check(zFilename, "readback", az, z1, z2, 0);
2377c478bdstevel@tonic-gate    }
2387c478bdstevel@tonic-gate    db_execute(db, zFilename, "DROP TABLE t%d;", t);
2397c478bdstevel@tonic-gate    sqlite_close(db);
2407c478bdstevel@tonic-gate  }
2417c478bdstevel@tonic-gate  printf("%s: END\n", zFilename);
2427c478bdstevel@tonic-gate  /* unlink(zFilename); */
2437c478bdstevel@tonic-gate  fflush(stdout);
2447c478bdstevel@tonic-gate  pthread_mutex_lock(&lock);
2457c478bdstevel@tonic-gate  thread_cnt--;
2467c478bdstevel@tonic-gate  if( thread_cnt<=0 ){
2477c478bdstevel@tonic-gate    pthread_cond_signal(&sig);
2487c478bdstevel@tonic-gate  }
2497c478bdstevel@tonic-gate  pthread_mutex_unlock(&lock);
2507c478bdstevel@tonic-gate  return 0;
2517c478bdstevel@tonic-gate}
2527c478bdstevel@tonic-gate
2537c478bdstevel@tonic-gateint main(int argc, char **argv){
2547c478bdstevel@tonic-gate  char *zFile;
2557c478bdstevel@tonic-gate  int i, n;
2567c478bdstevel@tonic-gate  pthread_t id;
2577c478bdstevel@tonic-gate  if( argc>2 && strcmp(argv[1], "-v")==0 ){
2587c478bdstevel@tonic-gate    verbose = 1;
2597c478bdstevel@tonic-gate    argc--;
2607c478bdstevel@tonic-gate    argv++;
2617c478bdstevel@tonic-gate  }
2627c478bdstevel@tonic-gate  if( argc<2 || (n=atoi(argv[1]))<1 ) n = 10;
2637c478bdstevel@tonic-gate  for(i=0; i<n; i++){
2647c478bdstevel@tonic-gate    char zBuf[200];
2657c478bdstevel@tonic-gate    sprintf(zBuf, "testdb-%d", (i+1)/2);
2667c478bdstevel@tonic-gate    unlink(zBuf);
2677c478bdstevel@tonic-gate  }
2687c478bdstevel@tonic-gate  for(i=0; i<n; i++){
2697c478bdstevel@tonic-gate    zFile = sqlite_mprintf("%d.testdb-%d", i%2+1, (i+2)/2);
2707c478bdstevel@tonic-gate    unlink(zFile);
2717c478bdstevel@tonic-gate    pthread_create(&id, 0, worker_bee, (void*)zFile);
2727c478bdstevel@tonic-gate    pthread_detach(id);
2737c478bdstevel@tonic-gate  }
2747c478bdstevel@tonic-gate  pthread_mutex_lock(&lock);
2757c478bdstevel@tonic-gate  while( thread_cnt>0 ){
2767c478bdstevel@tonic-gate    pthread_cond_wait(&sig, &lock);
2777c478bdstevel@tonic-gate  }
2787c478bdstevel@tonic-gate  pthread_mutex_unlock(&lock);
2797c478bdstevel@tonic-gate  for(i=0; i<n; i++){
2807c478bdstevel@tonic-gate    char zBuf[200];
2817c478bdstevel@tonic-gate    sprintf(zBuf, "testdb-%d", (i+1)/2);
2827c478bdstevel@tonic-gate    unlink(zBuf);
2837c478bdstevel@tonic-gate  }
2847c478bdstevel@tonic-gate  return 0;
2857c478bdstevel@tonic-gate}
286