/ Check-in [25f1275f]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add a speed-test program for the sessions module.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | preupdate-without-rowid
Files: files | file ages | folders
SHA1: 25f1275fe3e940c1d9a7b013cb3744304b2eda1e
User & Date: dan 2017-01-31 17:31:30
Context
2017-02-01
14:10
Changes so that the pre-update hook and the sessions module work with WITHOUT ROWID tables. check-in: 964bdc27 user: dan tags: trunk
2017-01-31
17:31
Add a speed-test program for the sessions module. Closed-Leaf check-in: 25f1275f user: dan tags: preupdate-without-rowid
14:08
Merge latest trunk with this branch. check-in: 4a592abb user: dan tags: preupdate-without-rowid
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added ext/session/session_speed_test.c.

















































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/*
** 2017 January 31
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains the source code for a standalone program used to
** test the performance of the sessions module. Compile and run:
**
**   ./session_speed_test -help
**
** for details.
*/

#include "sqlite3.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>

/*************************************************************************
** Start of generic command line parser.
*/
#define CMDLINE_BARE       0
#define CMDLINE_INTEGER    1
#define CMDLINE_STRING     2
#define CMDLINE_BOOLEAN    3

typedef struct CmdLineOption CmdLineOption;
struct CmdLineOption {
  const char *zText;              /* Name of command line option */
  const char *zHelp;              /* Help text for option */
  int eType;                      /* One of the CMDLINE_* values */
  int iOff;                       /* Offset of output variable */
};

#define CMDLINE_INT32(x,y,z) {x, y, CMDLINE_INTEGER, z}
#define CMDLINE_BOOL(x,y,z)  {x, y, CMDLINE_BOOLEAN, z}
#define CMDLINE_TEXT(x,y,z)  {x, y, CMDLINE_STRING, z}
#define CMDLINE_NONE(x,y,z)  {x, y, CMDLINE_BARE, z}

static void option_requires_argument_error(CmdLineOption *pOpt){
  fprintf(stderr, "Option requires a%s argument: %s\n", 
      pOpt->eType==CMDLINE_INTEGER ? "n integer" :
      pOpt->eType==CMDLINE_STRING ? " string" : " boolean",
      pOpt->zText
  );
  exit(1);
}

static void ambiguous_option_error(const char *zArg){
  fprintf(stderr, "Option is ambiguous: %s\n", zArg);
  exit(1);
}

static void unknown_option_error(
  const char *zArg, 
  CmdLineOption *aOpt,
  const char *zHelp
){
  int i;
  fprintf(stderr, "Unknown option: %s\n", zArg);
  fprintf(stderr, "\nOptions are:\n");
  fprintf(stderr, "  % -30sEcho command line options\n", "-cmdline:verbose");
  for(i=0; aOpt[i].zText; i++){
    int eType = aOpt[i].eType;
    char *zOpt = sqlite3_mprintf("%s %s", aOpt[i].zText,
        eType==CMDLINE_BARE ? "" :
        eType==CMDLINE_INTEGER ? "N" :
        eType==CMDLINE_BOOLEAN ? "BOOLEAN" : "TEXT"
    );
    fprintf(stderr, "  % -30s%s\n", zOpt, aOpt[i].zHelp);
    sqlite3_free(zOpt);
  }
  if( zHelp ){
    fprintf(stderr, "\n%s\n", zHelp);
  }
  exit(1);
}

static int get_integer_option(CmdLineOption *pOpt, const char *zArg){
  int i = 0;
  int iRet = 0;
  int bSign = 1;
  if( zArg[0]=='-' ){
    bSign = -1;
    i = 1;
  }
  while( zArg[i] ){
    if( zArg[i]<'0' || zArg[i]>'9' ) option_requires_argument_error(pOpt);
    iRet = iRet*10 + (zArg[i] - '0');
    i++;
  }
  return (iRet*bSign);
}

static int get_boolean_option(CmdLineOption *pOpt, const char *zArg){
  if( 0==sqlite3_stricmp(zArg, "true") ) return 1;
  if( 0==sqlite3_stricmp(zArg, "1") ) return 1;
  if( 0==sqlite3_stricmp(zArg, "0") ) return 0;
  if( 0==sqlite3_stricmp(zArg, "false") ) return 0;
  option_requires_argument_error(pOpt);
  return 0;
}

