1 /*
2 ** 2016-12-17
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 program is designed for fuzz-testing SQLite database files.
14 **
15 ** This program reads fuzzed database files from the disk files named
16 ** on the command-line.  Each database is loaded into an in-memory
17 ** filesystem so that the original database file is unmolested.
18 **
19 ** The fuzzed database is then opened, and series of SQL statements
20 ** are run against the database to ensure that SQLite can safely handle
21 ** the fuzzed database.
22 */
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29 #define ISSPACE(X) isspace((unsigned char)(X))
30 #define ISDIGIT(X) isdigit((unsigned char)(X))
31 #include "sqlite3.h"
32 #ifdef __unix__
33 # include <signal.h>
34 # include <unistd.h>
35 #endif
36 
37 /*
38 ** Print sketchy documentation for this utility program
39 */
showHelp(const char * zArgv0)40 static void showHelp(const char *zArgv0){
41   printf("Usage: %s [options] DATABASE ...\n", zArgv0);
42   printf(
43 "Read databases into an in-memory filesystem.  Run test SQL as specified\n"
44 "by command-line arguments or from\n"
45 "\n"
46 "    SELECT group_concat(sql) FROM autoexec;\n"
47 "\n"
48 "Options:\n"
49 "  --help              Show this help text\n"
50 "  -q|--quiet          Reduced output\n"
51 "  --limit-mem N       Limit memory used by test SQLite instances to N bytes\n"
52 "  --limit-vdbe        Panic if any test runs for more than 100,000 cycles\n"
53 "  --no-lookaside      Disable the lookaside memory allocator\n"
54 "  --timeout N         Timeout after N seconds.\n"
55 "  --trace             Show the results of each SQL command\n"
56 "  -v|--verbose        Increased output.  Repeat for more output.\n"
57   );
58   exit(0);
59 }
60 
61 /*
62 ** Print an error message and quit.
63 */
fatalError(const char * zFormat,...)64 static void fatalError(const char *zFormat, ...){
65   va_list ap;
66   va_start(ap, zFormat);
67   vfprintf(stderr, zFormat, ap);
68   va_end(ap);
69   fprintf(stderr, "\n");
70   exit(1);
71 }
72 
73 /*
74 ** Files in the virtual file system.
75 */
76 typedef struct VFile VFile;
77 typedef struct VHandle VHandle;
78 struct VFile {
79   char *zFilename;      /* Filename. NULL for delete-on-close. From malloc() */
80   int sz;               /* Size of the file in bytes */
81   int nRef;             /* Number of references to this file */
82   unsigned char *a;     /* Content of the file.  From malloc() */
83 };
84 struct VHandle {
85   sqlite3_file base;    /* Base class.  Must be first */
86   VFile *pVFile;        /* The underlying file */
87 };
88 
89 /*
90 ** Maximum number of files in the in-memory virtual filesystem.
91 */
92 #define MX_FILE  10
93 
94 /*
95 ** Maximum allowed file size
96 */
97 #define MX_FILE_SZ 1000000
98 
99 /*
100 ** All global variables are gathered into the "g" singleton.
101 */
102 static struct GlobalVars {
103   VFile aFile[MX_FILE];            /* The virtual filesystem */
104 } g;
105 
106 
107 /*
108 ** Initialize the virtual file system.
109 */
formatVfs(void)110 static void formatVfs(void){
111   int i;
112   for(i=0; i<MX_FILE; i++){
113     g.aFile[i].sz = -1;
114     g.aFile[i].zFilename = 0;
115     g.aFile[i].a = 0;
116     g.aFile[i].nRef = 0;
117   }
118 }
119 
120 
121 /*
122 ** Erase all information in the virtual file system.
123 */
reformatVfs(void)124 static void reformatVfs(void){
125   int i;
126   for(i=0; i<MX_FILE; i++){
127     if( g.aFile[i].sz<0 ) continue;
128     if( g.aFile[i].zFilename ){
129       free(g.aFile[i].zFilename);
130       g.aFile[i].zFilename = 0;
131     }
132     if( g.aFile[i].nRef>0 ){
133       fatalError("file %d still open.  nRef=%d", i, g.aFile[i].nRef);
134     }
135     g.aFile[i].sz = -1;
136     free(g.aFile[i].a);
137     g.aFile[i].a = 0;
138     g.aFile[i].nRef = 0;
139   }
140 }
141 
142 /*
143 ** Find a VFile by name
144 */
findVFile(const char * zName)145 static VFile *findVFile(const char *zName){
146   int i;
147   if( zName==0 ) return 0;
148   for(i=0; i<MX_FILE; i++){
149     if( g.aFile[i].zFilename==0 ) continue;
150     if( strcmp(g.aFile[i].zFilename, zName)==0 ) return &g.aFile[i];
151   }
152   return 0;
153 }
154 
155 /*
156 ** Find a VFile called zName.  Initialize it to the content of
157 ** disk file zDiskFile.
158 **
159 ** Return NULL if the filesystem is full.
160 */
createVFile(const char * zName,const char * zDiskFile)161 static VFile *createVFile(const char *zName, const char *zDiskFile){
162   VFile *pNew = findVFile(zName);
163   int i;
164   FILE *in = 0;
165   long sz = 0;
166 
167   if( pNew ) return pNew;
168   for(i=0; i<MX_FILE && g.aFile[i].sz>=0; i++){}
169   if( i>=MX_FILE ) return 0;
170   if( zDiskFile ){
171     in = fopen(zDiskFile, "rb");
172     if( in==0 ) fatalError("no such file: \"%s\"", zDiskFile);
173     fseek(in, 0, SEEK_END);
174     sz = ftell(in);
175     rewind(in);
176   }
177   pNew = &g.aFile[i];
178   if( zName ){
179     int nName = (int)strlen(zName)+1;
180     pNew->zFilename = malloc(nName);
181     if( pNew->zFilename==0 ){
182       if( in ) fclose(in);
183       return 0;
184     }
185     memcpy(pNew->zFilename, zName, nName);
186   }else{
187     pNew->zFilename = 0;
188   }
189   pNew->nRef = 0;
190   pNew->sz = sz;
191   pNew->a = malloc(sz);
192   if( sz>0 ){
193     if( pNew->a==0 || fread(pNew->a, sz, 1, in)<1 ){
194       free(pNew->zFilename);
195       free(pNew->a);
196       pNew->a = 0;
197       pNew->zFilename = 0;
198       pNew->sz = -1;
199       pNew = 0;
200     }
201   }
202   if( in ) fclose(in);
203   return pNew;
204 }
205 
206 /* Methods for the VHandle object
207 */
inmemClose(sqlite3_file * pFile)208 static int inmemClose(sqlite3_file *pFile){
209   VHandle *p = (VHandle*)pFile;
210   VFile *pVFile = p->pVFile;
211   pVFile->nRef--;
212   if( pVFile->nRef==0 && pVFile->zFilename==0 ){
213     pVFile->sz = -1;
214     free(pVFile->a);
215     pVFile->a = 0;
216   }
217   return SQLITE_OK;
218 }
inmemRead(sqlite3_file * pFile,void * pData,int iAmt,sqlite3_int64 iOfst)219 static int inmemRead(
220   sqlite3_file *pFile,   /* Read from this open file */
221   void *pData,           /* Store content in this buffer */
222   int iAmt,              /* Bytes of content */
223   sqlite3_int64 iOfst    /* Start reading here */
224 ){
225   VHandle *pHandle = (VHandle*)pFile;
226   VFile *pVFile = pHandle->pVFile;
227   if( iOfst<0 || iOfst>=pVFile->sz ){
228     memset(pData, 0, iAmt);
229     return SQLITE_IOERR_SHORT_READ;
230   }
231   if( iOfst+iAmt>pVFile->sz ){
232     memset(pData, 0, iAmt);
233     iAmt = (int)(pVFile->sz - iOfst);
234     memcpy(pData, pVFile->a, iAmt);
235     return SQLITE_IOERR_SHORT_READ;
236   }
237   memcpy(pData, pVFile->a + iOfst, iAmt);
238   return SQLITE_OK;
239 }
inmemWrite(sqlite3_file * pFile,const void * pData,int iAmt,sqlite3_int64 iOfst)240 static int inmemWrite(
241   sqlite3_file *pFile,   /* Write to this file */
242   const void *pData,     /* Content to write */
243   int iAmt,              /* bytes to write */
244   sqlite3_int64 iOfst    /* Start writing here */
245 ){
246   VHandle *pHandle = (VHandle*)pFile;
247   VFile *pVFile = pHandle->pVFile;
248   if( iOfst+iAmt > pVFile->sz ){
249     unsigned char *aNew;
250     if( iOfst+iAmt >= MX_FILE_SZ ){
251       return SQLITE_FULL;
252     }
253     aNew = realloc(pVFile->a, (int)(iOfst+iAmt));
254     if( aNew==0 ){
255       return SQLITE_FULL;
256     }
257     pVFile->a = aNew;
258     if( iOfst > pVFile->sz ){
259       memset(pVFile->a + pVFile->sz, 0, (int)(iOfst - pVFile->sz));
260     }
261     pVFile->sz = (int)(iOfst + iAmt);
262   }
263   memcpy(pVFile->a + iOfst, pData, iAmt);
264   return SQLITE_OK;
265 }
inmemTruncate(sqlite3_file * pFile,sqlite3_int64 iSize)266 static int inmemTruncate(sqlite3_file *pFile, sqlite3_int64 iSize){
267   VHandle *pHandle = (VHandle*)pFile;
268   VFile *pVFile = pHandle->pVFile;
269   if( pVFile->sz>iSize && iSize>=0 ) pVFile->sz = (int)iSize;
270   return SQLITE_OK;
271 }
inmemSync(sqlite3_file * pFile,int flags)272 static int inmemSync(sqlite3_file *pFile, int flags){
273   return SQLITE_OK;
274 }
inmemFileSize(sqlite3_file * pFile,sqlite3_int64 * pSize)275 static int inmemFileSize(sqlite3_file *pFile, sqlite3_int64 *pSize){
276   *pSize = ((VHandle*)pFile)->pVFile->sz;
277   return SQLITE_OK;
278 }
inmemLock(sqlite3_file * pFile,int type)279 static int inmemLock(sqlite3_file *pFile, int type){
280   return SQLITE_OK;
281 }
inmemUnlock(sqlite3_file * pFile,int type)282 static int inmemUnlock(sqlite3_file *pFile, int type){
283   return SQLITE_OK;
284 }
inmemCheckReservedLock(sqlite3_file * pFile,int * pOut)285 static int inmemCheckReservedLock(sqlite3_file *pFile, int *pOut){
286   *pOut = 0;
287   return SQLITE_OK;
288 }
inmemFileControl(sqlite3_file * pFile,int op,void * pArg)289 static int inmemFileControl(sqlite3_file *pFile, int op, void *pArg){
290   return SQLITE_NOTFOUND;
291 }
inmemSectorSize(sqlite3_file * pFile)292 static int inmemSectorSize(sqlite3_file *pFile){
293   return 512;
294 }
inmemDeviceCharacteristics(sqlite3_file * pFile)295 static int inmemDeviceCharacteristics(sqlite3_file *pFile){
296   return
297       SQLITE_IOCAP_SAFE_APPEND |
298       SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
299       SQLITE_IOCAP_POWERSAFE_OVERWRITE;
300 }
301 
302 
303 /* Method table for VHandle
304 */
305 static sqlite3_io_methods VHandleMethods = {
306   /* iVersion    */  1,
307   /* xClose      */  inmemClose,
308   /* xRead       */  inmemRead,
309   /* xWrite      */  inmemWrite,
310   /* xTruncate   */  inmemTruncate,
311   /* xSync       */  inmemSync,
312   /* xFileSize   */  inmemFileSize,
313   /* xLock       */  inmemLock,
314   /* xUnlock     */  inmemUnlock,
315   /* xCheck...   */  inmemCheckReservedLock,
316   /* xFileCtrl   */  inmemFileControl,
317   /* xSectorSz   */  inmemSectorSize,
318   /* xDevchar    */  inmemDeviceCharacteristics,
319   /* xShmMap     */  0,
320   /* xShmLock    */  0,
321   /* xShmBarrier */  0,
322   /* xShmUnmap   */  0,
323   /* xFetch      */  0,
324   /* xUnfetch    */  0
325 };
326 
327 /*
328 ** Open a new file in the inmem VFS.  All files are anonymous and are
329 ** delete-on-close.
330 */
inmemOpen(sqlite3_vfs * pVfs,const char * zFilename,sqlite3_file * pFile,int openFlags,int * pOutFlags)331 static int inmemOpen(
332   sqlite3_vfs *pVfs,
333   const char *zFilename,
334   sqlite3_file *pFile,
335   int openFlags,
336   int *pOutFlags
337 ){
338   VFile *pVFile = createVFile(zFilename, 0);
339   VHandle *pHandle = (VHandle*)pFile;
340   if( pVFile==0 ){
341     return SQLITE_FULL;
342   }
343   pHandle->pVFile = pVFile;
344   pVFile->nRef++;
345   pFile->pMethods = &VHandleMethods;
346   if( pOutFlags ) *pOutFlags = openFlags;
347   return SQLITE_OK;
348 }
349 
350 /*
351 ** Delete a file by name
352 */
inmemDelete(sqlite3_vfs * pVfs,const char * zFilename,int syncdir)353 static int inmemDelete(
354   sqlite3_vfs *pVfs,
355   const char *zFilename,
356   int syncdir
357 ){
358   VFile *pVFile = findVFile(zFilename);
359   if( pVFile==0 ) return SQLITE_OK;
360   if( pVFile->nRef==0 ){
361     free(pVFile->zFilename);
362     pVFile->zFilename = 0;
363     pVFile->sz = -1;
364     free(pVFile->a);
365     pVFile->a = 0;
366     return SQLITE_OK;
367   }
368   return SQLITE_IOERR_DELETE;
369 }
370 
371 /* Check for the existance of a file
372 */
inmemAccess(sqlite3_vfs * pVfs,const char * zFilename,int flags,int * pResOut)373 static int inmemAccess(
374   sqlite3_vfs *pVfs,
375   const char *zFilename,
376   int flags,
377   int *pResOut
378 ){
379   VFile *pVFile = findVFile(zFilename);
380   *pResOut =  pVFile!=0;
381   return SQLITE_OK;
382 }
383 
384 /* Get the canonical pathname for a file
385 */
inmemFullPathname(sqlite3_vfs * pVfs,const char * zFilename,int nOut,char * zOut)386 static int inmemFullPathname(
387   sqlite3_vfs *pVfs,
388   const char *zFilename,
389   int nOut,
390   char *zOut
391 ){
392   sqlite3_snprintf(nOut, zOut, "%s", zFilename);
393   return SQLITE_OK;
394 }
395 
396 /*
397 ** Register the VFS that reads from the g.aFile[] set of files.
398 */
inmemVfsRegister(void)399 static void inmemVfsRegister(void){
400   static sqlite3_vfs inmemVfs;
401   sqlite3_vfs *pDefault = sqlite3_vfs_find(0);
402   inmemVfs.iVersion = 3;
403   inmemVfs.szOsFile = sizeof(VHandle);
404   inmemVfs.mxPathname = 200;
405   inmemVfs.zName = "inmem";
406   inmemVfs.xOpen = inmemOpen;
407   inmemVfs.xDelete = inmemDelete;
408   inmemVfs.xAccess = inmemAccess;
409   inmemVfs.xFullPathname = inmemFullPathname;
410   inmemVfs.xRandomness = pDefault->xRandomness;
411   inmemVfs.xSleep = pDefault->xSleep;
412   inmemVfs.xCurrentTimeInt64 = pDefault->xCurrentTimeInt64;
413   sqlite3_vfs_register(&inmemVfs, 0);
414 };
415 
416 /*
417 ** Timeout handler
418 */
419 #ifdef __unix__
timeoutHandler(int NotUsed)420 static void timeoutHandler(int NotUsed){
421   (void)NotUsed;
422   fatalError("timeout\n");
423 }
424 #endif
425 
426 /*
427 ** Set the an alarm to go off after N seconds.  Disable the alarm
428 ** if N==0
429 */
setAlarm(int N)430 static void setAlarm(int N){
431 #ifdef __unix__
432   alarm(N);
433 #else
434   (void)N;
435 #endif
436 }
437 /***************************************************************************
438 ** String accumulator object
439 */
440 typedef struct Str Str;
441 struct Str {
442   char *z;                /* The string.  Memory from malloc() */
443   sqlite3_uint64 n;       /* Bytes of input used */
444   sqlite3_uint64 nAlloc;  /* Bytes allocated to z[] */
445   int oomErr;             /* OOM error has been seen */
446 };
447 
448 /* Initialize a Str object */
StrInit(Str * p)449 static void StrInit(Str *p){
450   memset(p, 0, sizeof(*p));
451 }
452 
453 /* Append text to the end of a Str object */
StrAppend(Str * p,const char * z)454 static void StrAppend(Str *p, const char *z){
455   sqlite3_uint64 n = strlen(z);
456   if( p->n + n >= p->nAlloc ){
457     char *zNew;
458     sqlite3_uint64 nNew;
459     if( p->oomErr ) return;
460     nNew = p->nAlloc*2 + 100 + n;
461     zNew = sqlite3_realloc64(p->z, nNew);
462     if( zNew==0 ){
463       sqlite3_free(p->z);
464       memset(p, 0, sizeof(*p));
465       p->oomErr = 1;
466       return;
467     }
468     p->z = zNew;
469     p->nAlloc = nNew;
470   }
471   memcpy(p->z + p->n, z, (int)n);
472   p->n += n;
473   p->z[p->n] = 0;
474 }
475 
476 /* Return the current string content */
StrStr(Str * p)477 static char *StrStr(Str *p){
478  return p->z;
479 }
480 
481 /* Free the string */
StrFree(Str * p)482 static void StrFree(Str *p){
483   sqlite3_free(p->z);
484   StrInit(p);
485 }
486 
487 /*
488 ** Return the value of a hexadecimal digit.  Return -1 if the input
489 ** is not a hex digit.
490 */
hexDigitValue(char c)491 static int hexDigitValue(char c){
492   if( c>='0' && c<='9' ) return c - '0';
493   if( c>='a' && c<='f' ) return c - 'a' + 10;
494   if( c>='A' && c<='F' ) return c - 'A' + 10;
495   return -1;
496 }
497 
498 /*
499 ** Interpret zArg as an integer value, possibly with suffixes.
500 */
integerValue(const char * zArg)501 static int integerValue(const char *zArg){
502   sqlite3_int64 v = 0;
503   static const struct { char *zSuffix; int iMult; } aMult[] = {
504     { "KiB", 1024 },
505     { "MiB", 1024*1024 },
506     { "GiB", 1024*1024*1024 },
507     { "KB",  1000 },
508     { "MB",  1000000 },
509     { "GB",  1000000000 },
510     { "K",   1000 },
511     { "M",   1000000 },
512     { "G",   1000000000 },
513   };
514   int i;
515   int isNeg = 0;
516   if( zArg[0]=='-' ){
517     isNeg = 1;
518     zArg++;
519   }else if( zArg[0]=='+' ){
520     zArg++;
521   }
522   if( zArg[0]=='0' && zArg[1]=='x' ){
523     int x;
524     zArg += 2;
525     while( (x = hexDigitValue(zArg[0]))>=0 ){
526       v = (v<<4) + x;
527       zArg++;
528     }
529   }else{
530     while( ISDIGIT(zArg[0]) ){
531       v = v*10 + zArg[0] - '0';
532       zArg++;
533     }
534   }
535   for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
536     if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
537       v *= aMult[i].iMult;
538       break;
539     }
540   }
541   if( v>0x7fffffff ) fatalError("parameter too large - max 2147483648");
542   return (int)(isNeg? -v : v);
543 }
544 
545 /*
546 ** This callback is invoked by sqlite3_log().
547 */
sqlLog(void * pNotUsed,int iErrCode,const char * zMsg)548 static void sqlLog(void *pNotUsed, int iErrCode, const char *zMsg){
549   printf("LOG: (%d) %s\n", iErrCode, zMsg);
550   fflush(stdout);
551 }
552 
553 #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
554 /*
555 ** This an SQL progress handler.  After an SQL statement has run for
556 ** many steps, we want to interrupt it.  This guards against infinite
557 ** loops from recursive common table expressions.
558 **
559 ** *pVdbeLimitFlag is true if the --limit-vdbe command-line option is used.
560 ** In that case, hitting the progress handler is a fatal error.
561 */
progressHandler(void * pVdbeLimitFlag)562 static int progressHandler(void *pVdbeLimitFlag){
563   if( *(int*)pVdbeLimitFlag ) fatalError("too many VDBE cycles");
564   return 1;
565 }
566 #endif
567 
568 /*
569 ** Allowed values for the runFlags parameter to runSql()
570 */
571 #define SQL_TRACE  0x0001     /* Print each SQL statement as it is prepared */
572 #define SQL_OUTPUT 0x0002     /* Show the SQL output */
573 
574 /*
575 ** Run multiple commands of SQL.  Similar to sqlite3_exec(), but does not
576 ** stop if an error is encountered.
577 */
runSql(sqlite3 * db,const char * zSql,unsigned runFlags)578 static void runSql(sqlite3 *db, const char *zSql, unsigned  runFlags){
579   const char *zMore;
580   const char *zEnd = &zSql[strlen(zSql)];
581   sqlite3_stmt *pStmt;
582 
583   while( zSql && zSql[0] ){
584     zMore = 0;
585     pStmt = 0;
586     sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zMore);
587     assert( zMore<=zEnd );
588     if( zMore==zSql ) break;
589     if( runFlags & SQL_TRACE ){
590       const char *z = zSql;
591       int n;
592       while( z<zMore && ISSPACE(z[0]) ) z++;
593       n = (int)(zMore - z);
594       while( n>0 && ISSPACE(z[n-1]) ) n--;
595       if( n==0 ) break;
596       if( pStmt==0 ){
597         printf("TRACE: %.*s (error: %s)\n", n, z, sqlite3_errmsg(db));
598       }else{
599         printf("TRACE: %.*s\n", n, z);
600       }
601     }
602     zSql = zMore;
603     if( pStmt ){
604       if( (runFlags & SQL_OUTPUT)==0 ){
605         while( SQLITE_ROW==sqlite3_step(pStmt) ){}
606       }else{
607         int nCol = -1;
608         int nRow;
609         for(nRow=0; SQLITE_ROW==sqlite3_step(pStmt); nRow++){
610           int i;
611           if( nCol<0 ){
612             nCol = sqlite3_column_count(pStmt);
613           }
614           for(i=0; i<nCol; i++){
615             int eType = sqlite3_column_type(pStmt,i);
616             printf("ROW[%d].%s = ", nRow, sqlite3_column_name(pStmt,i));
617             switch( eType ){
618               case SQLITE_NULL: {
619                 printf("NULL\n");
620                 break;
621               }
622               case SQLITE_INTEGER: {
623                 printf("INT %s\n", sqlite3_column_text(pStmt,i));
624                 break;
625               }
626               case SQLITE_FLOAT: {
627                 printf("FLOAT %s\n", sqlite3_column_text(pStmt,i));
628                 break;
629               }
630               case SQLITE_TEXT: {
631                 printf("TEXT [%s]\n", sqlite3_column_text(pStmt,i));
632                 break;
633               }
634               case SQLITE_BLOB: {
635                 printf("BLOB (%d bytes)\n", sqlite3_column_bytes(pStmt,i));
636                 break;
637               }
638             }
639           }
640         }
641       }
642       sqlite3_finalize(pStmt);
643     }
644   }
645 }
646 
main(int argc,char ** argv)647 int main(int argc, char **argv){
648   int i;                 /* Loop counter */
649   int nDb = 0;           /* Number of databases to fuzz */
650   char **azDb = 0;       /* Names of the databases (limit: 20) */
651   int verboseFlag = 0;   /* True for extra output */
652   int noLookaside = 0;   /* Disable lookaside if true */
653   int vdbeLimitFlag = 0; /* Stop after 100,000 VDBE ops */
654   int nHeap = 0;         /* True for fixed heap size */
655   int iTimeout = 0;      /* Timeout delay in seconds */
656   int rc;                /* Result code from SQLite3 API calls */
657   sqlite3 *db;           /* The database connection */
658   sqlite3_stmt *pStmt;   /* A single SQL statement */
659   Str sql;               /* SQL to run */
660   unsigned runFlags = 0; /* Flags passed to runSql */
661 
662   for(i=1; i<argc; i++){
663     char *z = argv[i];
664     if( z[0]!='-' ){
665       azDb = realloc(azDb, sizeof(azDb[0])*(nDb+1));
666       if( azDb==0 ) fatalError("out of memory");
667       azDb[nDb++] = z;
668       continue;
669     }
670     z++;
671     if( z[0]=='-' ) z++;
672     if( strcmp(z, "help")==0 ){
673       showHelp(argv[0]);
674     }else if( strcmp(z, "limit-mem")==0 ){
675       if( i==argc-1 ) fatalError("missing argument to %s", argv[i]);
676       nHeap = integerValue(argv[++i]);
677     }else if( strcmp(z, "no-lookaside")==0 ){
678       noLookaside = 1;
679     }else if( strcmp(z, "timeout")==0 ){
680       if( i==argc-1 ) fatalError("missing argument to %s", argv[i]);
681       iTimeout = integerValue(argv[++i]);
682     }else if( strcmp(z, "trace")==0 ){
683       runFlags |= SQL_OUTPUT|SQL_TRACE;
684     }else if( strcmp(z, "limit-vdbe")==0 ){
685       vdbeLimitFlag = 1;
686     }else if( strcmp(z, "v")==0 || strcmp(z, "verbose")==0 ){
687       verboseFlag = 1;
688       runFlags |= SQL_TRACE;
689     }else{
690       fatalError("unknown command-line option: \"%s\"\n", argv[i]);
691     }
692   }
693   if( nDb==0 ){
694     showHelp(argv[0]);
695   }
696   if( verboseFlag ){
697     sqlite3_config(SQLITE_CONFIG_LOG, sqlLog);
698   }
699   if( nHeap>0 ){
700     void *pHeap = malloc( nHeap );
701     if( pHeap==0 ) fatalError("cannot allocate %d-byte heap\n", nHeap);
702     rc = sqlite3_config(SQLITE_CONFIG_HEAP, pHeap, nHeap, 32);
703     if( rc ) fatalError("heap configuration failed: %d\n", rc);
704   }
705   if( noLookaside ){
706     sqlite3_config(SQLITE_CONFIG_LOOKASIDE, 0, 0);
707   }
708   inmemVfsRegister();
709   formatVfs();
710   StrInit(&sql);
711 #ifdef __unix__
712   signal(SIGALRM, timeoutHandler);
713 #endif
714   for(i=0; i<nDb; i++){
715     if( verboseFlag && nDb>1 ){
716       printf("DATABASE-FILE: %s\n", azDb[i]);
717       fflush(stdout);
718     }
719     if( iTimeout ) setAlarm(iTimeout);
720     createVFile("test.db", azDb[i]);
721     rc = sqlite3_open_v2("test.db", &db, SQLITE_OPEN_READWRITE, "inmem");
722     if( rc ){
723       printf("cannot open test.db for \"%s\"\n", azDb[i]);
724       reformatVfs();
725       continue;
726     }
727 #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
728     if( vdbeLimitFlag ){
729       sqlite3_progress_handler(db, 100000, progressHandler, &vdbeLimitFlag);
730     }
731 #endif
732     rc = sqlite3_prepare_v2(db, "SELECT sql FROM autoexec", -1, &pStmt, 0);
733     if( rc==SQLITE_OK ){
734       while( SQLITE_ROW==sqlite3_step(pStmt) ){
735         StrAppend(&sql, (const char*)sqlite3_column_text(pStmt, 0));
736         StrAppend(&sql, "\n");
737       }
738     }
739     sqlite3_finalize(pStmt);
740     StrAppend(&sql, "PRAGMA integrity_check;\n");
741     runSql(db, StrStr(&sql), runFlags);
742     sqlite3_close(db);
743     reformatVfs();
744     StrFree(&sql);
745     if( sqlite3_memory_used()>0 ){
746       free(azDb);
747       reformatVfs();
748       fatalError("memory leak of %lld bytes", sqlite3_memory_used());
749     }
750   }
751   StrFree(&sql);
752   reformatVfs();
753   return 0;
754 }
755