Beispiel #1
0
/*
** Wait for a client process to complete all its tasks
*/
static void waitForClient(int iClient, int iTimeout, char *zErrPrefix){
  sqlite3_stmt *pStmt;
  int rc;
  if( iClient>0 ){
    pStmt = prepareSql(
               "SELECT 1 FROM task"
               " WHERE client=%d"
               "   AND client IN (SELECT id FROM client)"
               "  AND endtime IS NULL",
               iClient);
  }else{
    pStmt = prepareSql(
               "SELECT 1 FROM task"
               " WHERE client IN (SELECT id FROM client)"
               "   AND endtime IS NULL");
  }
  g.iTimeout = 0;
  while( ((rc = sqlite3_step(pStmt))==SQLITE_BUSY || rc==SQLITE_ROW)
    && iTimeout>0
  ){
    sqlite3_reset(pStmt);
    sqlite3_sleep(50);
    iTimeout -= 50;
  }
  sqlite3_finalize(pStmt);
  g.iTimeout = DEFAULT_TIMEOUT;
  if( rc!=SQLITE_DONE ){
    if( zErrPrefix==0 ) zErrPrefix = "";
    if( iClient>0 ){
      errorMessage("%stimeout waiting for client %d", zErrPrefix, iClient);
    }else{
      errorMessage("%stimeout waiting for all clients", zErrPrefix);
    }
  }
}
Beispiel #2
0
int getRowCount(char* sql){
	sqlite3_stmt *stmt;
	prepareSql(&stmt, sql);
	
	int rc, counter=0;
	
	while((rc=sqlite3_step(stmt)) == SQLITE_ROW){
		counter++;
	}
	return counter;
}
Beispiel #3
0
void addDbRowBinaryAddress(time_t ts, int dr, int dl, int ul, char* ad, int adLen){
    char sql[200];
    sprintf(sql, "INSERT INTO data (ts, dr, ad, dl, ul) values (%d, %d, ?, %d, %d)", (int)ts, dr, dl, ul);
    sqlite3_stmt *stmt;
    prepareSql(&stmt, sql);
    sqlite3_bind_blob(stmt, 1, ad, adLen, SQLITE_STATIC);
    int rc = sqlite3_step(stmt);
	if (rc != SQLITE_DONE){
		logMsg(LOG_ERR, "addDbRowBinaryAddress caused sqlite3_step to return %d.", rc);
	}
	sqlite3_reset(stmt);
}
Beispiel #4
0
void setup(){
    setUpDbForTest();
    setupDb();
    setTzToGmt();
    prepareSql(&selectAllStmt, "SELECT ts AS ts, dr AS dr, dl As dl, ul AS ul, ad AS ad, hs AS hs FROM data ORDER BY ts DESC, ad ASC");
}
Beispiel #5
0
/*
** Run a script.
*/
static void runScript(
  int iClient,       /* The client number, or 0 for the master */
  int taskId,        /* The task ID for clients.  0 for master */
  char *zScript,     /* Text of the script */
  char *zFilename    /* File from which script was read. */
){
  int lineno = 1;
  int prevLine = 1;
  int ii = 0;
  int iBegin = 0;
  int n, c, j;
  int len;
  int nArg;
  String sResult;
  char zCmd[30];
  char zError[1000];
  char azArg[MX_ARG][100];

  memset(&sResult, 0, sizeof(sResult));
  stringReset(&sResult);
  while( (c = zScript[ii])!=0 ){
    prevLine = lineno;
    len = tokenLength(zScript+ii, &lineno);
    if( isspace(c) || (c=='/' && zScript[ii+1]=='*') ){
      ii += len;
      continue;
    }
    if( c!='-' || zScript[ii+1]!='-' || !isalpha(zScript[ii+2]) ){
      ii += len;
      continue;
    }

    /* Run any prior SQL before processing the new --command */
    if( ii>iBegin ){
      char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
      evalSql(&sResult, zSql);
      sqlite3_free(zSql);
      iBegin = ii + len;
    }

    /* Parse the --command */
    if( g.iTrace>=2 ) logMessage("%.*s", len, zScript+ii);
    n = extractToken(zScript+ii+2, len-2, zCmd, sizeof(zCmd));
    for(nArg=0; n<len-2 && nArg<MX_ARG; nArg++){
      while( n<len-2 && isspace(zScript[ii+2+n]) ){ n++; }
      if( n>=len-2 ) break;
      n += extractToken(zScript+ii+2+n, len-2-n,
                        azArg[nArg], sizeof(azArg[nArg]));
    }
    for(j=nArg; j<MX_ARG; j++) azArg[j++][0] = 0;

    /*
    **  --sleep N
    **
    ** Pause for N milliseconds
    */
    if( strcmp(zCmd, "sleep")==0 ){
      sqlite3_sleep(atoi(azArg[0]));
    }else 

    /*
    **   --exit N
    **
    ** Exit this process.  If N>0 then exit without shutting down
    ** SQLite.  (In other words, simulate a crash.)
    */
    if( strcmp(zCmd, "exit")==0 ){
      int rc = atoi(azArg[0]);
      finishScript(iClient, taskId, 1);
      if( rc==0 ) sqlite3_close(g.db);
      exit(rc);
    }else

    /*
    **   --testcase NAME
    **
    ** Begin a new test case.  Announce in the log that the test case
    ** has begun.
    */
    if( strcmp(zCmd, "testcase")==0 ){
      if( g.iTrace==1 ) logMessage("%.*s", len - 1, zScript+ii);
      stringReset(&sResult);
    }else

    /*
    **   --finish
    **
    ** Mark the current task as having finished, even if it is not.
    ** This can be used in conjunction with --exit to simulate a crash.
    */
    if( strcmp(zCmd, "finish")==0 && iClient>0 ){
      finishScript(iClient, taskId, 1);
    }else

    /*
    **  --reset
    **
    ** Reset accumulated results back to an empty string
    */
    if( strcmp(zCmd, "reset")==0 ){
      stringReset(&sResult);
    }else

    /*
    **  --match ANSWER...
    **
    ** Check to see if output matches ANSWER.  Report an error if not.
    */
    if( strcmp(zCmd, "match")==0 ){
      int jj;
      char *zAns = zScript+ii;
      for(jj=7; jj<len-1 && isspace(zAns[jj]); jj++){}
      zAns += jj;
      if( len-jj-1!=sResult.n || strncmp(sResult.z, zAns, len-jj-1) ){
        errorMessage("line %d of %s:\nExpected [%.*s]\n     Got [%s]",
          prevLine, zFilename, len-jj-1, zAns, sResult.z);
      }
      g.nTest++;
      stringReset(&sResult);
    }else

    /*
    **  --glob ANSWER...
    **  --notglob ANSWER....
    **
    ** Check to see if output does or does not match the glob pattern
    ** ANSWER.
    */
    if( strcmp(zCmd, "glob")==0 || strcmp(zCmd, "notglob")==0 ){
      int jj;
      char *zAns = zScript+ii;
      char *zCopy;
      int isGlob = (zCmd[0]=='g');
      for(jj=9-3*isGlob; jj<len-1 && isspace(zAns[jj]); jj++){}
      zAns += jj;
      zCopy = sqlite3_mprintf("%.*s", len-jj-1, zAns);
      if( (sqlite3_strglob(zCopy, sResult.z)==0)^isGlob ){
        errorMessage("line %d of %s:\nExpected [%s]\n     Got [%s]",
          prevLine, zFilename, zCopy, sResult.z);
      }
      sqlite3_free(zCopy);
      g.nTest++;
      stringReset(&sResult);
    }else

    /*
    **  --output
    **
    ** Output the result of the previous SQL.
    */
    if( strcmp(zCmd, "output")==0 ){
      logMessage("%s", sResult.z);
    }else

    /*
    **  --source FILENAME
    **
    ** Run a subscript from a separate file.
    */
    if( strcmp(zCmd, "source")==0 ){
      char *zNewFile, *zNewScript;
      char *zToDel = 0;
      zNewFile = azArg[0];
      if( zNewFile[0]!='/' ){
        int k;
        for(k=(int)strlen(zFilename)-1; k>=0 && zFilename[k]!='/'; k--){}
        if( k>0 ){
          zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile);
        }
      }
      zNewScript = readFile(zNewFile);
      if( g.iTrace ) logMessage("begin script [%s]\n", zNewFile);
      runScript(0, 0, zNewScript, zNewFile);
      sqlite3_free(zNewScript);
      if( g.iTrace ) logMessage("end script [%s]\n", zNewFile);
      sqlite3_free(zToDel);
    }else

    /*
    **  --print MESSAGE....
    **
    ** Output the remainder of the line to the log file
    */
    if( strcmp(zCmd, "print")==0 ){
      int jj;
      for(jj=7; jj<len && isspace(zScript[ii+jj]); jj++){}
      logMessage("%.*s", len-jj, zScript+ii+jj);
    }else

    /*
    **  --if EXPR
    **
    ** Skip forward to the next matching --endif or --else if EXPR is false.
    */
    if( strcmp(zCmd, "if")==0 ){
      int jj, rc;
      sqlite3_stmt *pStmt;
      for(jj=4; jj<len && isspace(zScript[ii+jj]); jj++){}
      pStmt = prepareSql("SELECT %.*s", len-jj, zScript+ii+jj);
      rc = sqlite3_step(pStmt);
      if( rc!=SQLITE_ROW || sqlite3_column_int(pStmt, 0)==0 ){
        ii += findEndif(zScript+ii+len, 1, &lineno);
      }
      sqlite3_finalize(pStmt);
    }else

    /*
    **  --else
    **
    ** This command can only be encountered if currently inside an --if that
    ** is true.  Skip forward to the next matching --endif.
    */
    if( strcmp(zCmd, "else")==0 ){
      ii += findEndif(zScript+ii+len, 0, &lineno);
    }else

    /*
    **  --endif
    **
    ** This command can only be encountered if currently inside an --if that
    ** is true or an --else of a false if.  This is a no-op.
    */
    if( strcmp(zCmd, "endif")==0 ){
      /* no-op */
    }else

    /*
    **  --start CLIENT
    **
    ** Start up the given client.
    */
    if( strcmp(zCmd, "start")==0 && iClient==0 ){
      int iNewClient = atoi(azArg[0]);
      if( iNewClient>0 ){
        startClient(iNewClient);
      }
    }else

    /*
    **  --wait CLIENT TIMEOUT
    **
    ** Wait until all tasks complete for the given client.  If CLIENT is
    ** "all" then wait for all clients to complete.  Wait no longer than
    ** TIMEOUT milliseconds (default 10,000)
    */
    if( strcmp(zCmd, "wait")==0 && iClient==0 ){
      int iTimeout = nArg>=2 ? atoi(azArg[1]) : 10000;
      sqlite3_snprintf(sizeof(zError),zError,"line %d of %s\n",
                       prevLine, zFilename);
      waitForClient(atoi(azArg[0]), iTimeout, zError);
    }else

    /*
    **  --task CLIENT
    **     <task-content-here>
    **  --end
    **
    ** Assign work to a client.  Start the client if it is not running
    ** already.
    */
    if( strcmp(zCmd, "task")==0 && iClient==0 ){
      int iTarget = atoi(azArg[0]);
      int iEnd;
      char *zTask;
      char *zTName;
      iEnd = findEnd(zScript+ii+len, &lineno);
      if( iTarget<0 ){
        errorMessage("line %d of %s: bad client number: %d",
                     prevLine, zFilename, iTarget);
      }else{
        zTask = sqlite3_mprintf("%.*s", iEnd, zScript+ii+len);
        if( nArg>1 ){
          zTName = sqlite3_mprintf("%s", azArg[1]);
        }else{
          zTName = sqlite3_mprintf("%s:%d", filenameTail(zFilename), prevLine);
        }
        startClient(iTarget);
        runSql("INSERT INTO task(client,script,name)"
               " VALUES(%d,'%q',%Q)", iTarget, zTask, zTName);
        sqlite3_free(zTask);
        sqlite3_free(zTName);
      }
      iEnd += tokenLength(zScript+ii+len+iEnd, &lineno);
      len += iEnd;
      iBegin = ii+len;
    }else

    /*
    **  --breakpoint
    **
    ** This command calls "test_breakpoint()" which is a routine provided
    ** as a convenient place to set a debugger breakpoint.
    */
    if( strcmp(zCmd, "breakpoint")==0 ){
      test_breakpoint();
    }else

    /*
    **  --show-sql-errors BOOLEAN
    **
    ** Turn display of SQL errors on and off.
    */
    if( strcmp(zCmd, "show-sql-errors")==0 ){
      g.bIgnoreSqlErrors = nArg>=1 ? !booleanValue(azArg[0]) : 1;
    }else


    /* error */{
      errorMessage("line %d of %s: unknown command --%s",
                   prevLine, zFilename, zCmd);
    }
    ii += len;
  }
  if( iBegin<ii ){
    char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
    runSql(zSql);
    sqlite3_free(zSql);
  }
  stringFree(&sResult);
}
Beispiel #6
0
/*
** Look up the next task for client iClient in the database.
** Return the task script and the task number and mark that
** task as being under way.
*/
static int startScript(
  int iClient,              /* The client number */
  char **pzScript,          /* Write task script here */
  int *pTaskId,             /* Write task number here */
  char **pzTaskName         /* Name of the task */
){
  sqlite3_stmt *pStmt = 0;
  int taskId;
  int rc;
  int totalTime = 0;

  *pzScript = 0;
  g.iTimeout = 0;
  while(1){
    rc = trySql("BEGIN IMMEDIATE");
    if( rc==SQLITE_BUSY ){
      sqlite3_sleep(10);
      totalTime += 10;
      continue;
    }
    if( rc!=SQLITE_OK ){
      fatalError("in startScript: %s", sqlite3_errmsg(g.db));
    }
    if( g.nError || g.nTest ){
      runSql("UPDATE counters SET nError=nError+%d, nTest=nTest+%d",
             g.nError, g.nTest);
      g.nError = 0;
      g.nTest = 0;
    }
    pStmt = prepareSql("SELECT 1 FROM client WHERE id=%d AND wantHalt",iClient);
    rc = sqlite3_step(pStmt);
    sqlite3_finalize(pStmt);
    if( rc==SQLITE_ROW ){
      runSql("DELETE FROM client WHERE id=%d", iClient);
      g.iTimeout = DEFAULT_TIMEOUT;
      runSql("COMMIT TRANSACTION;");
      return SQLITE_DONE;
    }
    pStmt = prepareSql(
              "SELECT script, id, name FROM task"
              " WHERE client=%d AND starttime IS NULL"
              " ORDER BY id LIMIT 1", iClient);
    rc = sqlite3_step(pStmt);
    if( rc==SQLITE_ROW ){
      int n = sqlite3_column_bytes(pStmt, 0);
      *pzScript = sqlite3_malloc(n+1);
      strcpy(*pzScript, (const char*)sqlite3_column_text(pStmt, 0));
      *pTaskId = taskId = sqlite3_column_int(pStmt, 1);
      *pzTaskName = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 2));
      sqlite3_finalize(pStmt);
      runSql("UPDATE task"
             "   SET starttime=strftime('%%Y-%%m-%%d %%H:%%M:%%f','now')"
             " WHERE id=%d;", taskId);
      g.iTimeout = DEFAULT_TIMEOUT;
      runSql("COMMIT TRANSACTION;");
      return SQLITE_OK;
    }
    sqlite3_finalize(pStmt);
    if( rc==SQLITE_DONE ){
      if( totalTime>30000 ){
        errorMessage("Waited over 30 seconds with no work.  Giving up.");
        runSql("DELETE FROM client WHERE id=%d; COMMIT;", iClient);
        sqlite3_close(g.db);
        exit(1);
      }
      while( trySql("COMMIT")==SQLITE_BUSY ){
        sqlite3_sleep(10);
        totalTime += 10;
      }
      sqlite3_sleep(100);
      totalTime += 100;
      continue;
    }
    fatalError("%s", sqlite3_errmsg(g.db));
  }
  g.iTimeout = DEFAULT_TIMEOUT;
}
Beispiel #7
0
int sqlite_test()
{
  const char *zClient;
  int iClient;
  int n, i;
  int openFlags = SQLITE_OPEN_READWRITE;
  int rc;
  char *zScript;
  int taskId;
  const char *zTrace;
  const char *zCOption;

  g.argv0 = argv[0];
  g.iTrace = 1;
  if( argc<2 ) usage(argv[0]);
  g.zDbFile = argv[1];
  if( strglob("*.test", g.zDbFile) ) usage(argv[0]);
  if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){
    fprintf(stderr, "SQLite library and header mismatch\n"
                    "Library: %s\n"
                    "Header:  %s\n",
                    sqlite3_sourceid(), SQLITE_SOURCE_ID);
    exit(1);
  }
  n = argc-2;
  sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID());
  g.zVfs = findOption(argv+2, &n, "vfs", 1);
  zClient = findOption(argv+2, &n, "client", 1);
  g.zErrLog = findOption(argv+2, &n, "errlog", 1);
  g.zLog = findOption(argv+2, &n, "log", 1);
  zTrace = findOption(argv+2, &n, "trace", 1);
  if( zTrace ) g.iTrace = atoi(zTrace);
  if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0;
  g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0;
  g.bSync = findOption(argv+2, &n, "sync", 0)!=0;
  if( g.zErrLog ){
    g.pErrLog = fopen(g.zErrLog, "a");
  }else{
    g.pErrLog = stderr;
  }
  if( g.zLog ){
    g.pLog = fopen(g.zLog, "a");
  }else{
    g.pLog = stdout;
  }
  
  sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0);
  if( zClient ){
    iClient = atoi(zClient);
    if( iClient<1 ) fatalError("illegal client number: %d\n", iClient);
    sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d",
                     GETPID(), iClient);
  }else{
    if( g.iTrace>0 ){
      printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" );
      for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){
        printf("-DSQLITE_%s\n", zCOption);
      }
      fflush(stdout);
    }
    iClient =  0;
    unlink(g.zDbFile);
    openFlags |= SQLITE_OPEN_CREATE;
  }
  rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs);
  if( rc ) fatalError("cannot open [%s]", g.zDbFile);
  sqlite3_enable_load_extension(g.db, 1);
  sqlite3_busy_handler(g.db, busyHandler, 0);
  sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0,
                          vfsNameFunc, 0, 0);
  sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0,
                          evalFunc, 0, 0);
  g.iTimeout = DEFAULT_TIMEOUT;
  if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0);
  if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
  if( iClient>0 ){
    if( n>0 ) unrecognizedArguments(argv[0], n, argv+2);
    if( g.iTrace ) logMessage("start-client");
    while(1){
      char *zTaskName = 0;
      rc = startScript(iClient, &zScript, &taskId, &zTaskName);
      if( rc==SQLITE_DONE ) break;
      if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId);
      runScript(iClient, taskId, zScript, zTaskName);
      if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId);
      finishScript(iClient, taskId, 0);
      sqlite3_free(zTaskName);
      sqlite3_sleep(10);
    }
    if( g.iTrace ) logMessage("end-client");
  }else{
    sqlite3_stmt *pStmt;
    int iTimeout;
    if( n==0 ){
      fatalError("missing script filename");
    }
    if( n>1 ) unrecognizedArguments(argv[0], n, argv+2);
    runSql(
      "CREATE TABLE task(\n"
      "  id INTEGER PRIMARY KEY,\n"
      "  name TEXT,\n"
      "  client INTEGER,\n"
      "  starttime DATE,\n"
      "  endtime DATE,\n"
      "  script TEXT\n"
      ");"
      "CREATE INDEX task_i1 ON task(client, starttime);\n"
      "CREATE INDEX task_i2 ON task(client, endtime);\n"
      "CREATE TABLE counters(nError,nTest);\n"
      "INSERT INTO counters VALUES(0,0);\n"
      "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n"
    );
    zScript = readFile(argv[2]);
    if( g.iTrace ) logMessage("begin script [%s]\n", argv[2]);
    runScript(0, 0, zScript, argv[2]);
    sqlite3_free(zScript);
    if( g.iTrace ) logMessage("end script [%s]\n", argv[2]);
    waitForClient(0, 2000, "during shutdown...\n");
    trySql("UPDATE client SET wantHalt=1");
    sqlite3_sleep(10);
    g.iTimeout = 0;
    iTimeout = 1000;
    while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY
        || rc==SQLITE_ROW) && iTimeout>0 ){
      sqlite3_sleep(10);
      iTimeout -= 10;
    }
    sqlite3_sleep(100);
    pStmt = prepareSql("SELECT nError, nTest FROM counters");
    iTimeout = 1000;
    while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){
      sqlite3_sleep(10);
      iTimeout -= 10;
    }
    if( rc==SQLITE_ROW ){
      g.nError += sqlite3_column_int(pStmt, 0);
      g.nTest += sqlite3_column_int(pStmt, 1);
    }
    sqlite3_finalize(pStmt);
  }
  sqlite3_close(g.db);  
  maybeClose(g.pLog);
  maybeClose(g.pErrLog);
  if( iClient==0 ){
    printf("Summary: %d errors in %d tests\n", g.nError, g.nTest);
  }
  return g.nError>0;
}
Beispiel #8
0
int SQLITE_CDECL main(int argc, char **argv){
  const char *zClient;
  int iClient;
  int n, i;
  int openFlags = SQLITE_OPEN_READWRITE;
  int rc;
  char *zScript;
  int taskId;
  const char *zTrace;
  const char *zCOption;
  const char *zJMode;
  const char *zNRep;
  int nRep = 1, iRep;
  int iTmout = 0;              /* Default: no timeout */
  const char *zTmout;

  g.argv0 = argv[0];
  g.iTrace = 1;
  if( argc<2 ) usage(argv[0]);
  g.zDbFile = argv[1];
  if( strglob("*.test", g.zDbFile) ) usage(argv[0]);
  if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){
    fprintf(stderr, "SQLite library and header mismatch\n"
                    "Library: %s\n"
                    "Header:  %s\n",
                    sqlite3_sourceid(), SQLITE_SOURCE_ID);
    exit(1);
  }
  n = argc-2;
  sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID());
  zJMode = findOption(argv+2, &n, "journalmode", 1);
  zNRep = findOption(argv+2, &n, "repeat", 1);
  if( zNRep ) nRep = atoi(zNRep);
  if( nRep<1 ) nRep = 1;
  g.zVfs = findOption(argv+2, &n, "vfs", 1);
  zClient = findOption(argv+2, &n, "client", 1);
  g.zErrLog = findOption(argv+2, &n, "errlog", 1);
  g.zLog = findOption(argv+2, &n, "log", 1);
  zTrace = findOption(argv+2, &n, "trace", 1);
  if( zTrace ) g.iTrace = atoi(zTrace);
  if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0;
  zTmout = findOption(argv+2, &n, "timeout", 1);
  if( zTmout ) iTmout = atoi(zTmout);
  g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0;
  g.bSync = findOption(argv+2, &n, "sync", 0)!=0;
  if( g.zErrLog ){
    g.pErrLog = fopen(g.zErrLog, "a");
  }else{
    g.pErrLog = stderr;
  }
  if( g.zLog ){
    g.pLog = fopen(g.zLog, "a");
  }else{
    g.pLog = stdout;
  }
  
  sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0);
  if( zClient ){
    iClient = atoi(zClient);
    if( iClient<1 ) fatalError("illegal client number: %d\n", iClient);
    sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d",
                     GETPID(), iClient);
  }else{
    int nTry = 0;
    if( g.iTrace>0 ){
      printf("BEGIN: %s", argv[0]);
      for(i=1; i<argc; i++) printf(" %s", argv[i]);
      printf("\n");
      printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" );
      for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){
        printf("-DSQLITE_%s\n", zCOption);
      }
      fflush(stdout);
    }
    iClient =  0;
    do{
      if( (nTry%5)==4 ) printf("... %strying to unlink '%s'\n",
                               nTry>5 ? "still " : "", g.zDbFile);
      rc = unlink(g.zDbFile);
      if( rc && errno==ENOENT ) rc = 0;
    }while( rc!=0 && (++nTry)<60 && sqlite3_sleep(1000)>0 );
    if( rc!=0 ){
      fatalError("unable to unlink '%s' after %d attempts\n",
                 g.zDbFile, nTry);
    }
    openFlags |= SQLITE_OPEN_CREATE;
  }
  rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs);
  if( rc ) fatalError("cannot open [%s]", g.zDbFile);
  if( iTmout>0 ) sqlite3_busy_timeout(g.db, iTmout);
  
  if( zJMode ){
#if defined(_WIN32)
    if( sqlite3_stricmp(zJMode,"persist")==0
     || sqlite3_stricmp(zJMode,"truncate")==0
    ){
      printf("Changing journal mode to DELETE from %s", zJMode);
      zJMode = "DELETE";
    }
#endif
    runSql("PRAGMA journal_mode=%Q;", zJMode);
  }
  if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
  sqlite3_enable_load_extension(g.db, 1);
  sqlite3_busy_handler(g.db, busyHandler, 0);
  sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0,
                          vfsNameFunc, 0, 0);
  sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0,
                          evalFunc, 0, 0);
  g.iTimeout = DEFAULT_TIMEOUT;
  if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0);
  if( iClient>0 ){
    if( n>0 ) unrecognizedArguments(argv[0], n, argv+2);
    if( g.iTrace ) logMessage("start-client");
    while(1){
      char *zTaskName = 0;
      rc = startScript(iClient, &zScript, &taskId, &zTaskName);
      if( rc==SQLITE_DONE ) break;
      if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId);
      runScript(iClient, taskId, zScript, zTaskName);
      if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId);
      finishScript(iClient, taskId, 0);
      sqlite3_free(zTaskName);
      sqlite3_sleep(10);
    }
    if( g.iTrace ) logMessage("end-client");
  }else{
    sqlite3_stmt *pStmt;
    int iTimeout;
    if( n==0 ){
      fatalError("missing script filename");
    }
    if( n>1 ) unrecognizedArguments(argv[0], n, argv+2);
    runSql(
      "DROP TABLE IF EXISTS task;\n"
      "DROP TABLE IF EXISTS counters;\n"
      "DROP TABLE IF EXISTS client;\n"
      "CREATE TABLE task(\n"
      "  id INTEGER PRIMARY KEY,\n"
      "  name TEXT,\n"
      "  client INTEGER,\n"
      "  starttime DATE,\n"
      "  endtime DATE,\n"
      "  script TEXT\n"
      ");"
      "CREATE INDEX task_i1 ON task(client, starttime);\n"
      "CREATE INDEX task_i2 ON task(client, endtime);\n"
      "CREATE TABLE counters(nError,nTest);\n"
      "INSERT INTO counters VALUES(0,0);\n"
      "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n"
    );
    zScript = readFile(argv[2]);
    for(iRep=1; iRep<=nRep; iRep++){
      if( g.iTrace ) logMessage("begin script [%s] cycle %d\n", argv[2], iRep);
      runScript(0, 0, zScript, argv[2]);
      if( g.iTrace ) logMessage("end script [%s] cycle %d\n", argv[2], iRep);
    }
    sqlite3_free(zScript);
    waitForClient(0, 2000, "during shutdown...\n");
    trySql("UPDATE client SET wantHalt=1");
    sqlite3_sleep(10);
    g.iTimeout = 0;
    iTimeout = 1000;
    while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY
        || rc==SQLITE_ROW) && iTimeout>0 ){
      sqlite3_sleep(10);
      iTimeout -= 10;
    }
    sqlite3_sleep(100);
    pStmt = prepareSql("SELECT nError, nTest FROM counters");
    iTimeout = 1000;
    while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){
      sqlite3_sleep(10);
      iTimeout -= 10;
    }
    if( rc==SQLITE_ROW ){
      g.nError += sqlite3_column_int(pStmt, 0);
      g.nTest += sqlite3_column_int(pStmt, 1);
    }
    sqlite3_finalize(pStmt);
  }
  sqlite3_close(g.db);
  maybeClose(g.pLog);
  maybeClose(g.pErrLog);
  if( iClient==0 ){
    printf("Summary: %d errors out of %d tests\n", g.nError, g.nTest);
    printf("END: %s", argv[0]);
    for(i=1; i<argc; i++) printf(" %s", argv[i]);
    printf("\n");
  }
  return g.nError>0;
}