| Index: third_party/sqlite/src/ext/session/session_speed_test.c
 | 
| diff --git a/third_party/sqlite/src/ext/session/session_speed_test.c b/third_party/sqlite/src/ext/session/session_speed_test.c
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..14b97ea07b6873b943bd75a0f61f22be27aa2a5d
 | 
| --- /dev/null
 | 
| +++ b/third_party/sqlite/src/ext/session/session_speed_test.c
 | 
| @@ -0,0 +1,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;
 | 
| +}
 | 
| +
 | 
| +
 | 
| 
 |