4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 *************************************************************************
12 ** This file contains the source code for a standalone program used to
13 ** test the performance of the sessions module. Compile and run:
15 ** ./session_speed_test -help
27 /*************************************************************************
28 ** Start of generic command line parser.
30 #define CMDLINE_BARE 0
31 #define CMDLINE_INTEGER 1
32 #define CMDLINE_STRING 2
33 #define CMDLINE_BOOLEAN 3
35 typedef struct CmdLineOption CmdLineOption
;
36 struct CmdLineOption
{
37 const char *zText
; /* Name of command line option */
38 const char *zHelp
; /* Help text for option */
39 int eType
; /* One of the CMDLINE_* values */
40 int iOff
; /* Offset of output variable */
43 #define CMDLINE_INT32(x,y,z) {x, y, CMDLINE_INTEGER, z}
44 #define CMDLINE_BOOL(x,y,z) {x, y, CMDLINE_BOOLEAN, z}
45 #define CMDLINE_TEXT(x,y,z) {x, y, CMDLINE_STRING, z}
46 #define CMDLINE_NONE(x,y,z) {x, y, CMDLINE_BARE, z}
48 static void option_requires_argument_error(CmdLineOption
*pOpt
){
49 fprintf(stderr
, "Option requires a%s argument: %s\n",
50 pOpt
->eType
==CMDLINE_INTEGER
? "n integer" :
51 pOpt
->eType
==CMDLINE_STRING
? " string" : " boolean",
57 static void ambiguous_option_error(const char *zArg
){
58 fprintf(stderr
, "Option is ambiguous: %s\n", zArg
);
62 static void unknown_option_error(
68 fprintf(stderr
, "Unknown option: %s\n", zArg
);
69 fprintf(stderr
, "\nOptions are:\n");
70 fprintf(stderr
, " % -30sEcho command line options\n", "-cmdline:verbose");
71 for(i
=0; aOpt
[i
].zText
; i
++){
72 int eType
= aOpt
[i
].eType
;
73 char *zOpt
= sqlite3_mprintf("%s %s", aOpt
[i
].zText
,
74 eType
==CMDLINE_BARE
? "" :
75 eType
==CMDLINE_INTEGER
? "N" :
76 eType
==CMDLINE_BOOLEAN
? "BOOLEAN" : "TEXT"
78 fprintf(stderr
, " % -30s%s\n", zOpt
, aOpt
[i
].zHelp
);
82 fprintf(stderr
, "\n%s\n", zHelp
);
87 static int get_integer_option(CmdLineOption
*pOpt
, const char *zArg
){
96 if( zArg
[i
]<'0' || zArg
[i
]>'9' ) option_requires_argument_error(pOpt
);
97 iRet
= iRet
*10 + (zArg
[i
] - '0');
103 static int get_boolean_option(CmdLineOption
*pOpt
, const char *zArg
){
104 if( 0==sqlite3_stricmp(zArg
, "true") ) return 1;
105 if( 0==sqlite3_stricmp(zArg
, "1") ) return 1;
106 if( 0==sqlite3_stricmp(zArg
, "0") ) return 0;
107 if( 0==sqlite3_stricmp(zArg
, "false") ) return 0;
108 option_requires_argument_error(pOpt
);
112 static void parse_command_line(
120 char *pOut
= (char*)pStruct
;
124 for(iArg
=iStart
; iArg
<argc
; iArg
++){
125 const char *zArg
= argv
[iArg
];
126 int nArg
= strlen(zArg
);
130 for(iOpt
=0; aOpt
[iOpt
].zText
; iOpt
++){
131 CmdLineOption
*pOpt
= &aOpt
[iOpt
];
132 if( 0==sqlite3_strnicmp(pOpt
->zText
, zArg
, nArg
) ){
134 ambiguous_option_error(zArg
);
137 if( pOpt
->eType
==CMDLINE_BARE
){
138 *(int*)(&pOut
[pOpt
->iOff
]) = 1;
142 option_requires_argument_error(pOpt
);
144 switch( pOpt
->eType
){
145 case CMDLINE_INTEGER
:
146 *(int*)(&pOut
[pOpt
->iOff
]) = get_integer_option(pOpt
, argv
[iArg
]);
149 *(const char**)(&pOut
[pOpt
->iOff
]) = argv
[iArg
];
151 case CMDLINE_BOOLEAN
:
152 *(int*)(&pOut
[pOpt
->iOff
]) = get_boolean_option(pOpt
, argv
[iArg
]);
159 if( nMatch
==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg
, nArg
) ){
165 unknown_option_error(zArg
, aOpt
, zHelp
);
171 fprintf(stdout
, "Options are: ");
172 for(iOpt
=0; aOpt
[iOpt
].zText
; iOpt
++){
173 CmdLineOption
*pOpt
= &aOpt
[iOpt
];
174 if( pOpt
->eType
!=CMDLINE_BARE
|| *(int*)(&pOut
[pOpt
->iOff
]) ){
175 fprintf(stdout
, "%s ", pOpt
->zText
);
177 switch( pOpt
->eType
){
178 case CMDLINE_INTEGER
:
179 fprintf(stdout
, "%d ", *(int*)(&pOut
[pOpt
->iOff
]));
181 case CMDLINE_BOOLEAN
:
182 fprintf(stdout
, "%d ", *(int*)(&pOut
[pOpt
->iOff
]));
185 fprintf(stdout
, "%s ", *(const char**)(&pOut
[pOpt
->iOff
]));
189 fprintf(stdout
, "\n");
193 ** End of generic command line parser.
194 *************************************************************************/
196 static void abort_due_to_error(int rc
){
197 fprintf(stderr
, "Error: %d\n");
201 static void execsql(sqlite3
*db
, const char *zSql
){
202 int rc
= sqlite3_exec(db
, zSql
, 0, 0, 0);
203 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
206 static int xConflict(void *pCtx
, int eConflict
, sqlite3_changeset_iter
*p
){
207 return SQLITE_CHANGESET_ABORT
;
210 static void run_test(
216 sqlite3_session
*pSession
= 0;
217 sqlite3_stmt
*pStmt
= 0;
223 /* Attach a session object to database db */
224 rc
= sqlite3session_create(db
, "main", &pSession
);
225 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
227 /* Configure the session to capture changes on all tables */
228 rc
= sqlite3session_attach(pSession
, 0);
229 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
231 /* Prepare the SQL statement */
232 rc
= sqlite3_prepare(db
, zSql
, -1, &pStmt
, 0);
233 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
235 /* Open a transaction */
236 execsql(db
, "BEGIN");
238 /* Execute the SQL statement nRow times */
239 for(i
=0; i
<nRow
; i
++){
240 sqlite3_bind_int(pStmt
, 1, i
);
242 rc
= sqlite3_reset(pStmt
);
243 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
245 sqlite3_finalize(pStmt
);
247 /* Extract a changeset from the sessions object */
248 rc
= sqlite3session_changeset(pSession
, &nChangeset
, &pChangeset
);
249 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
250 execsql(db
, "COMMIT");
252 /* Apply the changeset to the second db */
253 rc
= sqlite3changeset_apply(db2
, nChangeset
, pChangeset
, 0, xConflict
, 0);
254 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
257 sqlite3_free(pChangeset
);
258 sqlite3session_delete(pSession
);
261 int main(int argc
, char **argv
){
269 struct Options o
= { 2500, 0, 0, 0, "session_speed_test.db" };
271 CmdLineOption aOpt
[] = {
272 CMDLINE_INT32( "-rows", "number of rows in test",
273 offsetof(struct Options
, nRow
) ),
274 CMDLINE_BOOL("-without-rowid", "use WITHOUT ROWID tables",
275 offsetof(struct Options
, bWithoutRowid
) ),
276 CMDLINE_BOOL("-integer", "use integer data (instead of text/blobs)",
277 offsetof(struct Options
, bInteger
) ),
278 CMDLINE_NONE("-all", "Run all 4 combos of -without-rowid and -integer",
279 offsetof(struct Options
, bAll
) ),
280 CMDLINE_TEXT("-database", "prefix for database files to use",
281 offsetof(struct Options
, zDb
) ),
285 const char *azCreate
[] = {
286 "CREATE TABLE t1(a PRIMARY KEY, b, c, d)",
287 "CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID",
290 const char *azInsert
[] = {
291 "INSERT INTO t1 VALUES("
292 "printf('%.8d',?), randomblob(50), randomblob(50), randomblob(50))",
293 "INSERT INTO t1 VALUES(?, random(), random(), random())"
296 const char *azUpdate
[] = {
297 "UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)",
298 "UPDATE t1 SET d = random() WHERE a = ?"
301 const char *azDelete
[] = {
302 "DELETE FROM t1 WHERE a = printf('%.8d',?)",
303 "DELETE FROM t1 WHERE a = ?"
313 parse_command_line(argc
, argv
, 1, aOpt
, (void*)&o
,
314 "This program creates two new, empty, databases each containing a single\n"
315 "table. It then does the following:\n\n"
316 " 1. Inserts -rows rows into the first database\n"
317 " 2. Updates each row in the first db\n"
318 " 3. Delete each row from the first db\n\n"
319 "The modifications made by each step are captured in a changeset and\n"
320 "applied to the second database.\n"
322 zDb2
= sqlite3_mprintf("%s2", o
.zDb
);
324 for(bWithoutRowid
=0; bWithoutRowid
<2; bWithoutRowid
++){
325 for(bInteger
=0; bInteger
<2; bInteger
++){
326 if( o
.bAll
|| (o
.bWithoutRowid
==bWithoutRowid
&& o
.bInteger
==bInteger
) ){
327 fprintf(stdout
, "Testing %s data with %s table\n",
328 bInteger
? "integer" : "blob/text",
329 bWithoutRowid
? "WITHOUT ROWID" : "rowid"
332 /* Open new database handles on two empty databases */
334 rc
= sqlite3_open(o
.zDb
, &db
);
335 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
337 rc
= sqlite3_open(zDb2
, &db2
);
338 if( rc
!=SQLITE_OK
) abort_due_to_error(rc
);
340 /* Create the schema in both databases. */
341 execsql(db
, azCreate
[o
.bWithoutRowid
]);
342 execsql(db2
, azCreate
[o
.bWithoutRowid
]);
344 /* Run the three tests */
345 run_test(db
, db2
, o
.nRow
, azInsert
[o
.bInteger
]);
346 run_test(db
, db2
, o
.nRow
, azUpdate
[o
.bInteger
]);
347 run_test(db
, db2
, o
.nRow
, azDelete
[o
.bInteger
]);
349 /* Close the db handles */