static void parse_command_line(
  int argc, 
  char **argv, 
  int iStart,
  CmdLineOption *aOpt,
  void *pStruct,
  const char *zHelp
){
  char *pOut = (char*)pStruct;
  int bVerbose = 0;
  int iArg;

  for(iArg=iStart; iArg<argc; iArg++){
    const char *zArg = argv[iArg];
    int nArg = strlen(zArg);
    int nMatch = 0;
    int iOpt;

    for(iOpt=0; aOpt[iOpt].zText; iOpt++){
      CmdLineOption *pOpt = &aOpt[iOpt];
      if( 0==sqlite3_strnicmp(pOpt->zText, zArg, nArg) ){
        if( nMatch ){
          ambiguous_option_error(zArg);
        }
        nMatch++;
        if( pOpt->eType==CMDLINE_BARE ){
          *(int*)(&pOut[pOpt->iOff]) = 1;
        }else{
          iArg++;
          if( iArg==argc ){
            option_requires_argument_error(pOpt);
          }
          switch( pOpt->eType ){
            case CMDLINE_INTEGER:
              *(int*)(&pOut[pOpt->iOff]) = get_integer_option(pOpt, argv[iArg]);
              break;
            case CMDLINE_STRING:
              *(const char**)(&pOut[pOpt->iOff]) = argv[iArg];
              break;
            case CMDLINE_BOOLEAN:
              *(int*)(&pOut[pOpt->iOff]) = get_boolean_option(pOpt, argv[iArg]);
              break;
          }
        }
      }
    }

    if( nMatch==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg, nArg) ){
      bVerbose = 1;
      nMatch = 1;
    }

    if( nMatch==0 ){
      unknown_option_error(zArg, aOpt, zHelp);
    }
  }

  if( bVerbose ){
    int iOpt;
    fprintf(stdout, "Options are: ");
    for(iOpt=0; aOpt[iOpt].zText; iOpt++){
      CmdLineOption *pOpt = &aOpt[iOpt];
      if( pOpt->eType!=CMDLINE_BARE || *(int*)(&pOut[pOpt->iOff]) ){
        fprintf(stdout, "%s ", pOpt->zText);
      }
      switch( pOpt->eType ){
        case CMDLINE_INTEGER:
          fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
          break;
        case CMDLINE_BOOLEAN:
          fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
          break;
        case CMDLINE_STRING:
          fprintf(stdout, "%s ", *(const char**)(&pOut[pOpt->iOff]));
          break;
      }
    }
    fprintf(stdout, "\n");
  }
}
/* 
** End of generic command line parser.
*************************************************************************/

static void abort_due_to_error(int rc){
  fprintf(stderr, "Error: %d\n");
  exit(-1);
}

static void execsql(sqlite3 *db, const char *zSql){
  int rc = sqlite3_exec(db, zSql, 0, 0, 0);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);
}

static int xConflict(void *pCtx, int eConflict, sqlite3_changeset_iter *p){
  return SQLITE_CHANGESET_ABORT;
}

static void run_test(
  sqlite3 *db, 
  sqlite3 *db2, 
  int nRow, 
  const char *zSql
){
  sqlite3_session *pSession = 0;
  sqlite3_stmt *pStmt = 0;
  int rc;
  int i;
  int nChangeset;
  void *pChangeset;

  /* Attach a session object to database db */
  rc = sqlite3session_create(db, "main", &pSession);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);

  /* Configure the session to capture changes on all tables */
  rc = sqlite3session_attach(pSession, 0);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);

  /* Prepare the SQL statement */
  rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);

  /* Open a transaction */
  execsql(db, "BEGIN");

  /* Execute the SQL statement nRow times */
  for(i=0; i<nRow; i++){
    sqlite3_bind_int(pStmt, 1, i);
    sqlite3_step(pStmt);
    rc = sqlite3_reset(pStmt);
    if( rc!=SQLITE_OK ) abort_due_to_error(rc);
  }
  sqlite3_finalize(pStmt);

  /* Extract a changeset from the sessions object */
  rc = sqlite3session_changeset(pSession, &nChangeset, &pChangeset);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);
  execsql(db, "COMMIT");

  /* Apply the changeset to the second db */
  rc = sqlite3changeset_apply(db2, nChangeset, pChangeset, 0, xConflict, 0);
  if( rc!=SQLITE_OK ) abort_due_to_error(rc);

  /* Cleanup */
  sqlite3_free(pChangeset);
  sqlite3session_delete(pSession);
}

