blob: e47be5ae6daa4dd987214f4b7c86a14c5f29feec [file] [log] [blame]
drh27338e62013-04-06 00:19:37 +00001/*
2** 2013-04-05
3**
4** The author disclaims copyright to this source code. In place of
5** 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**
13** This is a program used for testing SQLite, and specifically for testing
14** the ability of independent processes to access the same SQLite database
15** concurrently.
16**
17** Compile this program as follows:
18**
19** gcc -g -c -Wall sqlite3.c $(OPTS)
20** gcc -g -o mptest mptest.c sqlite3.o $(LIBS)
21**
22** Recommended options:
23**
24** -DHAVE_USLEEP
25** -DSQLITE_NO_SYNC
26** -DSQLITE_THREADSAFE=0
27** -DSQLITE_OMIT_LOAD_EXTENSION
28**
29** Run like this:
30**
31** ./mptest $database $script
32**
33** where $database is the database to use for testing and $script is a
34** test script.
35*/
36#include "sqlite3.h"
37#include <stdio.h>
drhbc94dbb2013-04-08 14:28:33 +000038#if defined(_WIN32)
mistachkin08d41892013-04-11 00:09:44 +000039# define WIN32_LEAN_AND_MEAN
40# include <windows.h>
drhbc94dbb2013-04-08 14:28:33 +000041#else
42# include <unistd.h>
43#endif
drh27338e62013-04-06 00:19:37 +000044#include <stdlib.h>
45#include <string.h>
46#include <assert.h>
47#include <ctype.h>
48
mistachkin08d41892013-04-11 00:09:44 +000049/* The suffix to append to the child command lines, if any */
50#if defined(_WIN32)
mistachkinfdd72c92013-04-11 21:13:10 +000051# define GETPID (int)GetCurrentProcessId
mistachkin08d41892013-04-11 00:09:44 +000052#else
mistachkinfdd72c92013-04-11 21:13:10 +000053# define GETPID getpid
mistachkin08d41892013-04-11 00:09:44 +000054#endif
55
drh841810c2013-04-08 13:59:11 +000056/* Mark a parameter as unused to suppress compiler warnings */
57#define UNUSED_PARAMETER(x) (void)x
58
drh27338e62013-04-06 00:19:37 +000059/* Global data
60*/
61static struct Global {
62 char *argv0; /* Name of the executable */
63 const char *zVfs; /* Name of VFS to use. Often NULL meaning "default" */
64 char *zDbFile; /* Name of the database */
65 sqlite3 *db; /* Open connection to database */
66 char *zErrLog; /* Filename for error log */
67 FILE *pErrLog; /* Where to write errors */
68 char *zLog; /* Name of output log file */
69 FILE *pLog; /* Where to write log messages */
drhe3be8c82013-04-11 11:53:45 +000070 char zName[32]; /* Symbolic name of this process */
drh27338e62013-04-06 00:19:37 +000071 int taskId; /* Task ID. 0 means supervisor. */
72 int iTrace; /* Tracing level */
73 int bSqlTrace; /* True to trace SQL commands */
74 int nError; /* Number of errors */
drh3f5bc382013-04-06 13:09:11 +000075 int nTest; /* Number of --match operators */
76 int iTimeout; /* Milliseconds until a busy timeout */
drhbc94dbb2013-04-08 14:28:33 +000077 int bSync; /* Call fsync() */
drh27338e62013-04-06 00:19:37 +000078} g;
79
drh3f5bc382013-04-06 13:09:11 +000080/* Default timeout */
81#define DEFAULT_TIMEOUT 10000
82
drh27338e62013-04-06 00:19:37 +000083/*
84** Print a message adding zPrefix[] to the beginning of every line.
85*/
86static void printWithPrefix(FILE *pOut, const char *zPrefix, const char *zMsg){
87 while( zMsg && zMsg[0] ){
88 int i;
89 for(i=0; zMsg[i] && zMsg[i]!='\n' && zMsg[i]!='\r'; i++){}
90 fprintf(pOut, "%s%.*s\n", zPrefix, i, zMsg);
91 zMsg += i;
92 while( zMsg[0]=='\n' || zMsg[0]=='\r' ) zMsg++;
93 }
94}
95
96/*
97** Compare two pointers to strings, where the pointers might be NULL.
98*/
99static int safe_strcmp(const char *a, const char *b){
100 if( a==b ) return 0;
101 if( a==0 ) return -1;
102 if( b==0 ) return 1;
103 return strcmp(a,b);
104}
105
106/*
107** Return TRUE if string z[] matches glob pattern zGlob[].
108** Return FALSE if the pattern does not match.
109**
110** Globbing rules:
111**
112** '*' Matches any sequence of zero or more characters.
113**
114** '?' Matches exactly one character.
115**
116** [...] Matches one character from the enclosed list of
117** characters.
118**
119** [^...] Matches one character not in the enclosed list.
120**
121** '#' Matches any sequence of one or more digits with an
122** optional + or - sign in front
123*/
124int strglob(const char *zGlob, const char *z){
125 int c, c2;
126 int invert;
127 int seen;
128
129 while( (c = (*(zGlob++)))!=0 ){
130 if( c=='*' ){
131 while( (c=(*(zGlob++))) == '*' || c=='?' ){
132 if( c=='?' && (*(z++))==0 ) return 0;
133 }
134 if( c==0 ){
135 return 1;
136 }else if( c=='[' ){
137 while( *z && strglob(zGlob-1,z) ){
138 z++;
139 }
140 return (*z)!=0;
141 }
142 while( (c2 = (*(z++)))!=0 ){
143 while( c2!=c ){
144 c2 = *(z++);
145 if( c2==0 ) return 0;
146 }
147 if( strglob(zGlob,z) ) return 1;
148 }
149 return 0;
150 }else if( c=='?' ){
151 if( (*(z++))==0 ) return 0;
152 }else if( c=='[' ){
153 int prior_c = 0;
154 seen = 0;
155 invert = 0;
156 c = *(z++);
157 if( c==0 ) return 0;
158 c2 = *(zGlob++);
159 if( c2=='^' ){
160 invert = 1;
161 c2 = *(zGlob++);
162 }
163 if( c2==']' ){
164 if( c==']' ) seen = 1;
165 c2 = *(zGlob++);
166 }
167 while( c2 && c2!=']' ){
168 if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
169 c2 = *(zGlob++);
170 if( c>=prior_c && c<=c2 ) seen = 1;
171 prior_c = 0;
172 }else{
173 if( c==c2 ){
174 seen = 1;
175 }
176 prior_c = c2;
177 }
178 c2 = *(zGlob++);
179 }
180 if( c2==0 || (seen ^ invert)==0 ) return 0;
181 }else if( c=='#' ){
182 if( (z[0]=='-' || z[0]=='+') && isdigit(z[1]) ) z++;
183 if( !isdigit(z[0]) ) return 0;
184 z++;
185 while( isdigit(z[0]) ){ z++; }
186 }else{
187 if( c!=(*(z++)) ) return 0;
188 }
189 }
190 return *z==0;
191}
192
193/*
194** Close output stream pOut if it is not stdout or stderr
195*/
196static void maybeClose(FILE *pOut){
197 if( pOut!=stdout && pOut!=stderr ) fclose(pOut);
198}
199
200/*
201** Print an error message
202*/
203static void errorMessage(const char *zFormat, ...){
204 va_list ap;
205 char *zMsg;
206 char zPrefix[30];
207 va_start(ap, zFormat);
208 zMsg = sqlite3_vmprintf(zFormat, ap);
209 va_end(ap);
210 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:ERROR: ", g.zName);
211 if( g.pLog ){
212 printWithPrefix(g.pLog, zPrefix, zMsg);
213 fflush(g.pLog);
214 }
215 if( g.pErrLog && safe_strcmp(g.zErrLog,g.zLog) ){
216 printWithPrefix(g.pErrLog, zPrefix, zMsg);
217 fflush(g.pErrLog);
218 }
219 sqlite3_free(zMsg);
220 g.nError++;
221}
222
223/* Forward declaration */
224static int trySql(const char*, ...);
225
226/*
227** Print an error message and then quit.
228*/
229static void fatalError(const char *zFormat, ...){
230 va_list ap;
231 char *zMsg;
232 char zPrefix[30];
233 va_start(ap, zFormat);
234 zMsg = sqlite3_vmprintf(zFormat, ap);
235 va_end(ap);
236 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:FATAL: ", g.zName);
237 if( g.pLog ){
238 printWithPrefix(g.pLog, zPrefix, zMsg);
239 fflush(g.pLog);
240 maybeClose(g.pLog);
241 }
242 if( g.pErrLog && safe_strcmp(g.zErrLog,g.zLog) ){
243 printWithPrefix(g.pErrLog, zPrefix, zMsg);
244 fflush(g.pErrLog);
245 maybeClose(g.pErrLog);
246 }
247 sqlite3_free(zMsg);
248 if( g.db ){
249 int nTry = 0;
drh3f5bc382013-04-06 13:09:11 +0000250 g.iTimeout = 0;
251 while( trySql("UPDATE client SET wantHalt=1;")==SQLITE_BUSY
252 && (nTry++)<100 ){
drh27338e62013-04-06 00:19:37 +0000253 sqlite3_sleep(10);
254 }
255 }
256 sqlite3_close(g.db);
257 exit(1);
258}
259
260
261/*
262** Print a log message
263*/
264static void logMessage(const char *zFormat, ...){
265 va_list ap;
266 char *zMsg;
267 char zPrefix[30];
268 va_start(ap, zFormat);
269 zMsg = sqlite3_vmprintf(zFormat, ap);
270 va_end(ap);
271 sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s: ", g.zName);
272 if( g.pLog ){
273 printWithPrefix(g.pLog, zPrefix, zMsg);
274 fflush(g.pLog);
275 }
276 sqlite3_free(zMsg);
277}
278
279/*
280** Return the length of a string omitting trailing whitespace
281*/
282static int clipLength(const char *z){
283 int n = (int)strlen(z);
284 while( n>0 && isspace(z[n-1]) ){ n--; }
285 return n;
286}
287
288/*
drh1bf44c72013-04-08 13:48:29 +0000289** Auxiliary SQL function to return the name of the VFS
290*/
291static void vfsNameFunc(
292 sqlite3_context *context,
293 int argc,
294 sqlite3_value **argv
295){
296 sqlite3 *db = sqlite3_context_db_handle(context);
297 char *zVfs = 0;
drh841810c2013-04-08 13:59:11 +0000298 UNUSED_PARAMETER(argc);
299 UNUSED_PARAMETER(argv);
drh1bf44c72013-04-08 13:48:29 +0000300 sqlite3_file_control(db, "main", SQLITE_FCNTL_VFSNAME, &zVfs);
301 if( zVfs ){
302 sqlite3_result_text(context, zVfs, -1, sqlite3_free);
303 }
304}
305
306/*
drh3f5bc382013-04-06 13:09:11 +0000307** Busy handler with a g.iTimeout-millisecond timeout
308*/
309static int busyHandler(void *pCD, int count){
drh841810c2013-04-08 13:59:11 +0000310 UNUSED_PARAMETER(pCD);
drh3f5bc382013-04-06 13:09:11 +0000311 if( count*10>g.iTimeout ){
312 if( g.iTimeout>0 ) errorMessage("timeout after %dms", g.iTimeout);
313 return 0;
314 }
315 sqlite3_sleep(10);
316 return 1;
317}
318
319/*
drh27338e62013-04-06 00:19:37 +0000320** SQL Trace callback
321*/
322static void sqlTraceCallback(void *NotUsed1, const char *zSql){
drh841810c2013-04-08 13:59:11 +0000323 UNUSED_PARAMETER(NotUsed1);
drh27338e62013-04-06 00:19:37 +0000324 logMessage("[%.*s]", clipLength(zSql), zSql);
325}
326
327/*
drh1790bb32013-04-06 14:30:29 +0000328** SQL error log callback
329*/
330static void sqlErrorCallback(void *pArg, int iErrCode, const char *zMsg){
drh841810c2013-04-08 13:59:11 +0000331 UNUSED_PARAMETER(pArg);
drh1790bb32013-04-06 14:30:29 +0000332 if( (iErrCode&0xff)==SQLITE_SCHEMA && g.iTrace<3 ) return;
drhe5ebd222013-04-08 15:36:51 +0000333 if( g.iTimeout==0 && (iErrCode&0xff)==SQLITE_BUSY && g.iTrace<3 ) return;
drhe3be8c82013-04-11 11:53:45 +0000334 if( (iErrCode&0xff)==SQLITE_NOTICE ){
drhab755ac2013-04-09 18:36:36 +0000335 logMessage("(info) %s", zMsg);
336 }else{
337 errorMessage("(errcode=%d) %s", iErrCode, zMsg);
338 }
drh1790bb32013-04-06 14:30:29 +0000339}
340
341/*
drh27338e62013-04-06 00:19:37 +0000342** Prepare an SQL statement. Issue a fatal error if unable.
343*/
344static sqlite3_stmt *prepareSql(const char *zFormat, ...){
345 va_list ap;
346 char *zSql;
347 int rc;
348 sqlite3_stmt *pStmt = 0;
349 va_start(ap, zFormat);
350 zSql = sqlite3_vmprintf(zFormat, ap);
351 va_end(ap);
352 rc = sqlite3_prepare_v2(g.db, zSql, -1, &pStmt, 0);
353 if( rc!=SQLITE_OK ){
354 sqlite3_finalize(pStmt);
355 fatalError("%s\n%s\n", sqlite3_errmsg(g.db), zSql);
356 }
357 sqlite3_free(zSql);
358 return pStmt;
359}
360
361/*
362** Run arbitrary SQL. Issue a fatal error on failure.
363*/
364static void runSql(const char *zFormat, ...){
365 va_list ap;
366 char *zSql;
367 int rc;
368 va_start(ap, zFormat);
369 zSql = sqlite3_vmprintf(zFormat, ap);
370 va_end(ap);
371 rc = sqlite3_exec(g.db, zSql, 0, 0, 0);
372 if( rc!=SQLITE_OK ){
373 fatalError("%s\n%s\n", sqlite3_errmsg(g.db), zSql);
374 }
375 sqlite3_free(zSql);
376}
377
378/*
379** Try to run arbitrary SQL. Return success code.
380*/
381static int trySql(const char *zFormat, ...){
382 va_list ap;
383 char *zSql;
384 int rc;
385 va_start(ap, zFormat);
386 zSql = sqlite3_vmprintf(zFormat, ap);
387 va_end(ap);
388 rc = sqlite3_exec(g.db, zSql, 0, 0, 0);
389 sqlite3_free(zSql);
390 return rc;
391}
392
393/* Structure for holding an arbitrary length string
394*/
395typedef struct String String;
396struct String {
397 char *z; /* the string */
398 int n; /* Slots of z[] used */
399 int nAlloc; /* Slots of z[] allocated */
400};
401
402/* Free a string */
403static void stringFree(String *p){
404 if( p->z ) sqlite3_free(p->z);
405 memset(p, 0, sizeof(*p));
406}
407
408/* Append n bytes of text to a string. If n<0 append the entire string. */
409static void stringAppend(String *p, const char *z, int n){
410 if( n<0 ) n = (int)strlen(z);
411 if( p->n+n>=p->nAlloc ){
412 int nAlloc = p->nAlloc*2 + n + 100;
413 char *z = sqlite3_realloc(p->z, nAlloc);
414 if( z==0 ) fatalError("out of memory");
415 p->z = z;
416 p->nAlloc = nAlloc;
417 }
418 memcpy(p->z+p->n, z, n);
419 p->n += n;
420 p->z[p->n] = 0;
421}
422
423/* Reset a string to an empty string */
424static void stringReset(String *p){
425 if( p->z==0 ) stringAppend(p, " ", 1);
426 p->n = 0;
427 p->z[0] = 0;
428}
429
430/* Append a new token onto the end of the string */
431static void stringAppendTerm(String *p, const char *z){
432 int i;
433 if( p->n ) stringAppend(p, " ", 1);
434 if( z==0 ){
435 stringAppend(p, "nil", 3);
436 return;
437 }
438 for(i=0; z[i] && !isspace(z[i]); i++){}
439 if( i>0 && z[i]==0 ){
440 stringAppend(p, z, i);
441 return;
442 }
443 stringAppend(p, "'", 1);
444 while( z[0] ){
445 for(i=0; z[i] && z[i]!='\''; i++){}
446 if( z[i] ){
447 stringAppend(p, z, i+1);
448 stringAppend(p, "'", 1);
449 z += i+1;
450 }else{
451 stringAppend(p, z, i);
452 break;
453 }
454 }
455 stringAppend(p, "'", 1);
456}
457
458/*
459** Callback function for evalSql()
460*/
461static int evalCallback(void *pCData, int argc, char **argv, char **azCol){
462 String *p = (String*)pCData;
463 int i;
drh841810c2013-04-08 13:59:11 +0000464 UNUSED_PARAMETER(azCol);
drh27338e62013-04-06 00:19:37 +0000465 for(i=0; i<argc; i++) stringAppendTerm(p, argv[i]);
466 return 0;
467}
468
469/*
470** Run arbitrary SQL and record the results in an output string
471** given by the first parameter.
472*/
473static int evalSql(String *p, const char *zFormat, ...){
474 va_list ap;
475 char *zSql;
476 int rc;
477 char *zErrMsg = 0;
478 va_start(ap, zFormat);
479 zSql = sqlite3_vmprintf(zFormat, ap);
480 va_end(ap);
drh3f5bc382013-04-06 13:09:11 +0000481 assert( g.iTimeout>0 );
drh27338e62013-04-06 00:19:37 +0000482 rc = sqlite3_exec(g.db, zSql, evalCallback, p, &zErrMsg);
483 sqlite3_free(zSql);
484 if( rc ){
485 char zErr[30];
486 sqlite3_snprintf(sizeof(zErr), zErr, "error(%d)", rc);
487 stringAppendTerm(p, zErr);
488 if( zErrMsg ){
489 stringAppendTerm(p, zErrMsg);
490 sqlite3_free(zErrMsg);
491 }
492 }
493 return rc;
494}
495
496/*
drh1bf44c72013-04-08 13:48:29 +0000497** Auxiliary SQL function to recursively evaluate SQL.
498*/
499static void evalFunc(
500 sqlite3_context *context,
501 int argc,
502 sqlite3_value **argv
503){
504 sqlite3 *db = sqlite3_context_db_handle(context);
505 const char *zSql = (const char*)sqlite3_value_text(argv[0]);
506 String res;
507 char *zErrMsg = 0;
508 int rc;
drh841810c2013-04-08 13:59:11 +0000509 UNUSED_PARAMETER(argc);
drh1bf44c72013-04-08 13:48:29 +0000510 memset(&res, 0, sizeof(res));
511 rc = sqlite3_exec(db, zSql, evalCallback, &res, &zErrMsg);
512 if( zErrMsg ){
513 sqlite3_result_error(context, zErrMsg, -1);
514 sqlite3_free(zErrMsg);
515 }else if( rc ){
516 sqlite3_result_error_code(context, rc);
517 }else{
518 sqlite3_result_text(context, res.z, -1, SQLITE_TRANSIENT);
519 }
520 stringFree(&res);
521}
522
523/*
drh27338e62013-04-06 00:19:37 +0000524** Look up the next task for client iClient in the database.
525** Return the task script and the task number and mark that
526** task as being under way.
527*/
528static int startScript(
529 int iClient, /* The client number */
530 char **pzScript, /* Write task script here */
drh4c5298f2013-04-10 12:01:21 +0000531 int *pTaskId, /* Write task number here */
532 char **pzTaskName /* Name of the task */
drh27338e62013-04-06 00:19:37 +0000533){
534 sqlite3_stmt *pStmt = 0;
535 int taskId;
536 int rc;
drh3f5bc382013-04-06 13:09:11 +0000537 int totalTime = 0;
drh27338e62013-04-06 00:19:37 +0000538
539 *pzScript = 0;
drh3f5bc382013-04-06 13:09:11 +0000540 g.iTimeout = 0;
drh27338e62013-04-06 00:19:37 +0000541 while(1){
drhf90e50f2013-04-08 19:13:48 +0000542 rc = trySql("BEGIN IMMEDIATE");
drh27338e62013-04-06 00:19:37 +0000543 if( rc==SQLITE_BUSY ){
544 sqlite3_sleep(10);
drh3f5bc382013-04-06 13:09:11 +0000545 totalTime += 10;
drh27338e62013-04-06 00:19:37 +0000546 continue;
547 }
548 if( rc!=SQLITE_OK ){
drh6adab7a2013-04-08 18:58:00 +0000549 fatalError("in startScript: %s", sqlite3_errmsg(g.db));
drh27338e62013-04-06 00:19:37 +0000550 }
drh3f5bc382013-04-06 13:09:11 +0000551 if( g.nError || g.nTest ){
552 runSql("UPDATE counters SET nError=nError+%d, nTest=nTest+%d",
553 g.nError, g.nTest);
drh27338e62013-04-06 00:19:37 +0000554 g.nError = 0;
drh3f5bc382013-04-06 13:09:11 +0000555 g.nTest = 0;
556 }
557 pStmt = prepareSql("SELECT 1 FROM client WHERE id=%d AND wantHalt",iClient);
558 rc = sqlite3_step(pStmt);
559 sqlite3_finalize(pStmt);
560 if( rc==SQLITE_ROW ){
561 runSql("DELETE FROM client WHERE id=%d", iClient);
drh3f5bc382013-04-06 13:09:11 +0000562 g.iTimeout = DEFAULT_TIMEOUT;
drhf90e50f2013-04-08 19:13:48 +0000563 runSql("COMMIT TRANSACTION;");
drh3f5bc382013-04-06 13:09:11 +0000564 return SQLITE_DONE;
drh27338e62013-04-06 00:19:37 +0000565 }
566 pStmt = prepareSql(
drh4c5298f2013-04-10 12:01:21 +0000567 "SELECT script, id, name FROM task"
drh27338e62013-04-06 00:19:37 +0000568 " WHERE client=%d AND starttime IS NULL"
569 " ORDER BY id LIMIT 1", iClient);
570 rc = sqlite3_step(pStmt);
571 if( rc==SQLITE_ROW ){
572 int n = sqlite3_column_bytes(pStmt, 0);
drhf90e50f2013-04-08 19:13:48 +0000573 *pzScript = sqlite3_malloc(n+1);
drh27338e62013-04-06 00:19:37 +0000574 strcpy(*pzScript, (const char*)sqlite3_column_text(pStmt, 0));
575 *pTaskId = taskId = sqlite3_column_int(pStmt, 1);
drh4c5298f2013-04-10 12:01:21 +0000576 *pzTaskName = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 2));
drh27338e62013-04-06 00:19:37 +0000577 sqlite3_finalize(pStmt);
578 runSql("UPDATE task"
579 " SET starttime=strftime('%%Y-%%m-%%d %%H:%%M:%%f','now')"
580 " WHERE id=%d;", taskId);
drh3f5bc382013-04-06 13:09:11 +0000581 g.iTimeout = DEFAULT_TIMEOUT;
drhf90e50f2013-04-08 19:13:48 +0000582 runSql("COMMIT TRANSACTION;");
drh27338e62013-04-06 00:19:37 +0000583 return SQLITE_OK;
584 }
585 sqlite3_finalize(pStmt);
586 if( rc==SQLITE_DONE ){
drh3f5bc382013-04-06 13:09:11 +0000587 if( totalTime>30000 ){
588 errorMessage("Waited over 30 seconds with no work. Giving up.");
589 runSql("DELETE FROM client WHERE id=%d; COMMIT;", iClient);
590 sqlite3_close(g.db);
591 exit(1);
592 }
drhf90e50f2013-04-08 19:13:48 +0000593 while( trySql("COMMIT")==SQLITE_BUSY ){
594 sqlite3_sleep(10);
595 totalTime += 10;
596 }
drh27338e62013-04-06 00:19:37 +0000597 sqlite3_sleep(100);
drh3f5bc382013-04-06 13:09:11 +0000598 totalTime += 100;
drh27338e62013-04-06 00:19:37 +0000599 continue;
600 }
601 fatalError("%s", sqlite3_errmsg(g.db));
602 }
drh3f5bc382013-04-06 13:09:11 +0000603 g.iTimeout = DEFAULT_TIMEOUT;
drh27338e62013-04-06 00:19:37 +0000604}
605
606/*
drh3f5bc382013-04-06 13:09:11 +0000607** Mark a script as having finished. Remove the CLIENT table entry
608** if bShutdown is true.
drh27338e62013-04-06 00:19:37 +0000609*/
drh3f5bc382013-04-06 13:09:11 +0000610static int finishScript(int iClient, int taskId, int bShutdown){
611 runSql("UPDATE task"
612 " SET endtime=strftime('%%Y-%%m-%%d %%H:%%M:%%f','now')"
613 " WHERE id=%d;", taskId);
614 if( bShutdown ){
615 runSql("DELETE FROM client WHERE id=%d", iClient);
drh27338e62013-04-06 00:19:37 +0000616 }
drh3f5bc382013-04-06 13:09:11 +0000617 return SQLITE_OK;
618}
619
620/*
621** Start up a client process for iClient, if it is not already
622** running. If the client is already running, then this routine
623** is a no-op.
624*/
625static void startClient(int iClient){
626 runSql("INSERT OR IGNORE INTO client VALUES(%d,0)", iClient);
627 if( sqlite3_changes(g.db) ){
628 char *zSys;
drhbc94dbb2013-04-08 14:28:33 +0000629 int rc;
drh739ee7f2013-04-12 01:04:36 +0000630 zSys = sqlite3_mprintf("%s \"%s\" --client %d --trace %d",
631 g.argv0, g.zDbFile, iClient, g.iTrace);
632 if( g.bSqlTrace ){
633 zSys = sqlite3_mprintf("%z --sqltrace", zSys);
634 }
635 if( g.bSync ){
636 zSys = sqlite3_mprintf("%z --sync", zSys);
637 }
638 if( g.zVfs ){
639 zSys = sqlite3_mprintf("%z --vfs \"%s\"", zSys, g.zVfs);
640 }
641 if( g.iTrace>=2 ) logMessage("system('%q')", zSys);
mistachkin08d41892013-04-11 00:09:44 +0000642#if !defined(_WIN32)
drh739ee7f2013-04-12 01:04:36 +0000643 zSys = sqlite3_mprintf("%z &", zSys);
drhbc94dbb2013-04-08 14:28:33 +0000644 rc = system(zSys);
645 if( rc ) errorMessage("system() fails with error code %d", rc);
mistachkin08d41892013-04-11 00:09:44 +0000646#else
647 {
648 STARTUPINFOA startupInfo;
649 PROCESS_INFORMATION processInfo;
650 memset(&startupInfo, 0, sizeof(startupInfo));
651 startupInfo.cb = sizeof(startupInfo);
652 memset(&processInfo, 0, sizeof(processInfo));
653 rc = CreateProcessA(NULL, zSys, NULL, NULL, FALSE, 0, NULL, NULL,
654 &startupInfo, &processInfo);
655 if( rc ){
656 CloseHandle(processInfo.hThread);
657 CloseHandle(processInfo.hProcess);
658 }else{
659 errorMessage("CreateProcessA() fails with error code %lu",
660 GetLastError());
661 }
drhf012ae02013-04-06 14:04:22 +0000662 }
drhf012ae02013-04-06 14:04:22 +0000663#endif
mistachkin08d41892013-04-11 00:09:44 +0000664 sqlite3_free(zSys);
drh3f5bc382013-04-06 13:09:11 +0000665 }
drh27338e62013-04-06 00:19:37 +0000666}
667
668/*
669** Read the entire content of a file into memory
670*/
671static char *readFile(const char *zFilename){
672 FILE *in = fopen(zFilename, "rb");
673 long sz;
674 char *z;
675 if( in==0 ){
676 fatalError("cannot open \"%s\" for reading", zFilename);
677 }
678 fseek(in, 0, SEEK_END);
679 sz = ftell(in);
680 rewind(in);
681 z = sqlite3_malloc( sz+1 );
682 sz = (long)fread(z, 1, sz, in);
683 z[sz] = 0;
684 fclose(in);
685 return z;
686}
687
688/*
689** Return the length of the next token.
690*/
691static int tokenLength(const char *z, int *pnLine){
692 int n = 0;
693 if( isspace(z[0]) || (z[0]=='/' && z[1]=='*') ){
694 int inC = 0;
695 int c;
696 if( z[0]=='/' ){
697 inC = 1;
698 n = 2;
699 }
700 while( (c = z[n++])!=0 ){
701 if( c=='\n' ) (*pnLine)++;
702 if( isspace(c) ) continue;
703 if( inC && c=='*' && z[n]=='/' ){
704 n++;
705 inC = 0;
706 }else if( !inC && c=='/' && z[n]=='*' ){
707 n++;
708 inC = 1;
709 }else if( !inC ){
710 break;
711 }
712 }
713 n--;
714 }else if( z[0]=='-' && z[1]=='-' ){
715 for(n=2; z[n] && z[n]!='\n'; n++){}
716 if( z[n] ){ (*pnLine)++; n++; }
717 }else if( z[0]=='"' || z[0]=='\'' ){
718 int delim = z[0];
719 for(n=1; z[n]; n++){
720 if( z[n]=='\n' ) (*pnLine)++;
721 if( z[n]==delim ){
722 n++;
723 if( z[n+1]!=delim ) break;
724 }
725 }
726 }else{
727 int c;
728 for(n=1; (c = z[n])!=0 && !isspace(c) && c!='"' && c!='\'' && c!=';'; n++){}
729 }
730 return n;
731}
732
733/*
734** Copy a single token into a string buffer.
735*/
736static int extractToken(const char *zIn, int nIn, char *zOut, int nOut){
737 int i;
738 if( nIn<=0 ){
739 zOut[0] = 0;
740 return 0;
741 }
742 for(i=0; i<nIn && i<nOut-1 && !isspace(zIn[i]); i++){ zOut[i] = zIn[i]; }
743 zOut[i] = 0;
744 return i;
745}
746
747/*
drh7dfe8e22013-04-08 13:13:43 +0000748** Find the number of characters up to the start of the next "--end" token.
drh27338e62013-04-06 00:19:37 +0000749*/
750static int findEnd(const char *z, int *pnLine){
751 int n = 0;
752 while( z[n] && (strncmp(z+n,"--end",5) || !isspace(z[n+5])) ){
753 n += tokenLength(z+n, pnLine);
754 }
755 return n;
756}
757
758/*
drh7dfe8e22013-04-08 13:13:43 +0000759** Find the number of characters up to the first character past the
760** of the next "--endif" or "--else" token. Nested --if commands are
761** also skipped.
762*/
763static int findEndif(const char *z, int stopAtElse, int *pnLine){
764 int n = 0;
765 while( z[n] ){
766 int len = tokenLength(z+n, pnLine);
767 if( (strncmp(z+n,"--endif",7)==0 && isspace(z[n+7]))
768 || (stopAtElse && strncmp(z+n,"--else",6)==0 && isspace(z[n+6]))
769 ){
770 return n+len;
771 }
772 if( strncmp(z+n,"--if",4)==0 && isspace(z[n+4]) ){
773 int skip = findEndif(z+n+len, 0, pnLine);
774 n += skip + len;
775 }else{
776 n += len;
777 }
778 }
779 return n;
780}
781
782/*
drh27338e62013-04-06 00:19:37 +0000783** Wait for a client process to complete all its tasks
784*/
785static void waitForClient(int iClient, int iTimeout, char *zErrPrefix){
786 sqlite3_stmt *pStmt;
787 int rc;
788 if( iClient>0 ){
789 pStmt = prepareSql(
drh3f5bc382013-04-06 13:09:11 +0000790 "SELECT 1 FROM task"
791 " WHERE client=%d"
792 " AND client IN (SELECT id FROM client)"
793 " AND endtime IS NULL",
drh27338e62013-04-06 00:19:37 +0000794 iClient);
795 }else{
796 pStmt = prepareSql(
drh3f5bc382013-04-06 13:09:11 +0000797 "SELECT 1 FROM task"
798 " WHERE client IN (SELECT id FROM client)"
799 " AND endtime IS NULL");
drh27338e62013-04-06 00:19:37 +0000800 }
drh3f5bc382013-04-06 13:09:11 +0000801 g.iTimeout = 0;
drh27338e62013-04-06 00:19:37 +0000802 while( ((rc = sqlite3_step(pStmt))==SQLITE_BUSY || rc==SQLITE_ROW)
803 && iTimeout>0
804 ){
805 sqlite3_reset(pStmt);
806 sqlite3_sleep(50);
807 iTimeout -= 50;
808 }
809 sqlite3_finalize(pStmt);
drh3f5bc382013-04-06 13:09:11 +0000810 g.iTimeout = DEFAULT_TIMEOUT;
drh27338e62013-04-06 00:19:37 +0000811 if( rc!=SQLITE_DONE ){
812 if( zErrPrefix==0 ) zErrPrefix = "";
813 if( iClient>0 ){
814 errorMessage("%stimeout waiting for client %d", zErrPrefix, iClient);
815 }else{
816 errorMessage("%stimeout waiting for all clients", zErrPrefix);
817 }
818 }
819}
820
drh4c5298f2013-04-10 12:01:21 +0000821/* Return a pointer to the tail of a filename
822*/
823static char *filenameTail(char *z){
824 int i, j;
825 for(i=j=0; z[i]; i++) if( z[i]=='/' ) j = i+1;
826 return z+j;
827}
828
drh27338e62013-04-06 00:19:37 +0000829/* Maximum number of arguments to a --command */
drh7dfe8e22013-04-08 13:13:43 +0000830#define MX_ARG 2
drh27338e62013-04-06 00:19:37 +0000831
832/*
833** Run a script.
834*/
835static void runScript(
836 int iClient, /* The client number, or 0 for the master */
837 int taskId, /* The task ID for clients. 0 for master */
838 char *zScript, /* Text of the script */
839 char *zFilename /* File from which script was read. */
840){
841 int lineno = 1;
842 int prevLine = 1;
843 int ii = 0;
844 int iBegin = 0;
845 int n, c, j;
drh27338e62013-04-06 00:19:37 +0000846 int len;
847 int nArg;
848 String sResult;
849 char zCmd[30];
850 char zError[1000];
851 char azArg[MX_ARG][100];
drh27338e62013-04-06 00:19:37 +0000852
drh27338e62013-04-06 00:19:37 +0000853 memset(&sResult, 0, sizeof(sResult));
854 stringReset(&sResult);
855 while( (c = zScript[ii])!=0 ){
856 prevLine = lineno;
857 len = tokenLength(zScript+ii, &lineno);
858 if( isspace(c) || (c=='/' && zScript[ii+1]=='*') ){
859 ii += len;
860 continue;
861 }
862 if( c!='-' || zScript[ii+1]!='-' || !isalpha(zScript[ii+2]) ){
863 ii += len;
864 continue;
865 }
866
867 /* Run any prior SQL before processing the new --command */
868 if( ii>iBegin ){
869 char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
870 evalSql(&sResult, zSql);
871 sqlite3_free(zSql);
872 iBegin = ii + len;
873 }
874
875 /* Parse the --command */
876 if( g.iTrace>=2 ) logMessage("%.*s", len, zScript+ii);
877 n = extractToken(zScript+ii+2, len-2, zCmd, sizeof(zCmd));
878 for(nArg=0; n<len-2 && nArg<MX_ARG; nArg++){
879 while( n<len-2 && isspace(zScript[ii+2+n]) ){ n++; }
880 if( n>=len-2 ) break;
881 n += extractToken(zScript+ii+2+n, len-2-n,
882 azArg[nArg], sizeof(azArg[nArg]));
883 }
884 for(j=nArg; j<MX_ARG; j++) azArg[j++][0] = 0;
885
886 /*
887 ** --sleep N
888 **
889 ** Pause for N milliseconds
890 */
891 if( strcmp(zCmd, "sleep")==0 ){
892 sqlite3_sleep(atoi(azArg[0]));
893 }else
894
895 /*
896 ** --exit N
897 **
898 ** Exit this process. If N>0 then exit without shutting down
899 ** SQLite. (In other words, simulate a crash.)
900 */
drh6adab7a2013-04-08 18:58:00 +0000901 if( strcmp(zCmd, "exit")==0 && iClient>0 ){
drh27338e62013-04-06 00:19:37 +0000902 int rc = atoi(azArg[0]);
drh3f5bc382013-04-06 13:09:11 +0000903 finishScript(iClient, taskId, 1);
drh27338e62013-04-06 00:19:37 +0000904 if( rc==0 ) sqlite3_close(g.db);
905 exit(rc);
906 }else
907
908 /*
drh6adab7a2013-04-08 18:58:00 +0000909 ** --finish
910 **
911 ** Mark the current task as having finished, even if it is not.
912 ** This can be used in conjunction with --exit to simulate a crash.
913 */
914 if( strcmp(zCmd, "finish")==0 && iClient>0 ){
915 finishScript(iClient, taskId, 1);
916 }else
917
918 /*
drh7dfe8e22013-04-08 13:13:43 +0000919 ** --reset
drh27338e62013-04-06 00:19:37 +0000920 **
921 ** Reset accumulated results back to an empty string
922 */
923 if( strcmp(zCmd, "reset")==0 ){
924 stringReset(&sResult);
925 }else
926
927 /*
928 ** --match ANSWER...
929 **
930 ** Check to see if output matches ANSWER. Report an error if not.
931 */
932 if( strcmp(zCmd, "match")==0 ){
933 int jj;
934 char *zAns = zScript+ii;
935 for(jj=7; jj<len-1 && isspace(zAns[jj]); jj++){}
936 zAns += jj;
937 if( strncmp(sResult.z, zAns, len-jj-1) ){
938 errorMessage("line %d of %s:\nExpected [%.*s]\n Got [%s]",
939 prevLine, zFilename, len-jj-1, zAns, sResult.z);
940 }
drh3f5bc382013-04-06 13:09:11 +0000941 g.nTest++;
drh27338e62013-04-06 00:19:37 +0000942 stringReset(&sResult);
943 }else
944
945 /*
drh1bf44c72013-04-08 13:48:29 +0000946 ** --output
947 **
948 ** Output the result of the previous SQL.
949 */
950 if( strcmp(zCmd, "output")==0 ){
951 logMessage("%s", sResult.z);
952 }else
953
954 /*
drh27338e62013-04-06 00:19:37 +0000955 ** --source FILENAME
956 **
957 ** Run a subscript from a separate file.
958 */
959 if( strcmp(zCmd, "source")==0 ){
drhe348fc72013-04-06 18:35:07 +0000960 char *zNewFile, *zNewScript;
961 char *zToDel = 0;
962 zNewFile = azArg[0];
963 if( zNewFile[0]!='/' ){
964 int k;
965 for(k=(int)strlen(zFilename)-1; k>=0 && zFilename[k]!='/'; k--){}
966 if( k>0 ){
967 zNewFile = zToDel = sqlite3_mprintf("%.*s/%s", k,zFilename,zNewFile);
968 }
969 }
970 zNewScript = readFile(zNewFile);
drh27338e62013-04-06 00:19:37 +0000971 if( g.iTrace ) logMessage("begin script [%s]\n", zNewFile);
972 runScript(0, 0, zNewScript, zNewFile);
973 sqlite3_free(zNewScript);
974 if( g.iTrace ) logMessage("end script [%s]\n", zNewFile);
drhbc94dbb2013-04-08 14:28:33 +0000975 sqlite3_free(zToDel);
drh27338e62013-04-06 00:19:37 +0000976 }else
977
978 /*
979 ** --print MESSAGE....
980 **
981 ** Output the remainder of the line to the log file
982 */
983 if( strcmp(zCmd, "print")==0 ){
984 int jj;
985 for(jj=7; jj<len && isspace(zScript[ii+jj]); jj++){}
986 logMessage("%.*s", len-jj, zScript+ii+jj);
987 }else
988
989 /*
drh7dfe8e22013-04-08 13:13:43 +0000990 ** --if EXPR
991 **
992 ** Skip forward to the next matching --endif or --else if EXPR is false.
993 */
994 if( strcmp(zCmd, "if")==0 ){
995 int jj, rc;
996 sqlite3_stmt *pStmt;
997 for(jj=4; jj<len && isspace(zScript[ii+jj]); jj++){}
998 pStmt = prepareSql("SELECT %.*s", len-jj, zScript+ii+jj);
999 rc = sqlite3_step(pStmt);
1000 if( rc!=SQLITE_ROW || sqlite3_column_int(pStmt, 0)==0 ){
1001 ii += findEndif(zScript+ii+len, 1, &lineno);
1002 }
1003 sqlite3_finalize(pStmt);
1004 }else
1005
1006 /*
1007 ** --else
1008 **
1009 ** This command can only be encountered if currently inside an --if that
1010 ** is true. Skip forward to the next matching --endif.
1011 */
1012 if( strcmp(zCmd, "else")==0 ){
1013 ii += findEndif(zScript+ii+len, 0, &lineno);
1014 }else
1015
1016 /*
1017 ** --endif
1018 **
1019 ** This command can only be encountered if currently inside an --if that
1020 ** is true or an --else of a false if. This is a no-op.
1021 */
1022 if( strcmp(zCmd, "endif")==0 ){
1023 /* no-op */
1024 }else
1025
1026 /*
drh27338e62013-04-06 00:19:37 +00001027 ** --start CLIENT
1028 **
1029 ** Start up the given client.
1030 */
drh7dfe8e22013-04-08 13:13:43 +00001031 if( strcmp(zCmd, "start")==0 && iClient==0 ){
drh27338e62013-04-06 00:19:37 +00001032 int iNewClient = atoi(azArg[0]);
drh3f5bc382013-04-06 13:09:11 +00001033 if( iNewClient>0 ){
1034 startClient(iNewClient);
drh27338e62013-04-06 00:19:37 +00001035 }
drh27338e62013-04-06 00:19:37 +00001036 }else
1037
1038 /*
1039 ** --wait CLIENT TIMEOUT
1040 **
1041 ** Wait until all tasks complete for the given client. If CLIENT is
1042 ** "all" then wait for all clients to complete. Wait no longer than
1043 ** TIMEOUT milliseconds (default 10,000)
1044 */
drh7dfe8e22013-04-08 13:13:43 +00001045 if( strcmp(zCmd, "wait")==0 && iClient==0 ){
drh27338e62013-04-06 00:19:37 +00001046 int iTimeout = nArg>=2 ? atoi(azArg[1]) : 10000;
1047 sqlite3_snprintf(sizeof(zError),zError,"line %d of %s\n",
1048 prevLine, zFilename);
1049 waitForClient(atoi(azArg[0]), iTimeout, zError);
1050 }else
1051
1052 /*
1053 ** --task CLIENT
1054 ** <task-content-here>
1055 ** --end
1056 **
drh3f5bc382013-04-06 13:09:11 +00001057 ** Assign work to a client. Start the client if it is not running
1058 ** already.
drh27338e62013-04-06 00:19:37 +00001059 */
drh7dfe8e22013-04-08 13:13:43 +00001060 if( strcmp(zCmd, "task")==0 && iClient==0 ){
drh27338e62013-04-06 00:19:37 +00001061 int iTarget = atoi(azArg[0]);
1062 int iEnd;
1063 char *zTask;
drh4c5298f2013-04-10 12:01:21 +00001064 char *zTName;
drh27338e62013-04-06 00:19:37 +00001065 iEnd = findEnd(zScript+ii+len, &lineno);
drh3f5bc382013-04-06 13:09:11 +00001066 if( iTarget<0 ){
1067 errorMessage("line %d of %s: bad client number: %d",
drh27338e62013-04-06 00:19:37 +00001068 prevLine, zFilename, iTarget);
drh3f5bc382013-04-06 13:09:11 +00001069 }else{
1070 zTask = sqlite3_mprintf("%.*s", iEnd, zScript+ii+len);
drh4c5298f2013-04-10 12:01:21 +00001071 if( nArg>1 ){
1072 zTName = sqlite3_mprintf("%s", azArg[1]);
1073 }else{
1074 zTName = sqlite3_mprintf("%s:%d", filenameTail(zFilename), prevLine);
1075 }
drh3f5bc382013-04-06 13:09:11 +00001076 startClient(iTarget);
drh4c5298f2013-04-10 12:01:21 +00001077 runSql("INSERT INTO task(client,script,name)"
1078 " VALUES(%d,'%q',%Q)", iTarget, zTask, zTName);
drh3f5bc382013-04-06 13:09:11 +00001079 sqlite3_free(zTask);
drh4c5298f2013-04-10 12:01:21 +00001080 sqlite3_free(zTName);
drh27338e62013-04-06 00:19:37 +00001081 }
drh27338e62013-04-06 00:19:37 +00001082 iEnd += tokenLength(zScript+ii+len+iEnd, &lineno);
1083 len += iEnd;
1084 iBegin = ii+len;
1085 }else
1086
1087 /* error */{
1088 errorMessage("line %d of %s: unknown command --%s",
1089 prevLine, zFilename, zCmd);
1090 }
1091 ii += len;
1092 }
1093 if( iBegin<ii ){
1094 char *zSql = sqlite3_mprintf("%.*s", ii-iBegin, zScript+iBegin);
1095 runSql(zSql);
1096 sqlite3_free(zSql);
1097 }
1098 stringFree(&sResult);
1099}
1100
1101/*
1102** Look for a command-line option. If present, return a pointer.
1103** Return NULL if missing.
1104**
1105** hasArg==0 means the option is a flag. It is either present or not.
1106** hasArg==1 means the option has an argument. Return a pointer to the
1107** argument.
1108*/
1109static char *findOption(
1110 char **azArg,
1111 int *pnArg,
1112 const char *zOption,
1113 int hasArg
1114){
1115 int i, j;
1116 char *zReturn = 0;
1117 int nArg = *pnArg;
1118
1119 assert( hasArg==0 || hasArg==1 );
1120 for(i=0; i<nArg; i++){
1121 const char *z;
1122 if( i+hasArg >= nArg ) break;
1123 z = azArg[i];
1124 if( z[0]!='-' ) continue;
1125 z++;
1126 if( z[0]=='-' ){
1127 if( z[1]==0 ) break;
1128 z++;
1129 }
1130 if( strcmp(z,zOption)==0 ){
1131 if( hasArg && i==nArg-1 ){
1132 fatalError("command-line option \"--%s\" requires an argument", z);
1133 }
1134 if( hasArg ){
1135 zReturn = azArg[i+1];
1136 }else{
1137 zReturn = azArg[i];
1138 }
1139 j = i+1+(hasArg!=0);
1140 while( j<nArg ) azArg[i++] = azArg[j++];
1141 *pnArg = i;
1142 return zReturn;
1143 }
1144 }
1145 return zReturn;
1146}
1147
1148/* Print a usage message for the program and exit */
1149static void usage(const char *argv0){
1150 int i;
1151 const char *zTail = argv0;
1152 for(i=0; argv0[i]; i++){
1153 if( argv0[i]=='/' ) zTail = argv0+i+1;
1154 }
1155 fprintf(stderr,"Usage: %s DATABASE ?OPTIONS? ?SCRIPT?\n", zTail);
1156 exit(1);
1157}
1158
1159/* Report on unrecognized arguments */
1160static void unrecognizedArguments(
1161 const char *argv0,
1162 int nArg,
1163 char **azArg
1164){
1165 int i;
1166 fprintf(stderr,"%s: unrecognized arguments:", argv0);
1167 for(i=0; i<nArg; i++){
1168 fprintf(stderr," %s", azArg[i]);
1169 }
1170 fprintf(stderr,"\n");
1171 exit(1);
1172}
1173
1174int main(int argc, char **argv){
1175 const char *zClient;
1176 int iClient;
drhe348fc72013-04-06 18:35:07 +00001177 int n, i;
drh27338e62013-04-06 00:19:37 +00001178 int openFlags = SQLITE_OPEN_READWRITE;
1179 int rc;
1180 char *zScript;
1181 int taskId;
1182 const char *zTrace;
drhe348fc72013-04-06 18:35:07 +00001183 const char *zCOption;
drh27338e62013-04-06 00:19:37 +00001184
1185 g.argv0 = argv[0];
1186 g.iTrace = 1;
1187 if( argc<2 ) usage(argv[0]);
1188 g.zDbFile = argv[1];
drhe348fc72013-04-06 18:35:07 +00001189 if( strglob("*.test", g.zDbFile) ) usage(argv[0]);
1190 if( strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)!=0 ){
1191 fprintf(stderr, "SQLite library and header mismatch\n"
1192 "Library: %s\n"
1193 "Header: %s\n",
1194 sqlite3_sourceid(), SQLITE_SOURCE_ID);
1195 exit(1);
1196 }
drh27338e62013-04-06 00:19:37 +00001197 n = argc-2;
drhe3be8c82013-04-11 11:53:45 +00001198 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.mptest", GETPID());
drh27338e62013-04-06 00:19:37 +00001199 g.zVfs = findOption(argv+2, &n, "vfs", 1);
1200 zClient = findOption(argv+2, &n, "client", 1);
1201 g.zErrLog = findOption(argv+2, &n, "errlog", 1);
1202 g.zLog = findOption(argv+2, &n, "log", 1);
1203 zTrace = findOption(argv+2, &n, "trace", 1);
1204 if( zTrace ) g.iTrace = atoi(zTrace);
1205 if( findOption(argv+2, &n, "quiet", 0)!=0 ) g.iTrace = 0;
1206 g.bSqlTrace = findOption(argv+2, &n, "sqltrace", 0)!=0;
drhbc94dbb2013-04-08 14:28:33 +00001207 g.bSync = findOption(argv+2, &n, "sync", 0)!=0;
drh27338e62013-04-06 00:19:37 +00001208 if( g.zErrLog ){
1209 g.pErrLog = fopen(g.zErrLog, "a");
1210 }else{
1211 g.pErrLog = stderr;
1212 }
1213 if( g.zLog ){
1214 g.pLog = fopen(g.zLog, "a");
1215 }else{
1216 g.pLog = stdout;
1217 }
drhe3be8c82013-04-11 11:53:45 +00001218
drh1790bb32013-04-06 14:30:29 +00001219 sqlite3_config(SQLITE_CONFIG_LOG, sqlErrorCallback, 0);
drh27338e62013-04-06 00:19:37 +00001220 if( zClient ){
1221 iClient = atoi(zClient);
1222 if( iClient<1 ) fatalError("illegal client number: %d\n", iClient);
drhe3be8c82013-04-11 11:53:45 +00001223 sqlite3_snprintf(sizeof(g.zName), g.zName, "%05d.client%02d",
1224 GETPID(), iClient);
drh27338e62013-04-06 00:19:37 +00001225 }else{
drhe348fc72013-04-06 18:35:07 +00001226 if( g.iTrace>0 ){
1227 printf("With SQLite " SQLITE_VERSION " " SQLITE_SOURCE_ID "\n" );
1228 for(i=0; (zCOption = sqlite3_compileoption_get(i))!=0; i++){
1229 printf("-DSQLITE_%s\n", zCOption);
1230 }
1231 fflush(stdout);
1232 }
drh27338e62013-04-06 00:19:37 +00001233 iClient = 0;
1234 unlink(g.zDbFile);
1235 openFlags |= SQLITE_OPEN_CREATE;
1236 }
1237 rc = sqlite3_open_v2(g.zDbFile, &g.db, openFlags, g.zVfs);
1238 if( rc ) fatalError("cannot open [%s]", g.zDbFile);
drh3f5bc382013-04-06 13:09:11 +00001239 sqlite3_busy_handler(g.db, busyHandler, 0);
drh1bf44c72013-04-08 13:48:29 +00001240 sqlite3_create_function(g.db, "vfsname", 0, SQLITE_UTF8, 0,
1241 vfsNameFunc, 0, 0);
1242 sqlite3_create_function(g.db, "eval", 1, SQLITE_UTF8, 0,
1243 evalFunc, 0, 0);
drh3f5bc382013-04-06 13:09:11 +00001244 g.iTimeout = DEFAULT_TIMEOUT;
drh27338e62013-04-06 00:19:37 +00001245 if( g.bSqlTrace ) sqlite3_trace(g.db, sqlTraceCallback, 0);
drhbc94dbb2013-04-08 14:28:33 +00001246 if( !g.bSync ) trySql("PRAGMA synchronous=OFF");
drh27338e62013-04-06 00:19:37 +00001247 if( iClient>0 ){
1248 if( n>0 ) unrecognizedArguments(argv[0], n, argv+2);
1249 if( g.iTrace ) logMessage("start-client");
1250 while(1){
drh4c5298f2013-04-10 12:01:21 +00001251 char *zTaskName = 0;
1252 rc = startScript(iClient, &zScript, &taskId, &zTaskName);
drh27338e62013-04-06 00:19:37 +00001253 if( rc==SQLITE_DONE ) break;
drh4c5298f2013-04-10 12:01:21 +00001254 if( g.iTrace ) logMessage("begin %s (%d)", zTaskName, taskId);
drh27338e62013-04-06 00:19:37 +00001255 runScript(iClient, taskId, zScript, zTaskName);
drh4c5298f2013-04-10 12:01:21 +00001256 if( g.iTrace ) logMessage("end %s (%d)", zTaskName, taskId);
drh3f5bc382013-04-06 13:09:11 +00001257 finishScript(iClient, taskId, 0);
drh4c5298f2013-04-10 12:01:21 +00001258 sqlite3_free(zTaskName);
drh27338e62013-04-06 00:19:37 +00001259 sqlite3_sleep(10);
1260 }
1261 if( g.iTrace ) logMessage("end-client");
1262 }else{
1263 sqlite3_stmt *pStmt;
drh3f5bc382013-04-06 13:09:11 +00001264 int iTimeout;
drh27338e62013-04-06 00:19:37 +00001265 if( n==0 ){
1266 fatalError("missing script filename");
1267 }
1268 if( n>1 ) unrecognizedArguments(argv[0], n, argv+2);
1269 runSql(
1270 "CREATE TABLE task(\n"
1271 " id INTEGER PRIMARY KEY,\n"
drh4c5298f2013-04-10 12:01:21 +00001272 " name TEXT,\n"
drh27338e62013-04-06 00:19:37 +00001273 " client INTEGER,\n"
1274 " starttime DATE,\n"
1275 " endtime DATE,\n"
1276 " script TEXT\n"
1277 ");"
drh3f5bc382013-04-06 13:09:11 +00001278 "CREATE INDEX task_i1 ON task(client, starttime);\n"
1279 "CREATE INDEX task_i2 ON task(client, endtime);\n"
1280 "CREATE TABLE counters(nError,nTest);\n"
1281 "INSERT INTO counters VALUES(0,0);\n"
1282 "CREATE TABLE client(id INTEGER PRIMARY KEY, wantHalt);\n"
drh27338e62013-04-06 00:19:37 +00001283 );
1284 zScript = readFile(argv[2]);
1285 if( g.iTrace ) logMessage("begin script [%s]\n", argv[2]);
1286 runScript(0, 0, zScript, argv[2]);
1287 sqlite3_free(zScript);
1288 if( g.iTrace ) logMessage("end script [%s]\n", argv[2]);
1289 waitForClient(0, 2000, "during shutdown...\n");
drh3f5bc382013-04-06 13:09:11 +00001290 trySql("UPDATE client SET wantHalt=1");
1291 sqlite3_sleep(10);
1292 g.iTimeout = 0;
1293 iTimeout = 1000;
1294 while( ((rc = trySql("SELECT 1 FROM client"))==SQLITE_BUSY
1295 || rc==SQLITE_ROW) && iTimeout>0 ){
drh27338e62013-04-06 00:19:37 +00001296 sqlite3_sleep(10);
drh3f5bc382013-04-06 13:09:11 +00001297 iTimeout -= 10;
drh27338e62013-04-06 00:19:37 +00001298 }
1299 sqlite3_sleep(100);
drh3f5bc382013-04-06 13:09:11 +00001300 pStmt = prepareSql("SELECT nError, nTest FROM counters");
1301 iTimeout = 1000;
1302 while( (rc = sqlite3_step(pStmt))==SQLITE_BUSY && iTimeout>0 ){
drh27338e62013-04-06 00:19:37 +00001303 sqlite3_sleep(10);
drh3f5bc382013-04-06 13:09:11 +00001304 iTimeout -= 10;
drh27338e62013-04-06 00:19:37 +00001305 }
1306 if( rc==SQLITE_ROW ){
1307 g.nError += sqlite3_column_int(pStmt, 0);
drh3f5bc382013-04-06 13:09:11 +00001308 g.nTest += sqlite3_column_int(pStmt, 1);
drh27338e62013-04-06 00:19:37 +00001309 }
1310 sqlite3_finalize(pStmt);
1311 }
1312 sqlite3_close(g.db);
1313 maybeClose(g.pLog);
1314 maybeClose(g.pErrLog);
1315 if( iClient==0 ){
drh3f5bc382013-04-06 13:09:11 +00001316 printf("Summary: %d errors in %d tests\n", g.nError, g.nTest);
drh27338e62013-04-06 00:19:37 +00001317 }
1318 return g.nError>0;
1319}