xref: /sqlite-3.40.0/test/kvtest.c (revision dfd0de82)
1 /*
2 ** 2016-12-28
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 file implements "key-value" performance test for SQLite.  The
14 ** purpose is to compare the speed of SQLite for accessing large BLOBs
15 ** versus reading those same BLOB values out of individual files in the
16 ** filesystem.
17 **
18 ** Run "kvtest" with no arguments for on-line help, or see comments below.
19 **
20 ** HOW TO COMPILE:
21 **
22 ** (1) Gather this source file and a recent SQLite3 amalgamation with its
23 **     header into the working directory.  You should have:
24 **
25 **          kvtest.c       >--- this file
26 **          sqlite3.c      \___ SQLite
27 **          sqlite3.h      /    amlagamation & header
28 **
29 ** (2) Run you compiler against the two C source code files.
30 **
31 **    (a) On linux or mac:
32 **
33 **        OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION"
34 **        gcc -Os -I. $OPTS kvtest.c sqlite3.c -o kvtest
35 **
36 **             The $OPTS options can be omitted.  The $OPTS merely omit
37 **             the need to link against -ldl and -lpthread, or whatever
38 **             the equivalent libraries are called on your system.
39 **
40 **    (b) Windows with MSVC:
41 **
42 **        cl -I. kvtest.c sqlite3.c
43 **
44 ** USAGE:
45 **
46 ** (1) Create a test database by running "kvtest init" with appropriate
47 **     options.  See the help message for available options.
48 **
49 ** (2) Construct the corresponding pile-of-files database on disk using
50 **     the "kvtest export" command.
51 **
52 ** (3) Run tests using "kvtest run" against either the SQLite database or
53 **     the pile-of-files database and with appropriate options.
54 **
55 ** For example:
56 **
57 **       ./kvtest init x1.db --count 100000 --size 10000
58 **       mkdir x1
59 **       ./kvtest export x1.db x1
60 **       ./kvtest run x1.db --count 10000 --max-id 1000000
61 **       ./kvtest run x1 --count 10000 --max-id 1000000
62 */
63 static const char zHelp[] =
64 "Usage: kvtest COMMAND ARGS...\n"
65 "\n"
66 "   kvtest init DBFILE --count N --size M --pagesize X\n"
67 "\n"
68 "        Generate a new test database file named DBFILE containing N\n"
69 "        BLOBs each of size M bytes.  The page size of the new database\n"
70 "        file will be X\n"
71 "\n"
72 "   kvtest export DBFILE DIRECTORY\n"
73 "\n"
74 "        Export all the blobs in the kv table of DBFILE into separate\n"
75 "        files in DIRECTORY.\n"
76 "\n"
77 "   kvtest run DBFILE [options]\n"
78 "\n"
79 "        Run a performance test.  DBFILE can be either the name of a\n"
80 "        database or a directory containing sample files.  Options:\n"
81 "\n"
82 "             --asc                Read blobs in ascending order\n"
83 "             --blob-api           Use the BLOB API\n"
84 "             --cache-size N       Database cache size\n"
85 "             --count N            Read N blobs\n"
86 "             --desc               Read blobs in descending order\n"
87 "             --max-id N           Maximum blob key to use\n"
88 "             --random             Read blobs in a random order\n"
89 "             --start N            Start reading with this blob key\n"
90 "             --stats              Output operating stats before exiting\n"
91 ;
92 
93 /* Reference resources used */
94 #include <stdio.h>
95 #include <stdlib.h>
96 #include <sys/types.h>
97 #include <sys/stat.h>
98 #include <assert.h>
99 #include <string.h>
100 #include "sqlite3.h"
101 
102 #ifndef _WIN32
103 # include <unistd.h>
104 #else
105   /* Provide Windows equivalent for the needed parts of unistd.h */
106 # include <io.h>
107 # define R_OK 2
108 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
109 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
110 # define access _access
111 #endif
112 
113 
114 /*
115 ** Show thqe help text and quit.
116 */
117 static void showHelp(void){
118   fprintf(stdout, "%s", zHelp);
119   exit(1);
120 }
121 
122 /*
123 ** Show an error message an quit.
124 */
125 static void fatalError(const char *zFormat, ...){
126   va_list ap;
127   fprintf(stdout, "ERROR: ");
128   va_start(ap, zFormat);
129   vfprintf(stdout, zFormat, ap);
130   va_end(ap);
131   fprintf(stdout, "\n");
132   exit(1);
133 }
134 
135 /*
136 ** Check the filesystem object zPath.  Determine what it is:
137 **
138 **    PATH_DIR     A directory
139 **    PATH_DB      An SQLite database
140 **    PATH_NEXIST  Does not exist
141 **    PATH_OTHER   Something else
142 */
143 #define PATH_DIR     1
144 #define PATH_DB      2
145 #define PATH_NEXIST  0
146 #define PATH_OTHER   99
147 static int pathType(const char *zPath){
148   struct stat x;
149   int rc;
150   if( access(zPath,R_OK) ) return PATH_NEXIST;
151   memset(&x, 0, sizeof(x));
152   rc = stat(zPath, &x);
153   if( rc<0 ) return PATH_OTHER;
154   if( S_ISDIR(x.st_mode) ) return PATH_DIR;
155   if( (x.st_size%512)==0 ) return PATH_DB;
156   return PATH_OTHER;
157 }
158 
159 /*
160 ** Return the size of a file in bytes.  Or return -1 if the
161 ** named object is not a regular file or does not exist.
162 */
163 static sqlite3_int64 fileSize(const char *zPath){
164   struct stat x;
165   int rc;
166   memset(&x, 0, sizeof(x));
167   rc = stat(zPath, &x);
168   if( rc<0 ) return -1;
169   if( !S_ISREG(x.st_mode) ) return -1;
170   return x.st_size;
171 }
172 
173 /*
174 ** A Pseudo-random number generator with a fixed seed.  Use this so
175 ** that the same sequence of "random" numbers are generated on each
176 ** run, for repeatability.
177 */
178 static unsigned int randInt(void){
179   static unsigned int x = 0x333a13cd;
180   static unsigned int y = 0xecb2adea;
181   x = (x>>1) ^ ((1+~(x&1)) & 0xd0000001);
182   y = y*1103515245 + 12345;
183   return x^y;
184 }
185 
186 /*
187 ** Do database initialization.
188 */
189 static int initMain(int argc, char **argv){
190   char *zDb;
191   int i, rc;
192   int nCount = 1000;
193   int sz = 10000;
194   int pgsz = 4096;
195   sqlite3 *db;
196   char *zSql;
197   char *zErrMsg = 0;
198 
199   assert( strcmp(argv[1],"init")==0 );
200   assert( argc>=3 );
201   zDb = argv[2];
202   for(i=3; i<argc; i++){
203     char *z = argv[i];
204     if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
205     if( z[1]=='-' ) z++;
206     if( strcmp(z, "-count")==0 ){
207       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
208       nCount = atoi(argv[++i]);
209       if( nCount<1 ) fatalError("the --count must be positive");
210       continue;
211     }
212     if( strcmp(z, "-size")==0 ){
213       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
214       sz = atoi(argv[++i]);
215       if( sz<1 ) fatalError("the --size must be positive");
216       continue;
217     }
218     if( strcmp(z, "-pagesize")==0 ){
219       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
220       pgsz = atoi(argv[++i]);
221       if( pgsz<512 || pgsz>65536 || ((pgsz-1)&pgsz)!=0 ){
222         fatalError("the --pagesize must be power of 2 between 512 and 65536");
223       }
224       continue;
225     }
226     fatalError("unknown option: \"%s\"", argv[i]);
227   }
228   rc = sqlite3_open(zDb, &db);
229   if( rc ){
230     fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
231   }
232   zSql = sqlite3_mprintf(
233     "DROP TABLE IF EXISTS kv;\n"
234     "PRAGMA page_size=%d;\n"
235     "VACUUM;\n"
236     "BEGIN;\n"
237     "CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);\n"
238     "WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<%d)"
239     " INSERT INTO kv(k,v) SELECT x, randomblob(%d) FROM c;\n"
240     "COMMIT;\n",
241     pgsz, nCount, sz
242   );
243   rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
244   if( rc ) fatalError("database create failed: %s", zErrMsg);
245   sqlite3_free(zSql);
246   sqlite3_close(db);
247   return 0;
248 }
249 
250 /*
251 ** Implementation of the "writefile(X,Y)" SQL function.  The argument Y
252 ** is written into file X.  The number of bytes written is returned.  Or
253 ** NULL is returned if something goes wrong, such as being unable to open
254 ** file X for writing.
255 */
256 static void writefileFunc(
257   sqlite3_context *context,
258   int argc,
259   sqlite3_value **argv
260 ){
261   FILE *out;
262   const char *z;
263   sqlite3_int64 rc;
264   const char *zFile;
265 
266   zFile = (const char*)sqlite3_value_text(argv[0]);
267   if( zFile==0 ) return;
268   out = fopen(zFile, "wb");
269   if( out==0 ) return;
270   z = (const char*)sqlite3_value_blob(argv[1]);
271   if( z==0 ){
272     rc = 0;
273   }else{
274     rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
275   }
276   fclose(out);
277   printf("\r%s   ", zFile); fflush(stdout);
278   sqlite3_result_int64(context, rc);
279 }
280 
281 /*
282 ** Export the kv table to individual files in the filesystem
283 */
284 static int exportMain(int argc, char **argv){
285   char *zDb;
286   char *zDir;
287   sqlite3 *db;
288   char *zSql;
289   int rc;
290   char *zErrMsg = 0;
291 
292   assert( strcmp(argv[1],"export")==0 );
293   assert( argc>=3 );
294   zDb = argv[2];
295   if( argc!=4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY");
296   zDir = argv[3];
297   if( pathType(zDir)!=PATH_DIR ){
298     fatalError("object \"%s\" is not a directory", zDir);
299   }
300   rc = sqlite3_open(zDb, &db);
301   if( rc ){
302     fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
303   }
304   sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
305                           writefileFunc, 0, 0);
306   zSql = sqlite3_mprintf(
307     "SELECT writefile(printf('%s/%%06d',k),v) FROM kv;",
308     zDir
309   );
310   rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
311   if( rc ) fatalError("database create failed: %s", zErrMsg);
312   sqlite3_free(zSql);
313   sqlite3_close(db);
314   printf("\n");
315   return 0;
316 }
317 
318 /*
319 ** Read the content of file zName into memory obtained from sqlite3_malloc64()
320 ** and return a pointer to the buffer. The caller is responsible for freeing
321 ** the memory.
322 **
323 ** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
324 ** read.
325 **
326 ** For convenience, a nul-terminator byte is always appended to the data read
327 ** from the file before the buffer is returned. This byte is not included in
328 ** the final value of (*pnByte), if applicable.
329 **
330 ** NULL is returned if any error is encountered. The final value of *pnByte
331 ** is undefined in this case.
332 */
333 static unsigned char *readFile(const char *zName, int *pnByte){
334   FILE *in;               /* FILE from which to read content of zName */
335   sqlite3_int64 nIn;      /* Size of zName in bytes */
336   size_t nRead;           /* Number of bytes actually read */
337   unsigned char *pBuf;    /* Content read from disk */
338 
339   nIn = fileSize(zName);
340   if( nIn<0 ) return 0;
341   in = fopen(zName, "rb");
342   if( in==0 ) return 0;
343   pBuf = sqlite3_malloc64( nIn );
344   if( pBuf==0 ) return 0;
345   nRead = fread(pBuf, nIn, 1, in);
346   fclose(in);
347   if( nRead!=1 ){
348     sqlite3_free(pBuf);
349     return 0;
350   }
351   if( pnByte ) *pnByte = nIn;
352   return pBuf;
353 }
354 
355 /*
356 ** Return the current time in milliseconds since the beginning of
357 ** the Julian epoch.
358 */
359 static sqlite3_int64 timeOfDay(void){
360   static sqlite3_vfs *clockVfs = 0;
361   sqlite3_int64 t;
362   if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
363   if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
364     clockVfs->xCurrentTimeInt64(clockVfs, &t);
365   }else{
366     double r;
367     clockVfs->xCurrentTime(clockVfs, &r);
368     t = (sqlite3_int64)(r*86400000.0);
369   }
370   return t;
371 }
372 
373 #ifdef __linux__
374 /*
375 ** Attempt to display I/O stats on Linux using /proc/PID/io
376 */
377 static void displayLinuxIoStats(FILE *out){
378   FILE *in;
379   char z[200];
380   sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
381   in = fopen(z, "rb");
382   if( in==0 ) return;
383   while( fgets(z, sizeof(z), in)!=0 ){
384     static const struct {
385       const char *zPattern;
386       const char *zDesc;
387     } aTrans[] = {
388       { "rchar: ",                  "Bytes received by read():" },
389       { "wchar: ",                  "Bytes sent to write():"    },
390       { "syscr: ",                  "Read() system calls:"      },
391       { "syscw: ",                  "Write() system calls:"     },
392       { "read_bytes: ",             "Bytes read from storage:"  },
393       { "write_bytes: ",            "Bytes written to storage:" },
394       { "cancelled_write_bytes: ",  "Cancelled write bytes:"    },
395     };
396     int i;
397     for(i=0; i<sizeof(aTrans)/sizeof(aTrans[0]); i++){
398       int n = (int)strlen(aTrans[i].zPattern);
399       if( strncmp(aTrans[i].zPattern, z, n)==0 ){
400         fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
401         break;
402       }
403     }
404   }
405   fclose(in);
406 }
407 #endif
408 
409 /*
410 ** Display memory stats.
411 */
412 static int display_stats(
413   sqlite3 *db,                    /* Database to query */
414   int bReset                      /* True to reset SQLite stats */
415 ){
416   int iCur;
417   int iHiwtr;
418   FILE *out = stdout;
419 
420   fprintf(out, "\n");
421 
422   iHiwtr = iCur = -1;
423   sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
424   fprintf(out,
425           "Memory Used:                         %d (max %d) bytes\n",
426           iCur, iHiwtr);
427   iHiwtr = iCur = -1;
428   sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
429   fprintf(out, "Number of Outstanding Allocations:   %d (max %d)\n",
430           iCur, iHiwtr);
431   iHiwtr = iCur = -1;
432   sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
433   fprintf(out,
434       "Number of Pcache Pages Used:         %d (max %d) pages\n",
435       iCur, iHiwtr);
436   iHiwtr = iCur = -1;
437   sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
438   fprintf(out,
439           "Number of Pcache Overflow Bytes:     %d (max %d) bytes\n",
440           iCur, iHiwtr);
441   iHiwtr = iCur = -1;
442   sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset);
443   fprintf(out,
444       "Number of Scratch Allocations Used:  %d (max %d)\n",
445       iCur, iHiwtr);
446   iHiwtr = iCur = -1;
447   sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset);
448   fprintf(out,
449           "Number of Scratch Overflow Bytes:    %d (max %d) bytes\n",
450           iCur, iHiwtr);
451   iHiwtr = iCur = -1;
452   sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
453   fprintf(out, "Largest Allocation:                  %d bytes\n",
454           iHiwtr);
455   iHiwtr = iCur = -1;
456   sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
457   fprintf(out, "Largest Pcache Allocation:           %d bytes\n",
458           iHiwtr);
459   iHiwtr = iCur = -1;
460   sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset);
461   fprintf(out, "Largest Scratch Allocation:          %d bytes\n",
462           iHiwtr);
463 
464   iHiwtr = iCur = -1;
465   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
466   fprintf(out, "Pager Heap Usage:                    %d bytes\n",
467       iCur);
468   iHiwtr = iCur = -1;
469   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
470   fprintf(out, "Page cache hits:                     %d\n", iCur);
471   iHiwtr = iCur = -1;
472   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
473   fprintf(out, "Page cache misses:                   %d\n", iCur);
474   iHiwtr = iCur = -1;
475   sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
476   fprintf(out, "Page cache writes:                   %d\n", iCur);
477   iHiwtr = iCur = -1;
478 
479 #ifdef __linux__
480   displayLinuxIoStats(out);
481 #endif
482 
483   /* Do not remove this machine readable comment: extra-stats-output-here */
484 
485   fprintf(out, "\n");
486   return 0;
487 }
488 
489 /* Blob access order */
490 #define ORDER_ASC     1
491 #define ORDER_DESC    2
492 #define ORDER_RANDOM  3
493 
494 /*
495 ** Run a performance test
496 */
497 static int runMain(int argc, char **argv){
498   int eType;                  /* Is zDb a database or a directory? */
499   char *zDb;                  /* Database or directory name */
500   int i;                      /* Loop counter */
501   int rc;                     /* Return code from SQLite calls */
502   int nCount = 1000;          /* Number of blob fetch operations */
503   int nExtra = 0;             /* Extra cycles */
504   int iKey = 1;               /* Next blob key */
505   int iMax = 1000;            /* Largest allowed key */
506   int iPagesize = 0;          /* Database page size */
507   int iCache = 1000;          /* Database cache size in kibibytes */
508   int bBlobApi = 0;           /* Use the incremental blob I/O API */
509   int bStats = 0;             /* Print stats before exiting */
510   int eOrder = ORDER_ASC;     /* Access order */
511   sqlite3 *db = 0;            /* Database connection */
512   sqlite3_stmt *pStmt = 0;    /* Prepared statement for SQL access */
513   sqlite3_blob *pBlob = 0;    /* Handle for incremental Blob I/O */
514   sqlite3_int64 tmStart;      /* Start time */
515   sqlite3_int64 tmElapsed;    /* Elapsed time */
516   int nData = 0;              /* Bytes of data */
517   sqlite3_int64 nTotal = 0;   /* Total data read */
518   unsigned char *pData;       /* Content of the blob */
519 
520 
521   assert( strcmp(argv[1],"run")==0 );
522   assert( argc>=3 );
523   zDb = argv[2];
524   eType = pathType(zDb);
525   if( eType==PATH_OTHER ) fatalError("unknown object type: \"%s\"", zDb);
526   if( eType==PATH_NEXIST ) fatalError("object does not exist: \"%s\"", zDb);
527   for(i=3; i<argc; i++){
528     char *z = argv[i];
529     if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
530     if( z[1]=='-' ) z++;
531     if( strcmp(z, "-count")==0 ){
532       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
533       nCount = atoi(argv[++i]);
534       if( nCount<1 ) fatalError("the --count must be positive");
535       continue;
536     }
537     if( strcmp(z, "-max-id")==0 ){
538       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
539       iMax = atoi(argv[++i]);
540       if( iMax<1 ) fatalError("the --max-id must be positive");
541       continue;
542     }
543     if( strcmp(z, "-start")==0 ){
544       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
545       iKey = atoi(argv[++i]);
546       if( iKey<1 ) fatalError("the --start must be positive");
547       continue;
548     }
549     if( strcmp(z, "-cache-size")==0 ){
550       if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
551       iCache = atoi(argv[++i]);
552       continue;
553     }
554     if( strcmp(z, "-random")==0 ){
555       eOrder = ORDER_RANDOM;
556       continue;
557     }
558     if( strcmp(z, "-asc")==0 ){
559       eOrder = ORDER_ASC;
560       continue;
561     }
562     if( strcmp(z, "-desc")==0 ){
563       eOrder = ORDER_DESC;
564       continue;
565     }
566     if( strcmp(z, "-blob-api")==0 ){
567       bBlobApi = 1;
568       continue;
569     }
570     if( strcmp(z, "-stats")==0 ){
571       bStats = 1;
572       continue;
573     }
574     fatalError("unknown option: \"%s\"", argv[i]);
575   }
576   tmStart = timeOfDay();
577   if( eType==PATH_DB ){
578     char *zSql;
579     rc = sqlite3_open(zDb, &db);
580     if( rc ){
581       fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
582     }
583     zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache);
584     sqlite3_exec(db, zSql, 0, 0, 0);
585     sqlite3_free(zSql);
586     pStmt = 0;
587     sqlite3_prepare_v2(db, "PRAGMA page_size", -1, &pStmt, 0);
588     if( sqlite3_step(pStmt)==SQLITE_ROW ){
589       iPagesize = sqlite3_column_int(pStmt, 0);
590     }
591     sqlite3_finalize(pStmt);
592     sqlite3_prepare_v2(db, "PRAGMA cache_size", -1, &pStmt, 0);
593     if( sqlite3_step(pStmt)==SQLITE_ROW ){
594       iCache = sqlite3_column_int(pStmt, 0);
595     }else{
596       iCache = 0;
597     }
598     sqlite3_finalize(pStmt);
599     pStmt = 0;
600     sqlite3_exec(db, "BEGIN", 0, 0, 0);
601   }
602   for(i=0; i<nCount; i++){
603     if( eType==PATH_DIR ){
604       /* CASE 1: Reading blobs out of separate files */
605       char *zKey;
606       zKey = sqlite3_mprintf("%s/%06d", zDb, iKey);
607       nData = 0;
608       pData = readFile(zKey, &nData);
609       sqlite3_free(zKey);
610       sqlite3_free(pData);
611     }else if( bBlobApi ){
612       /* CASE 2: Reading from database using the incremental BLOB I/O API */
613       if( pBlob==0 ){
614         rc = sqlite3_blob_open(db, "main", "kv", "v", iKey, 0, &pBlob);
615         if( rc ){
616           fatalError("could not open sqlite3_blob handle: %s",
617                      sqlite3_errmsg(db));
618         }
619       }else{
620         rc = sqlite3_blob_reopen(pBlob, iKey);
621       }
622       if( rc==SQLITE_OK ){
623         nData = sqlite3_blob_bytes(pBlob);
624         pData = sqlite3_malloc( nData+1 );
625         if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1);
626         rc = sqlite3_blob_read(pBlob, pData, nData, 0);
627         if( rc!=SQLITE_OK ){
628           fatalError("could not read the blob at %d: %s", iKey,
629                      sqlite3_errmsg(db));
630         }
631         sqlite3_free(pData);
632       }
633     }else{
634       /* CASE 3: Reading from database using SQL */
635       if( pStmt==0 ){
636         rc = sqlite3_prepare_v2(db,
637                "SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0);
638         if( rc ){
639           fatalError("cannot prepare query: %s", sqlite3_errmsg(db));
640         }
641       }else{
642         sqlite3_reset(pStmt);
643       }
644       sqlite3_bind_int(pStmt, 1, iKey);
645       rc = sqlite3_step(pStmt);
646       if( rc==SQLITE_ROW ){
647         nData = sqlite3_column_bytes(pStmt, 0);
648         pData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
649       }else{
650         nData = 0;
651       }
652     }
653     if( eOrder==ORDER_ASC ){
654       iKey++;
655       if( iKey>iMax ) iKey = 1;
656     }else if( eOrder==ORDER_DESC ){
657       iKey--;
658       if( iKey<=0 ) iKey = iMax;
659     }else{
660       iKey = (randInt()%iMax)+1;
661     }
662     nTotal += nData;
663     if( nData==0 ){ nCount++; nExtra++; }
664   }
665   if( pStmt ) sqlite3_finalize(pStmt);
666   if( pBlob ) sqlite3_blob_close(pBlob);
667   if( bStats ){
668     display_stats(db, 0);
669   }
670   if( db ) sqlite3_close(db);
671   tmElapsed = timeOfDay() - tmStart;
672   if( nExtra ){
673     printf("%d cycles due to %d misses\n", nCount, nExtra);
674   }
675   if( eType==PATH_DB ){
676     printf("SQLite version: %s\n", sqlite3_libversion());
677   }
678   printf("--count %d --max-id %d", nCount-nExtra, iMax);
679   if( eType==PATH_DB ){
680     printf(" --cache-size %d", iCache);
681   }
682   switch( eOrder ){
683     case ORDER_RANDOM:  printf(" --random\n");  break;
684     case ORDER_DESC:    printf(" --desc\n");    break;
685     default:            printf(" --asc\n");     break;
686   }
687   if( iPagesize ) printf("Database page size: %d\n", iPagesize);
688   printf("Total elapsed time: %.3f\n", tmElapsed/1000.0);
689   printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
690   printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
691   return 0;
692 }
693 
694 
695 int main(int argc, char **argv){
696   if( argc<3 ) showHelp();
697   if( strcmp(argv[1],"init")==0 ){
698     return initMain(argc, argv);
699   }
700   if( strcmp(argv[1],"export")==0 ){
701     return exportMain(argc, argv);
702   }
703   if( strcmp(argv[1],"run")==0 ){
704     return runMain(argc, argv);
705   }
706   showHelp();
707   return 0;
708 }
709