modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/session/session_speed_test.c (about)

     1  /*
     2  ** 2017 January 31
     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  ** This file contains the source code for a standalone program used to
    13  ** test the performance of the sessions module. Compile and run:
    14  **
    15  **   ./session_speed_test -help
    16  **
    17  ** for details.
    18  */
    19  
    20  #include "sqlite3.h"
    21  #include <stdio.h>
    22  #include <stdlib.h>
    23  #include <string.h>
    24  #include <stddef.h>
    25  #include <unistd.h>
    26  
    27  /*************************************************************************
    28  ** Start of generic command line parser.
    29  */
    30  #define CMDLINE_BARE       0
    31  #define CMDLINE_INTEGER    1
    32  #define CMDLINE_STRING     2
    33  #define CMDLINE_BOOLEAN    3
    34  
    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 */
    41  };
    42  
    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}
    47  
    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",
    52        pOpt->zText
    53    );
    54    exit(1);
    55  }
    56  
    57  static void ambiguous_option_error(const char *zArg){
    58    fprintf(stderr, "Option is ambiguous: %s\n", zArg);
    59    exit(1);
    60  }
    61  
    62  static void unknown_option_error(
    63    const char *zArg, 
    64    CmdLineOption *aOpt,
    65    const char *zHelp
    66  ){
    67    int i;
    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"
    77      );
    78      fprintf(stderr, "  % -30s%s\n", zOpt, aOpt[i].zHelp);
    79      sqlite3_free(zOpt);
    80    }
    81    if( zHelp ){
    82      fprintf(stderr, "\n%s\n", zHelp);
    83    }
    84    exit(1);
    85  }
    86  
    87  static int get_integer_option(CmdLineOption *pOpt, const char *zArg){
    88    int i = 0;
    89    int iRet = 0;
    90    int bSign = 1;
    91    if( zArg[0]=='-' ){
    92      bSign = -1;
    93      i = 1;
    94    }
    95    while( zArg[i] ){
    96      if( zArg[i]<'0' || zArg[i]>'9' ) option_requires_argument_error(pOpt);
    97      iRet = iRet*10 + (zArg[i] - '0');
    98      i++;
    99    }
   100    return (iRet*bSign);
   101  }
   102  
   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);
   109    return 0;
   110  }
   111  
   112  static void parse_command_line(
   113    int argc, 
   114    char **argv, 
   115    int iStart,
   116    CmdLineOption *aOpt,
   117    void *pStruct,
   118    const char *zHelp
   119  ){
   120    char *pOut = (char*)pStruct;
   121    int bVerbose = 0;
   122    int iArg;
   123  
   124    for(iArg=iStart; iArg<argc; iArg++){
   125      const char *zArg = argv[iArg];
   126      int nArg = strlen(zArg);
   127      int nMatch = 0;
   128      int iOpt;
   129  
   130      for(iOpt=0; aOpt[iOpt].zText; iOpt++){
   131        CmdLineOption *pOpt = &aOpt[iOpt];
   132        if( 0==sqlite3_strnicmp(pOpt->zText, zArg, nArg) ){
   133          if( nMatch ){
   134            ambiguous_option_error(zArg);
   135          }
   136          nMatch++;
   137          if( pOpt->eType==CMDLINE_BARE ){
   138            *(int*)(&pOut[pOpt->iOff]) = 1;
   139          }else{
   140            iArg++;
   141            if( iArg==argc ){
   142              option_requires_argument_error(pOpt);
   143            }
   144            switch( pOpt->eType ){
   145              case CMDLINE_INTEGER:
   146                *(int*)(&pOut[pOpt->iOff]) = get_integer_option(pOpt, argv[iArg]);
   147                break;
   148              case CMDLINE_STRING:
   149                *(const char**)(&pOut[pOpt->iOff]) = argv[iArg];
   150                break;
   151              case CMDLINE_BOOLEAN:
   152                *(int*)(&pOut[pOpt->iOff]) = get_boolean_option(pOpt, argv[iArg]);
   153                break;
   154            }
   155          }
   156        }
   157      }
   158  
   159      if( nMatch==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg, nArg) ){
   160        bVerbose = 1;
   161        nMatch = 1;
   162      }
   163  
   164      if( nMatch==0 ){
   165        unknown_option_error(zArg, aOpt, zHelp);
   166      }
   167    }
   168  
   169    if( bVerbose ){
   170      int iOpt;
   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);
   176        }
   177        switch( pOpt->eType ){
   178          case CMDLINE_INTEGER:
   179            fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
   180            break;
   181          case CMDLINE_BOOLEAN:
   182            fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
   183            break;
   184          case CMDLINE_STRING:
   185            fprintf(stdout, "%s ", *(const char**)(&pOut[pOpt->iOff]));
   186            break;
   187        }
   188      }
   189      fprintf(stdout, "\n");
   190    }
   191  }
   192  /* 
   193  ** End of generic command line parser.
   194  *************************************************************************/
   195  
   196  static void abort_due_to_error(int rc){
   197    fprintf(stderr, "Error: %d\n");
   198    exit(-1);
   199  }
   200  
   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);
   204  }
   205  
   206  static int xConflict(void *pCtx, int eConflict, sqlite3_changeset_iter *p){
   207    return SQLITE_CHANGESET_ABORT;
   208  }
   209  
   210  static void run_test(
   211    sqlite3 *db, 
   212    sqlite3 *db2, 
   213    int nRow, 
   214    const char *zSql
   215  ){
   216    sqlite3_session *pSession = 0;
   217    sqlite3_stmt *pStmt = 0;
   218    int rc;
   219    int i;
   220    int nChangeset;
   221    void *pChangeset;
   222  
   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);
   226  
   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);
   230  
   231    /* Prepare the SQL statement */
   232    rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
   233    if( rc!=SQLITE_OK ) abort_due_to_error(rc);
   234  
   235    /* Open a transaction */
   236    execsql(db, "BEGIN");
   237  
   238    /* Execute the SQL statement nRow times */
   239    for(i=0; i<nRow; i++){
   240      sqlite3_bind_int(pStmt, 1, i);
   241      sqlite3_step(pStmt);
   242      rc = sqlite3_reset(pStmt);
   243      if( rc!=SQLITE_OK ) abort_due_to_error(rc);
   244    }
   245    sqlite3_finalize(pStmt);
   246  
   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");
   251  
   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);
   255  
   256    /* Cleanup */
   257    sqlite3_free(pChangeset);
   258    sqlite3session_delete(pSession);
   259  }
   260  
   261  int main(int argc, char **argv){
   262    struct Options {
   263      int nRow;
   264      int bWithoutRowid;
   265      int bInteger;
   266      int bAll;
   267      const char *zDb;
   268    };
   269    struct Options o = { 2500, 0, 0, 0, "session_speed_test.db" };
   270  
   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) ),
   282      {0, 0, 0, 0}
   283    };
   284  
   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",
   288    };
   289  
   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())"
   294    };
   295  
   296    const char *azUpdate[] = {
   297      "UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)",
   298      "UPDATE t1 SET d = random() WHERE a = ?"
   299    };
   300  
   301    const char *azDelete[] = {
   302      "DELETE FROM t1 WHERE a = printf('%.8d',?)",
   303      "DELETE FROM t1 WHERE a = ?"
   304    };
   305  
   306    int rc;
   307    sqlite3 *db;
   308    sqlite3 *db2;
   309    char *zDb2;
   310    int bWithoutRowid;
   311    int bInteger;
   312  
   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"
   321    );
   322    zDb2 = sqlite3_mprintf("%s2", o.zDb);
   323  
   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"
   330          );
   331  
   332          /* Open new database handles on two empty databases */
   333          unlink(o.zDb);
   334          rc = sqlite3_open(o.zDb, &db);
   335          if( rc!=SQLITE_OK ) abort_due_to_error(rc);
   336          unlink(zDb2);
   337          rc = sqlite3_open(zDb2, &db2);
   338          if( rc!=SQLITE_OK ) abort_due_to_error(rc);
   339  
   340          /* Create the schema in both databases. */
   341          execsql(db, azCreate[o.bWithoutRowid]);
   342          execsql(db2, azCreate[o.bWithoutRowid]);
   343  
   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]);
   348  
   349          /* Close the db handles */
   350          sqlite3_close(db);
   351          sqlite3_close(db2);
   352        }
   353      }
   354    }
   355  
   356  
   357    return 0;
   358  }
   359  
   360