1*610e17bdSdan /*
2*610e17bdSdan ** 2017 January 31
3*610e17bdSdan **
4*610e17bdSdan ** The author disclaims copyright to this source code. In place of
5*610e17bdSdan ** a legal notice, here is a blessing:
6*610e17bdSdan **
7*610e17bdSdan ** May you do good and not evil.
8*610e17bdSdan ** May you find forgiveness for yourself and forgive others.
9*610e17bdSdan ** May you share freely, never taking more than you give.
10*610e17bdSdan **
11*610e17bdSdan *************************************************************************
12*610e17bdSdan ** This file contains the source code for a standalone program used to
13*610e17bdSdan ** test the performance of the sessions module. Compile and run:
14*610e17bdSdan **
15*610e17bdSdan ** ./session_speed_test -help
16*610e17bdSdan **
17*610e17bdSdan ** for details.
18*610e17bdSdan */
19*610e17bdSdan
20*610e17bdSdan #include "sqlite3.h"
21*610e17bdSdan #include <stdio.h>
22*610e17bdSdan #include <stdlib.h>
23*610e17bdSdan #include <string.h>
24*610e17bdSdan #include <stddef.h>
25*610e17bdSdan #include <unistd.h>
26*610e17bdSdan
27*610e17bdSdan /*************************************************************************
28*610e17bdSdan ** Start of generic command line parser.
29*610e17bdSdan */
30*610e17bdSdan #define CMDLINE_BARE 0
31*610e17bdSdan #define CMDLINE_INTEGER 1
32*610e17bdSdan #define CMDLINE_STRING 2
33*610e17bdSdan #define CMDLINE_BOOLEAN 3
34*610e17bdSdan
35*610e17bdSdan typedef struct CmdLineOption CmdLineOption;
36*610e17bdSdan struct CmdLineOption {
37*610e17bdSdan const char *zText; /* Name of command line option */
38*610e17bdSdan const char *zHelp; /* Help text for option */
39*610e17bdSdan int eType; /* One of the CMDLINE_* values */
40*610e17bdSdan int iOff; /* Offset of output variable */
41*610e17bdSdan };
42*610e17bdSdan
43*610e17bdSdan #define CMDLINE_INT32(x,y,z) {x, y, CMDLINE_INTEGER, z}
44*610e17bdSdan #define CMDLINE_BOOL(x,y,z) {x, y, CMDLINE_BOOLEAN, z}
45*610e17bdSdan #define CMDLINE_TEXT(x,y,z) {x, y, CMDLINE_STRING, z}
46*610e17bdSdan #define CMDLINE_NONE(x,y,z) {x, y, CMDLINE_BARE, z}
47*610e17bdSdan
option_requires_argument_error(CmdLineOption * pOpt)48*610e17bdSdan static void option_requires_argument_error(CmdLineOption *pOpt){
49*610e17bdSdan fprintf(stderr, "Option requires a%s argument: %s\n",
50*610e17bdSdan pOpt->eType==CMDLINE_INTEGER ? "n integer" :
51*610e17bdSdan pOpt->eType==CMDLINE_STRING ? " string" : " boolean",
52*610e17bdSdan pOpt->zText
53*610e17bdSdan );
54*610e17bdSdan exit(1);
55*610e17bdSdan }
56*610e17bdSdan
ambiguous_option_error(const char * zArg)57*610e17bdSdan static void ambiguous_option_error(const char *zArg){
58*610e17bdSdan fprintf(stderr, "Option is ambiguous: %s\n", zArg);
59*610e17bdSdan exit(1);
60*610e17bdSdan }
61*610e17bdSdan
unknown_option_error(const char * zArg,CmdLineOption * aOpt,const char * zHelp)62*610e17bdSdan static void unknown_option_error(
63*610e17bdSdan const char *zArg,
64*610e17bdSdan CmdLineOption *aOpt,
65*610e17bdSdan const char *zHelp
66*610e17bdSdan ){
67*610e17bdSdan int i;
68*610e17bdSdan fprintf(stderr, "Unknown option: %s\n", zArg);
69*610e17bdSdan fprintf(stderr, "\nOptions are:\n");
70*610e17bdSdan fprintf(stderr, " % -30sEcho command line options\n", "-cmdline:verbose");
71*610e17bdSdan for(i=0; aOpt[i].zText; i++){
72*610e17bdSdan int eType = aOpt[i].eType;
73*610e17bdSdan char *zOpt = sqlite3_mprintf("%s %s", aOpt[i].zText,
74*610e17bdSdan eType==CMDLINE_BARE ? "" :
75*610e17bdSdan eType==CMDLINE_INTEGER ? "N" :
76*610e17bdSdan eType==CMDLINE_BOOLEAN ? "BOOLEAN" : "TEXT"
77*610e17bdSdan );
78*610e17bdSdan fprintf(stderr, " % -30s%s\n", zOpt, aOpt[i].zHelp);
79*610e17bdSdan sqlite3_free(zOpt);
80*610e17bdSdan }
81*610e17bdSdan if( zHelp ){
82*610e17bdSdan fprintf(stderr, "\n%s\n", zHelp);
83*610e17bdSdan }
84*610e17bdSdan exit(1);
85*610e17bdSdan }
86*610e17bdSdan
get_integer_option(CmdLineOption * pOpt,const char * zArg)87*610e17bdSdan static int get_integer_option(CmdLineOption *pOpt, const char *zArg){
88*610e17bdSdan int i = 0;
89*610e17bdSdan int iRet = 0;
90*610e17bdSdan int bSign = 1;
91*610e17bdSdan if( zArg[0]=='-' ){
92*610e17bdSdan bSign = -1;
93*610e17bdSdan i = 1;
94*610e17bdSdan }
95*610e17bdSdan while( zArg[i] ){
96*610e17bdSdan if( zArg[i]<'0' || zArg[i]>'9' ) option_requires_argument_error(pOpt);
97*610e17bdSdan iRet = iRet*10 + (zArg[i] - '0');
98*610e17bdSdan i++;
99*610e17bdSdan }
100*610e17bdSdan return (iRet*bSign);
101*610e17bdSdan }
102*610e17bdSdan
get_boolean_option(CmdLineOption * pOpt,const char * zArg)103*610e17bdSdan static int get_boolean_option(CmdLineOption *pOpt, const char *zArg){
104*610e17bdSdan if( 0==sqlite3_stricmp(zArg, "true") ) return 1;
105*610e17bdSdan if( 0==sqlite3_stricmp(zArg, "1") ) return 1;
106*610e17bdSdan if( 0==sqlite3_stricmp(zArg, "0") ) return 0;
107*610e17bdSdan if( 0==sqlite3_stricmp(zArg, "false") ) return 0;
108*610e17bdSdan option_requires_argument_error(pOpt);
109*610e17bdSdan return 0;
110*610e17bdSdan }
111*610e17bdSdan
parse_command_line(int argc,char ** argv,int iStart,CmdLineOption * aOpt,void * pStruct,const char * zHelp)112*610e17bdSdan static void parse_command_line(
113*610e17bdSdan int argc,
114*610e17bdSdan char **argv,
115*610e17bdSdan int iStart,
116*610e17bdSdan CmdLineOption *aOpt,
117*610e17bdSdan void *pStruct,
118*610e17bdSdan const char *zHelp
119*610e17bdSdan ){
120*610e17bdSdan char *pOut = (char*)pStruct;
121*610e17bdSdan int bVerbose = 0;
122*610e17bdSdan int iArg;
123*610e17bdSdan
124*610e17bdSdan for(iArg=iStart; iArg<argc; iArg++){
125*610e17bdSdan const char *zArg = argv[iArg];
126*610e17bdSdan int nArg = strlen(zArg);
127*610e17bdSdan int nMatch = 0;
128*610e17bdSdan int iOpt;
129*610e17bdSdan
130*610e17bdSdan for(iOpt=0; aOpt[iOpt].zText; iOpt++){
131*610e17bdSdan CmdLineOption *pOpt = &aOpt[iOpt];
132*610e17bdSdan if( 0==sqlite3_strnicmp(pOpt->zText, zArg, nArg) ){
133*610e17bdSdan if( nMatch ){
134*610e17bdSdan ambiguous_option_error(zArg);
135*610e17bdSdan }
136*610e17bdSdan nMatch++;
137*610e17bdSdan if( pOpt->eType==CMDLINE_BARE ){
138*610e17bdSdan *(int*)(&pOut[pOpt->iOff]) = 1;
139*610e17bdSdan }else{
140*610e17bdSdan iArg++;
141*610e17bdSdan if( iArg==argc ){
142*610e17bdSdan option_requires_argument_error(pOpt);
143*610e17bdSdan }
144*610e17bdSdan switch( pOpt->eType ){
145*610e17bdSdan case CMDLINE_INTEGER:
146*610e17bdSdan *(int*)(&pOut[pOpt->iOff]) = get_integer_option(pOpt, argv[iArg]);
147*610e17bdSdan break;
148*610e17bdSdan case CMDLINE_STRING:
149*610e17bdSdan *(const char**)(&pOut[pOpt->iOff]) = argv[iArg];
150*610e17bdSdan break;
151*610e17bdSdan case CMDLINE_BOOLEAN:
152*610e17bdSdan *(int*)(&pOut[pOpt->iOff]) = get_boolean_option(pOpt, argv[iArg]);
153*610e17bdSdan break;
154*610e17bdSdan }
155*610e17bdSdan }
156*610e17bdSdan }
157*610e17bdSdan }
158*610e17bdSdan
159*610e17bdSdan if( nMatch==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg, nArg) ){
160*610e17bdSdan bVerbose = 1;
161*610e17bdSdan nMatch = 1;
162*610e17bdSdan }
163*610e17bdSdan
164*610e17bdSdan if( nMatch==0 ){
165*610e17bdSdan unknown_option_error(zArg, aOpt, zHelp);
166*610e17bdSdan }
167*610e17bdSdan }
168*610e17bdSdan
169*610e17bdSdan if( bVerbose ){
170*610e17bdSdan int iOpt;
171*610e17bdSdan fprintf(stdout, "Options are: ");
172*610e17bdSdan for(iOpt=0; aOpt[iOpt].zText; iOpt++){
173*610e17bdSdan CmdLineOption *pOpt = &aOpt[iOpt];
174*610e17bdSdan if( pOpt->eType!=CMDLINE_BARE || *(int*)(&pOut[pOpt->iOff]) ){
175*610e17bdSdan fprintf(stdout, "%s ", pOpt->zText);
176*610e17bdSdan }
177*610e17bdSdan switch( pOpt->eType ){
178*610e17bdSdan case CMDLINE_INTEGER:
179*610e17bdSdan fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
180*610e17bdSdan break;
181*610e17bdSdan case CMDLINE_BOOLEAN:
182*610e17bdSdan fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
183*610e17bdSdan break;
184*610e17bdSdan case CMDLINE_STRING:
185*610e17bdSdan fprintf(stdout, "%s ", *(const char**)(&pOut[pOpt->iOff]));
186*610e17bdSdan break;
187*610e17bdSdan }
188*610e17bdSdan }
189*610e17bdSdan fprintf(stdout, "\n");
190*610e17bdSdan }
191*610e17bdSdan }
192*610e17bdSdan /*
193*610e17bdSdan ** End of generic command line parser.
194*610e17bdSdan *************************************************************************/
195*610e17bdSdan
abort_due_to_error(int rc)196*610e17bdSdan static void abort_due_to_error(int rc){
197*610e17bdSdan fprintf(stderr, "Error: %d\n");
198*610e17bdSdan exit(-1);
199*610e17bdSdan }
200*610e17bdSdan
execsql(sqlite3 * db,const char * zSql)201*610e17bdSdan static void execsql(sqlite3 *db, const char *zSql){
202*610e17bdSdan int rc = sqlite3_exec(db, zSql, 0, 0, 0);
203*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
204*610e17bdSdan }
205*610e17bdSdan
xConflict(void * pCtx,int eConflict,sqlite3_changeset_iter * p)206*610e17bdSdan static int xConflict(void *pCtx, int eConflict, sqlite3_changeset_iter *p){
207*610e17bdSdan return SQLITE_CHANGESET_ABORT;
208*610e17bdSdan }
209*610e17bdSdan
run_test(sqlite3 * db,sqlite3 * db2,int nRow,const char * zSql)210*610e17bdSdan static void run_test(
211*610e17bdSdan sqlite3 *db,
212*610e17bdSdan sqlite3 *db2,
213*610e17bdSdan int nRow,
214*610e17bdSdan const char *zSql
215*610e17bdSdan ){
216*610e17bdSdan sqlite3_session *pSession = 0;
217*610e17bdSdan sqlite3_stmt *pStmt = 0;
218*610e17bdSdan int rc;
219*610e17bdSdan int i;
220*610e17bdSdan int nChangeset;
221*610e17bdSdan void *pChangeset;
222*610e17bdSdan
223*610e17bdSdan /* Attach a session object to database db */
224*610e17bdSdan rc = sqlite3session_create(db, "main", &pSession);
225*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
226*610e17bdSdan
227*610e17bdSdan /* Configure the session to capture changes on all tables */
228*610e17bdSdan rc = sqlite3session_attach(pSession, 0);
229*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
230*610e17bdSdan
231*610e17bdSdan /* Prepare the SQL statement */
232*610e17bdSdan rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
233*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
234*610e17bdSdan
235*610e17bdSdan /* Open a transaction */
236*610e17bdSdan execsql(db, "BEGIN");
237*610e17bdSdan
238*610e17bdSdan /* Execute the SQL statement nRow times */
239*610e17bdSdan for(i=0; i<nRow; i++){
240*610e17bdSdan sqlite3_bind_int(pStmt, 1, i);
241*610e17bdSdan sqlite3_step(pStmt);
242*610e17bdSdan rc = sqlite3_reset(pStmt);
243*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
244*610e17bdSdan }
245*610e17bdSdan sqlite3_finalize(pStmt);
246*610e17bdSdan
247*610e17bdSdan /* Extract a changeset from the sessions object */
248*610e17bdSdan rc = sqlite3session_changeset(pSession, &nChangeset, &pChangeset);
249*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
250*610e17bdSdan execsql(db, "COMMIT");
251*610e17bdSdan
252*610e17bdSdan /* Apply the changeset to the second db */
253*610e17bdSdan rc = sqlite3changeset_apply(db2, nChangeset, pChangeset, 0, xConflict, 0);
254*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
255*610e17bdSdan
256*610e17bdSdan /* Cleanup */
257*610e17bdSdan sqlite3_free(pChangeset);
258*610e17bdSdan sqlite3session_delete(pSession);
259*610e17bdSdan }
260*610e17bdSdan
main(int argc,char ** argv)261*610e17bdSdan int main(int argc, char **argv){
262*610e17bdSdan struct Options {
263*610e17bdSdan int nRow;
264*610e17bdSdan int bWithoutRowid;
265*610e17bdSdan int bInteger;
266*610e17bdSdan int bAll;
267*610e17bdSdan const char *zDb;
268*610e17bdSdan };
269*610e17bdSdan struct Options o = { 2500, 0, 0, 0, "session_speed_test.db" };
270*610e17bdSdan
271*610e17bdSdan CmdLineOption aOpt[] = {
272*610e17bdSdan CMDLINE_INT32( "-rows", "number of rows in test",
273*610e17bdSdan offsetof(struct Options, nRow) ),
274*610e17bdSdan CMDLINE_BOOL("-without-rowid", "use WITHOUT ROWID tables",
275*610e17bdSdan offsetof(struct Options, bWithoutRowid) ),
276*610e17bdSdan CMDLINE_BOOL("-integer", "use integer data (instead of text/blobs)",
277*610e17bdSdan offsetof(struct Options, bInteger) ),
278*610e17bdSdan CMDLINE_NONE("-all", "Run all 4 combos of -without-rowid and -integer",
279*610e17bdSdan offsetof(struct Options, bAll) ),
280*610e17bdSdan CMDLINE_TEXT("-database", "prefix for database files to use",
281*610e17bdSdan offsetof(struct Options, zDb) ),
282*610e17bdSdan {0, 0, 0, 0}
283*610e17bdSdan };
284*610e17bdSdan
285*610e17bdSdan const char *azCreate[] = {
286*610e17bdSdan "CREATE TABLE t1(a PRIMARY KEY, b, c, d)",
287*610e17bdSdan "CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID",
288*610e17bdSdan };
289*610e17bdSdan
290*610e17bdSdan const char *azInsert[] = {
291*610e17bdSdan "INSERT INTO t1 VALUES("
292*610e17bdSdan "printf('%.8d',?), randomblob(50), randomblob(50), randomblob(50))",
293*610e17bdSdan "INSERT INTO t1 VALUES(?, random(), random(), random())"
294*610e17bdSdan };
295*610e17bdSdan
296*610e17bdSdan const char *azUpdate[] = {
297*610e17bdSdan "UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)",
298*610e17bdSdan "UPDATE t1 SET d = random() WHERE a = ?"
299*610e17bdSdan };
300*610e17bdSdan
301*610e17bdSdan const char *azDelete[] = {
302*610e17bdSdan "DELETE FROM t1 WHERE a = printf('%.8d',?)",
303*610e17bdSdan "DELETE FROM t1 WHERE a = ?"
304*610e17bdSdan };
305*610e17bdSdan
306*610e17bdSdan int rc;
307*610e17bdSdan sqlite3 *db;
308*610e17bdSdan sqlite3 *db2;
309*610e17bdSdan char *zDb2;
310*610e17bdSdan int bWithoutRowid;
311*610e17bdSdan int bInteger;
312*610e17bdSdan
313*610e17bdSdan parse_command_line(argc, argv, 1, aOpt, (void*)&o,
314*610e17bdSdan "This program creates two new, empty, databases each containing a single\n"
315*610e17bdSdan "table. It then does the following:\n\n"
316*610e17bdSdan " 1. Inserts -rows rows into the first database\n"
317*610e17bdSdan " 2. Updates each row in the first db\n"
318*610e17bdSdan " 3. Delete each row from the first db\n\n"
319*610e17bdSdan "The modifications made by each step are captured in a changeset and\n"
320*610e17bdSdan "applied to the second database.\n"
321*610e17bdSdan );
322*610e17bdSdan zDb2 = sqlite3_mprintf("%s2", o.zDb);
323*610e17bdSdan
324*610e17bdSdan for(bWithoutRowid=0; bWithoutRowid<2; bWithoutRowid++){
325*610e17bdSdan for(bInteger=0; bInteger<2; bInteger++){
326*610e17bdSdan if( o.bAll || (o.bWithoutRowid==bWithoutRowid && o.bInteger==bInteger) ){
327*610e17bdSdan fprintf(stdout, "Testing %s data with %s table\n",
328*610e17bdSdan bInteger ? "integer" : "blob/text",
329*610e17bdSdan bWithoutRowid ? "WITHOUT ROWID" : "rowid"
330*610e17bdSdan );
331*610e17bdSdan
332*610e17bdSdan /* Open new database handles on two empty databases */
333*610e17bdSdan unlink(o.zDb);
334*610e17bdSdan rc = sqlite3_open(o.zDb, &db);
335*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
336*610e17bdSdan unlink(zDb2);
337*610e17bdSdan rc = sqlite3_open(zDb2, &db2);
338*610e17bdSdan if( rc!=SQLITE_OK ) abort_due_to_error(rc);
339*610e17bdSdan
340*610e17bdSdan /* Create the schema in both databases. */
341*610e17bdSdan execsql(db, azCreate[o.bWithoutRowid]);
342*610e17bdSdan execsql(db2, azCreate[o.bWithoutRowid]);
343*610e17bdSdan
344*610e17bdSdan /* Run the three tests */
345*610e17bdSdan run_test(db, db2, o.nRow, azInsert[o.bInteger]);
346*610e17bdSdan run_test(db, db2, o.nRow, azUpdate[o.bInteger]);
347*610e17bdSdan run_test(db, db2, o.nRow, azDelete[o.bInteger]);
348*610e17bdSdan
349*610e17bdSdan /* Close the db handles */
350*610e17bdSdan sqlite3_close(db);
351*610e17bdSdan sqlite3_close(db2);
352*610e17bdSdan }
353*610e17bdSdan }
354*610e17bdSdan }
355*610e17bdSdan
356*610e17bdSdan
357*610e17bdSdan return 0;
358*610e17bdSdan }
359