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