int main(int argc, char **argv){
  struct Options {
    int nRow;
    int bWithoutRowid;
    int bInteger;
    int bAll;
    const char *zDb;
  };
  struct Options o = { 2500, 0, 0, 0, "session_speed_test.db" };

  CmdLineOption aOpt[] = {
    CMDLINE_INT32( "-rows", "number of rows in test",
      offsetof(struct Options, nRow) ),
    CMDLINE_BOOL("-without-rowid", "use WITHOUT ROWID tables", 
      offsetof(struct Options, bWithoutRowid) ),
    CMDLINE_BOOL("-integer", "use integer data (instead of text/blobs)",
      offsetof(struct Options, bInteger) ),
    CMDLINE_NONE("-all", "Run all 4 combos of -without-rowid and -integer",
      offsetof(struct Options, bAll) ),
    CMDLINE_TEXT("-database", "prefix for database files to use",
      offsetof(struct Options, zDb) ),
    {0, 0, 0, 0}
  };

  const char *azCreate[] = {
    "CREATE TABLE t1(a PRIMARY KEY, b, c, d)",
    "CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID",
  };

  const char *azInsert[] = {
    "INSERT INTO t1 VALUES("
    "printf('%.8d',?), randomblob(50), randomblob(50), randomblob(50))",
    "INSERT INTO t1 VALUES(?, random(), random(), random())"
  };

  const char *azUpdate[] = {
    "UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)",
    "UPDATE t1 SET d = random() WHERE a = ?"
  };

  const char *azDelete[] = {
    "DELETE FROM t1 WHERE a = printf('%.8d',?)",
    "DELETE FROM t1 WHERE a = ?"
  };

  int rc;
  sqlite3 *db;
  sqlite3 *db2;
  char *zDb2;
  int bWithoutRowid;
  int bInteger;

  parse_command_line(argc, argv, 1, aOpt, (void*)&o,
    "This program creates two new, empty, databases each containing a single\n"
    "table. It then does the following:\n\n"
    "  1. Inserts -rows rows into the first database\n"
    "  2. Updates each row in the first db\n"
    "  3. Delete each row from the first db\n\n"
    "The modifications made by each step are captured in a changeset and\n"
    "applied to the second database.\n"
  );
  zDb2 = sqlite3_mprintf("%s2", o.zDb);

  for(bWithoutRowid=0; bWithoutRowid<2; bWithoutRowid++){
    for(bInteger=0; bInteger<2; bInteger++){
      if( o.bAll || (o.bWithoutRowid==bWithoutRowid && o.bInteger==bInteger) ){
        fprintf(stdout, "Testing %s data with %s table\n",
            bInteger ? "integer" : "blob/text",
            bWithoutRowid ? "WITHOUT ROWID" : "rowid"
        );

        /* Open new database handles on two empty databases */
        unlink(o.zDb);
        rc = sqlite3_open(o.zDb, &db);
        if( rc!=SQLITE_OK ) abort_due_to_error(rc);
        unlink(zDb2);
        rc = sqlite3_open(zDb2, &db2);
        if( rc!=SQLITE_OK ) abort_due_to_error(rc);

        /* Create the schema in both databases. */
        execsql(db, azCreate[o.bWithoutRowid]);
        execsql(db2, azCreate[o.bWithoutRowid]);

        /* Run the three tests */
        run_test(db, db2, o.nRow, azInsert[o.bInteger]);
        run_test(db, db2, o.nRow, azUpdate[o.bInteger]);
        run_test(db, db2, o.nRow, azDelete[o.bInteger]);

        /* Close the db handles */
        sqlite3_close(db);
        sqlite3_close(db2);
      }
    }
  }


  return 0;
}