Annotation of embedaddon/sqlite3/test/threadtest3.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: ** The code in this file runs a few multi-threaded test cases using the
! 4: ** SQLite library. It can be compiled to an executable on unix using the
! 5: ** following command:
! 6: **
! 7: ** gcc -O2 threadtest3.c sqlite3.c -ldl -lpthread -lm
! 8: **
! 9: ** Then run the compiled program. The exit status is non-zero if any tests
! 10: ** failed (hopefully there is also some output to stdout to clarify what went
! 11: ** wrong).
! 12: **
! 13: ** There are three parts to the code in this file, in the following order:
! 14: **
! 15: ** 1. Code for the SQL aggregate function md5sum() copied from
! 16: ** tclsqlite.c in the SQLite distribution. The names of all the
! 17: ** types and functions in this section begin with "MD5" or "md5".
! 18: **
! 19: ** 2. A set of utility functions that may be used to implement
! 20: ** multi-threaded test cases. These are all called by test code
! 21: ** via macros that help with error reporting. The macros are defined
! 22: ** immediately below this comment.
! 23: **
! 24: ** 3. The test code itself. And a main() routine to drive the test
! 25: ** code.
! 26: */
! 27:
! 28: /*************************************************************************
! 29: ** Start of test code/infrastructure interface macros.
! 30: **
! 31: ** The following macros constitute the interface between the test
! 32: ** programs and the test infrastructure. Test infrastructure code
! 33: ** does not itself use any of these macros. Test code should not
! 34: ** call any of the macroname_x() functions directly.
! 35: **
! 36: ** See the header comments above the corresponding macroname_x()
! 37: ** function for a description of each interface.
! 38: */
! 39:
! 40: /* Database functions */
! 41: #define opendb(w,x,y,z) (SEL(w), opendb_x(w,x,y,z))
! 42: #define closedb(y,z) (SEL(y), closedb_x(y,z))
! 43:
! 44: /* Functions to execute SQL */
! 45: #define sql_script(x,y,z) (SEL(x), sql_script_x(x,y,z))
! 46: #define integrity_check(x,y) (SEL(x), integrity_check_x(x,y))
! 47: #define execsql_i64(x,y,...) (SEL(x), execsql_i64_x(x,y,__VA_ARGS__))
! 48: #define execsql_text(x,y,z,...) (SEL(x), execsql_text_x(x,y,z,__VA_ARGS__))
! 49: #define execsql(x,y,...) (SEL(x), (void)execsql_i64_x(x,y,__VA_ARGS__))
! 50:
! 51: /* Thread functions */
! 52: #define launch_thread(w,x,y,z) (SEL(w), launch_thread_x(w,x,y,z))
! 53: #define join_all_threads(y,z) (SEL(y), join_all_threads_x(y,z))
! 54:
! 55: /* Timer functions */
! 56: #define setstoptime(y,z) (SEL(y), setstoptime_x(y,z))
! 57: #define timetostop(z) (SEL(z), timetostop_x(z))
! 58:
! 59: /* Report/clear errors. */
! 60: #define test_error(z, ...) test_error_x(z, sqlite3_mprintf(__VA_ARGS__))
! 61: #define clear_error(y,z) clear_error_x(y, z)
! 62:
! 63: /* File-system operations */
! 64: #define filesize(y,z) (SEL(y), filesize_x(y,z))
! 65: #define filecopy(x,y,z) (SEL(x), filecopy_x(x,y,z))
! 66:
! 67: /*
! 68: ** End of test code/infrastructure interface macros.
! 69: *************************************************************************/
! 70:
! 71:
! 72:
! 73:
! 74: #include <sqlite3.h>
! 75: #include <unistd.h>
! 76: #include <stdio.h>
! 77: #include <pthread.h>
! 78: #include <assert.h>
! 79: #include <sys/types.h>
! 80: #include <sys/stat.h>
! 81: #include <string.h>
! 82: #include <fcntl.h>
! 83: #include <errno.h>
! 84:
! 85: /*
! 86: * This code implements the MD5 message-digest algorithm.
! 87: * The algorithm is due to Ron Rivest. This code was
! 88: * written by Colin Plumb in 1993, no copyright is claimed.
! 89: * This code is in the public domain; do with it what you wish.
! 90: *
! 91: * Equivalent code is available from RSA Data Security, Inc.
! 92: * This code has been tested against that, and is equivalent,
! 93: * except that you don't need to include two pages of legalese
! 94: * with every copy.
! 95: *
! 96: * To compute the message digest of a chunk of bytes, declare an
! 97: * MD5Context structure, pass it to MD5Init, call MD5Update as
! 98: * needed on buffers full of bytes, and then call MD5Final, which
! 99: * will fill a supplied 16-byte array with the digest.
! 100: */
! 101:
! 102: /*
! 103: * If compiled on a machine that doesn't have a 32-bit integer,
! 104: * you just set "uint32" to the appropriate datatype for an
! 105: * unsigned 32-bit integer. For example:
! 106: *
! 107: * cc -Duint32='unsigned long' md5.c
! 108: *
! 109: */
! 110: #ifndef uint32
! 111: # define uint32 unsigned int
! 112: #endif
! 113:
! 114: struct MD5Context {
! 115: int isInit;
! 116: uint32 buf[4];
! 117: uint32 bits[2];
! 118: unsigned char in[64];
! 119: };
! 120: typedef struct MD5Context MD5Context;
! 121:
! 122: /*
! 123: * Note: this code is harmless on little-endian machines.
! 124: */
! 125: static void byteReverse (unsigned char *buf, unsigned longs){
! 126: uint32 t;
! 127: do {
! 128: t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
! 129: ((unsigned)buf[1]<<8 | buf[0]);
! 130: *(uint32 *)buf = t;
! 131: buf += 4;
! 132: } while (--longs);
! 133: }
! 134: /* The four core functions - F1 is optimized somewhat */
! 135:
! 136: /* #define F1(x, y, z) (x & y | ~x & z) */
! 137: #define F1(x, y, z) (z ^ (x & (y ^ z)))
! 138: #define F2(x, y, z) F1(z, x, y)
! 139: #define F3(x, y, z) (x ^ y ^ z)
! 140: #define F4(x, y, z) (y ^ (x | ~z))
! 141:
! 142: /* This is the central step in the MD5 algorithm. */
! 143: #define MD5STEP(f, w, x, y, z, data, s) \
! 144: ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
! 145:
! 146: /*
! 147: * The core of the MD5 algorithm, this alters an existing MD5 hash to
! 148: * reflect the addition of 16 longwords of new data. MD5Update blocks
! 149: * the data and converts bytes into longwords for this routine.
! 150: */
! 151: static void MD5Transform(uint32 buf[4], const uint32 in[16]){
! 152: register uint32 a, b, c, d;
! 153:
! 154: a = buf[0];
! 155: b = buf[1];
! 156: c = buf[2];
! 157: d = buf[3];
! 158:
! 159: MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
! 160: MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
! 161: MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
! 162: MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
! 163: MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
! 164: MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
! 165: MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
! 166: MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
! 167: MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
! 168: MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
! 169: MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
! 170: MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
! 171: MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
! 172: MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
! 173: MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
! 174: MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
! 175:
! 176: MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
! 177: MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
! 178: MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
! 179: MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
! 180: MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
! 181: MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
! 182: MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
! 183: MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
! 184: MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
! 185: MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
! 186: MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
! 187: MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
! 188: MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
! 189: MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
! 190: MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
! 191: MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
! 192:
! 193: MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
! 194: MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
! 195: MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
! 196: MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
! 197: MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
! 198: MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
! 199: MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
! 200: MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
! 201: MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
! 202: MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
! 203: MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
! 204: MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
! 205: MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
! 206: MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
! 207: MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
! 208: MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
! 209:
! 210: MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
! 211: MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
! 212: MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
! 213: MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
! 214: MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
! 215: MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
! 216: MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
! 217: MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
! 218: MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
! 219: MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
! 220: MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
! 221: MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
! 222: MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
! 223: MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
! 224: MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
! 225: MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
! 226:
! 227: buf[0] += a;
! 228: buf[1] += b;
! 229: buf[2] += c;
! 230: buf[3] += d;
! 231: }
! 232:
! 233: /*
! 234: * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
! 235: * initialization constants.
! 236: */
! 237: static void MD5Init(MD5Context *ctx){
! 238: ctx->isInit = 1;
! 239: ctx->buf[0] = 0x67452301;
! 240: ctx->buf[1] = 0xefcdab89;
! 241: ctx->buf[2] = 0x98badcfe;
! 242: ctx->buf[3] = 0x10325476;
! 243: ctx->bits[0] = 0;
! 244: ctx->bits[1] = 0;
! 245: }
! 246:
! 247: /*
! 248: * Update context to reflect the concatenation of another buffer full
! 249: * of bytes.
! 250: */
! 251: static
! 252: void MD5Update(MD5Context *ctx, const unsigned char *buf, unsigned int len){
! 253: uint32 t;
! 254:
! 255: /* Update bitcount */
! 256:
! 257: t = ctx->bits[0];
! 258: if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
! 259: ctx->bits[1]++; /* Carry from low to high */
! 260: ctx->bits[1] += len >> 29;
! 261:
! 262: t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
! 263:
! 264: /* Handle any leading odd-sized chunks */
! 265:
! 266: if ( t ) {
! 267: unsigned char *p = (unsigned char *)ctx->in + t;
! 268:
! 269: t = 64-t;
! 270: if (len < t) {
! 271: memcpy(p, buf, len);
! 272: return;
! 273: }
! 274: memcpy(p, buf, t);
! 275: byteReverse(ctx->in, 16);
! 276: MD5Transform(ctx->buf, (uint32 *)ctx->in);
! 277: buf += t;
! 278: len -= t;
! 279: }
! 280:
! 281: /* Process data in 64-byte chunks */
! 282:
! 283: while (len >= 64) {
! 284: memcpy(ctx->in, buf, 64);
! 285: byteReverse(ctx->in, 16);
! 286: MD5Transform(ctx->buf, (uint32 *)ctx->in);
! 287: buf += 64;
! 288: len -= 64;
! 289: }
! 290:
! 291: /* Handle any remaining bytes of data. */
! 292:
! 293: memcpy(ctx->in, buf, len);
! 294: }
! 295:
! 296: /*
! 297: * Final wrapup - pad to 64-byte boundary with the bit pattern
! 298: * 1 0* (64-bit count of bits processed, MSB-first)
! 299: */
! 300: static void MD5Final(unsigned char digest[16], MD5Context *ctx){
! 301: unsigned count;
! 302: unsigned char *p;
! 303:
! 304: /* Compute number of bytes mod 64 */
! 305: count = (ctx->bits[0] >> 3) & 0x3F;
! 306:
! 307: /* Set the first char of padding to 0x80. This is safe since there is
! 308: always at least one byte free */
! 309: p = ctx->in + count;
! 310: *p++ = 0x80;
! 311:
! 312: /* Bytes of padding needed to make 64 bytes */
! 313: count = 64 - 1 - count;
! 314:
! 315: /* Pad out to 56 mod 64 */
! 316: if (count < 8) {
! 317: /* Two lots of padding: Pad the first block to 64 bytes */
! 318: memset(p, 0, count);
! 319: byteReverse(ctx->in, 16);
! 320: MD5Transform(ctx->buf, (uint32 *)ctx->in);
! 321:
! 322: /* Now fill the next block with 56 bytes */
! 323: memset(ctx->in, 0, 56);
! 324: } else {
! 325: /* Pad block to 56 bytes */
! 326: memset(p, 0, count-8);
! 327: }
! 328: byteReverse(ctx->in, 14);
! 329:
! 330: /* Append length in bits and transform */
! 331: ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
! 332: ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
! 333:
! 334: MD5Transform(ctx->buf, (uint32 *)ctx->in);
! 335: byteReverse((unsigned char *)ctx->buf, 4);
! 336: memcpy(digest, ctx->buf, 16);
! 337: memset(ctx, 0, sizeof(ctx)); /* In case it is sensitive */
! 338: }
! 339:
! 340: /*
! 341: ** Convert a 128-bit MD5 digest into a 32-digit base-16 number.
! 342: */
! 343: static void MD5DigestToBase16(unsigned char *digest, char *zBuf){
! 344: static char const zEncode[] = "0123456789abcdef";
! 345: int i, j;
! 346:
! 347: for(j=i=0; i<16; i++){
! 348: int a = digest[i];
! 349: zBuf[j++] = zEncode[(a>>4)&0xf];
! 350: zBuf[j++] = zEncode[a & 0xf];
! 351: }
! 352: zBuf[j] = 0;
! 353: }
! 354:
! 355: /*
! 356: ** During testing, the special md5sum() aggregate function is available.
! 357: ** inside SQLite. The following routines implement that function.
! 358: */
! 359: static void md5step(sqlite3_context *context, int argc, sqlite3_value **argv){
! 360: MD5Context *p;
! 361: int i;
! 362: if( argc<1 ) return;
! 363: p = sqlite3_aggregate_context(context, sizeof(*p));
! 364: if( p==0 ) return;
! 365: if( !p->isInit ){
! 366: MD5Init(p);
! 367: }
! 368: for(i=0; i<argc; i++){
! 369: const char *zData = (char*)sqlite3_value_text(argv[i]);
! 370: if( zData ){
! 371: MD5Update(p, (unsigned char*)zData, strlen(zData));
! 372: }
! 373: }
! 374: }
! 375: static void md5finalize(sqlite3_context *context){
! 376: MD5Context *p;
! 377: unsigned char digest[16];
! 378: char zBuf[33];
! 379: p = sqlite3_aggregate_context(context, sizeof(*p));
! 380: MD5Final(digest,p);
! 381: MD5DigestToBase16(digest, zBuf);
! 382: sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
! 383: }
! 384:
! 385: /*************************************************************************
! 386: ** End of copied md5sum() code.
! 387: */
! 388:
! 389: typedef sqlite3_int64 i64;
! 390:
! 391: typedef struct Error Error;
! 392: typedef struct Sqlite Sqlite;
! 393: typedef struct Statement Statement;
! 394:
! 395: typedef struct Threadset Threadset;
! 396: typedef struct Thread Thread;
! 397:
! 398: /* Total number of errors in this process so far. */
! 399: static int nGlobalErr = 0;
! 400:
! 401: /* Set to true to run in "process" instead of "thread" mode. */
! 402: static int bProcessMode = 0;
! 403:
! 404: struct Error {
! 405: int rc;
! 406: int iLine;
! 407: char *zErr;
! 408: };
! 409:
! 410: struct Sqlite {
! 411: sqlite3 *db; /* Database handle */
! 412: Statement *pCache; /* Linked list of cached statements */
! 413: int nText; /* Size of array at aText[] */
! 414: char **aText; /* Stored text results */
! 415: };
! 416:
! 417: struct Statement {
! 418: sqlite3_stmt *pStmt; /* Pre-compiled statement handle */
! 419: Statement *pNext; /* Next statement in linked-list */
! 420: };
! 421:
! 422: struct Thread {
! 423: int iTid; /* Thread number within test */
! 424: int iArg; /* Integer argument passed by caller */
! 425:
! 426: pthread_t tid; /* Thread id */
! 427: char *(*xProc)(int, int); /* Thread main proc */
! 428: Thread *pNext; /* Next in this list of threads */
! 429: };
! 430:
! 431: struct Threadset {
! 432: int iMaxTid; /* Largest iTid value allocated so far */
! 433: Thread *pThread; /* Linked list of threads */
! 434: };
! 435:
! 436: static void free_err(Error *p){
! 437: sqlite3_free(p->zErr);
! 438: p->zErr = 0;
! 439: p->rc = 0;
! 440: }
! 441:
! 442: static void print_err(Error *p){
! 443: if( p->rc!=SQLITE_OK ){
! 444: printf("Error: (%d) \"%s\" at line %d\n", p->rc, p->zErr, p->iLine);
! 445: nGlobalErr++;
! 446: }
! 447: }
! 448:
! 449: static void print_and_free_err(Error *p){
! 450: print_err(p);
! 451: free_err(p);
! 452: }
! 453:
! 454: static void system_error(Error *pErr, int iSys){
! 455: pErr->rc = iSys;
! 456: pErr->zErr = (char *)sqlite3_malloc(512);
! 457: strerror_r(iSys, pErr->zErr, 512);
! 458: pErr->zErr[511] = '\0';
! 459: }
! 460:
! 461: static void sqlite_error(
! 462: Error *pErr,
! 463: Sqlite *pDb,
! 464: const char *zFunc
! 465: ){
! 466: pErr->rc = sqlite3_errcode(pDb->db);
! 467: pErr->zErr = sqlite3_mprintf(
! 468: "sqlite3_%s() - %s (%d)", zFunc, sqlite3_errmsg(pDb->db),
! 469: sqlite3_extended_errcode(pDb->db)
! 470: );
! 471: }
! 472:
! 473: static void test_error_x(
! 474: Error *pErr,
! 475: char *zErr
! 476: ){
! 477: if( pErr->rc==SQLITE_OK ){
! 478: pErr->rc = 1;
! 479: pErr->zErr = zErr;
! 480: }else{
! 481: sqlite3_free(zErr);
! 482: }
! 483: }
! 484:
! 485: static void clear_error_x(
! 486: Error *pErr,
! 487: int rc
! 488: ){
! 489: if( pErr->rc==rc ){
! 490: pErr->rc = SQLITE_OK;
! 491: sqlite3_free(pErr->zErr);
! 492: pErr->zErr = 0;
! 493: }
! 494: }
! 495:
! 496: static int busyhandler(void *pArg, int n){
! 497: usleep(10*1000);
! 498: return 1;
! 499: }
! 500:
! 501: static void opendb_x(
! 502: Error *pErr, /* IN/OUT: Error code */
! 503: Sqlite *pDb, /* OUT: Database handle */
! 504: const char *zFile, /* Database file name */
! 505: int bDelete /* True to delete db file before opening */
! 506: ){
! 507: if( pErr->rc==SQLITE_OK ){
! 508: int rc;
! 509: if( bDelete ) unlink(zFile);
! 510: rc = sqlite3_open(zFile, &pDb->db);
! 511: if( rc ){
! 512: sqlite_error(pErr, pDb, "open");
! 513: sqlite3_close(pDb->db);
! 514: pDb->db = 0;
! 515: }else{
! 516: sqlite3_create_function(
! 517: pDb->db, "md5sum", -1, SQLITE_UTF8, 0, 0, md5step, md5finalize
! 518: );
! 519: sqlite3_busy_handler(pDb->db, busyhandler, 0);
! 520: sqlite3_exec(pDb->db, "PRAGMA synchronous=OFF", 0, 0, 0);
! 521: }
! 522: }
! 523: }
! 524:
! 525: static void closedb_x(
! 526: Error *pErr, /* IN/OUT: Error code */
! 527: Sqlite *pDb /* OUT: Database handle */
! 528: ){
! 529: int rc;
! 530: int i;
! 531: Statement *pIter;
! 532: Statement *pNext;
! 533: for(pIter=pDb->pCache; pIter; pIter=pNext){
! 534: pNext = pIter->pNext;
! 535: sqlite3_finalize(pIter->pStmt);
! 536: sqlite3_free(pIter);
! 537: }
! 538: for(i=0; i<pDb->nText; i++){
! 539: sqlite3_free(pDb->aText[i]);
! 540: }
! 541: sqlite3_free(pDb->aText);
! 542: rc = sqlite3_close(pDb->db);
! 543: if( rc && pErr->rc==SQLITE_OK ){
! 544: pErr->zErr = sqlite3_mprintf("%s", sqlite3_errmsg(pDb->db));
! 545: }
! 546: memset(pDb, 0, sizeof(Sqlite));
! 547: }
! 548:
! 549: static void sql_script_x(
! 550: Error *pErr, /* IN/OUT: Error code */
! 551: Sqlite *pDb, /* Database handle */
! 552: const char *zSql /* SQL script to execute */
! 553: ){
! 554: if( pErr->rc==SQLITE_OK ){
! 555: pErr->rc = sqlite3_exec(pDb->db, zSql, 0, 0, &pErr->zErr);
! 556: }
! 557: }
! 558:
! 559: static Statement *getSqlStatement(
! 560: Error *pErr, /* IN/OUT: Error code */
! 561: Sqlite *pDb, /* Database handle */
! 562: const char *zSql /* SQL statement */
! 563: ){
! 564: Statement *pRet;
! 565: int rc;
! 566:
! 567: for(pRet=pDb->pCache; pRet; pRet=pRet->pNext){
! 568: if( 0==strcmp(sqlite3_sql(pRet->pStmt), zSql) ){
! 569: return pRet;
! 570: }
! 571: }
! 572:
! 573: pRet = sqlite3_malloc(sizeof(Statement));
! 574: rc = sqlite3_prepare_v2(pDb->db, zSql, -1, &pRet->pStmt, 0);
! 575: if( rc!=SQLITE_OK ){
! 576: sqlite_error(pErr, pDb, "prepare_v2");
! 577: return 0;
! 578: }
! 579: assert( 0==strcmp(sqlite3_sql(pRet->pStmt), zSql) );
! 580:
! 581: pRet->pNext = pDb->pCache;
! 582: pDb->pCache = pRet;
! 583: return pRet;
! 584: }
! 585:
! 586: static sqlite3_stmt *getAndBindSqlStatement(
! 587: Error *pErr, /* IN/OUT: Error code */
! 588: Sqlite *pDb, /* Database handle */
! 589: va_list ap /* SQL followed by parameters */
! 590: ){
! 591: Statement *pStatement; /* The SQLite statement wrapper */
! 592: sqlite3_stmt *pStmt; /* The SQLite statement to return */
! 593: int i; /* Used to iterate through parameters */
! 594:
! 595: pStatement = getSqlStatement(pErr, pDb, va_arg(ap, const char *));
! 596: if( !pStatement ) return 0;
! 597: pStmt = pStatement->pStmt;
! 598: for(i=1; i<=sqlite3_bind_parameter_count(pStmt); i++){
! 599: const char *zName = sqlite3_bind_parameter_name(pStmt, i);
! 600: void * pArg = va_arg(ap, void*);
! 601:
! 602: switch( zName[1] ){
! 603: case 'i':
! 604: sqlite3_bind_int64(pStmt, i, *(i64 *)pArg);
! 605: break;
! 606:
! 607: default:
! 608: pErr->rc = 1;
! 609: pErr->zErr = sqlite3_mprintf("Cannot discern type: \"%s\"", zName);
! 610: pStmt = 0;
! 611: break;
! 612: }
! 613: }
! 614:
! 615: return pStmt;
! 616: }
! 617:
! 618: static i64 execsql_i64_x(
! 619: Error *pErr, /* IN/OUT: Error code */
! 620: Sqlite *pDb, /* Database handle */
! 621: ... /* SQL and pointers to parameter values */
! 622: ){
! 623: i64 iRet = 0;
! 624: if( pErr->rc==SQLITE_OK ){
! 625: sqlite3_stmt *pStmt; /* SQL statement to execute */
! 626: va_list ap; /* ... arguments */
! 627: int i; /* Used to iterate through parameters */
! 628: va_start(ap, pDb);
! 629: pStmt = getAndBindSqlStatement(pErr, pDb, ap);
! 630: if( pStmt ){
! 631: int rc;
! 632: int first = 1;
! 633: while( SQLITE_ROW==sqlite3_step(pStmt) ){
! 634: if( first && sqlite3_column_count(pStmt)>0 ){
! 635: iRet = sqlite3_column_int64(pStmt, 0);
! 636: }
! 637: first = 0;
! 638: }
! 639: if( SQLITE_OK!=sqlite3_reset(pStmt) ){
! 640: sqlite_error(pErr, pDb, "reset");
! 641: }
! 642: }
! 643: va_end(ap);
! 644: }
! 645: return iRet;
! 646: }
! 647:
! 648: static char * execsql_text_x(
! 649: Error *pErr, /* IN/OUT: Error code */
! 650: Sqlite *pDb, /* Database handle */
! 651: int iSlot, /* Db handle slot to store text in */
! 652: ... /* SQL and pointers to parameter values */
! 653: ){
! 654: char *zRet = 0;
! 655:
! 656: if( iSlot>=pDb->nText ){
! 657: int nByte = sizeof(char *)*(iSlot+1);
! 658: pDb->aText = (char **)sqlite3_realloc(pDb->aText, nByte);
! 659: memset(&pDb->aText[pDb->nText], 0, sizeof(char*)*(iSlot+1-pDb->nText));
! 660: pDb->nText = iSlot+1;
! 661: }
! 662:
! 663: if( pErr->rc==SQLITE_OK ){
! 664: sqlite3_stmt *pStmt; /* SQL statement to execute */
! 665: va_list ap; /* ... arguments */
! 666: int i; /* Used to iterate through parameters */
! 667: va_start(ap, iSlot);
! 668: pStmt = getAndBindSqlStatement(pErr, pDb, ap);
! 669: if( pStmt ){
! 670: int rc;
! 671: int first = 1;
! 672: while( SQLITE_ROW==sqlite3_step(pStmt) ){
! 673: if( first && sqlite3_column_count(pStmt)>0 ){
! 674: zRet = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
! 675: sqlite3_free(pDb->aText[iSlot]);
! 676: pDb->aText[iSlot] = zRet;
! 677: }
! 678: first = 0;
! 679: }
! 680: if( SQLITE_OK!=sqlite3_reset(pStmt) ){
! 681: sqlite_error(pErr, pDb, "reset");
! 682: }
! 683: }
! 684: va_end(ap);
! 685: }
! 686:
! 687: return zRet;
! 688: }
! 689:
! 690: static void integrity_check_x(
! 691: Error *pErr, /* IN/OUT: Error code */
! 692: Sqlite *pDb /* Database handle */
! 693: ){
! 694: if( pErr->rc==SQLITE_OK ){
! 695: Statement *pStatement; /* Statement to execute */
! 696: int rc; /* Return code */
! 697: char *zErr = 0; /* Integrity check error */
! 698:
! 699: pStatement = getSqlStatement(pErr, pDb, "PRAGMA integrity_check");
! 700: if( pStatement ){
! 701: sqlite3_stmt *pStmt = pStatement->pStmt;
! 702: while( SQLITE_ROW==sqlite3_step(pStmt) ){
! 703: const char *z = sqlite3_column_text(pStmt, 0);
! 704: if( strcmp(z, "ok") ){
! 705: if( zErr==0 ){
! 706: zErr = sqlite3_mprintf("%s", z);
! 707: }else{
! 708: zErr = sqlite3_mprintf("%z\n%s", zErr, z);
! 709: }
! 710: }
! 711: }
! 712: sqlite3_reset(pStmt);
! 713:
! 714: if( zErr ){
! 715: pErr->zErr = zErr;
! 716: pErr->rc = 1;
! 717: }
! 718: }
! 719: }
! 720: }
! 721:
! 722: static void *launch_thread_main(void *pArg){
! 723: Thread *p = (Thread *)pArg;
! 724: return (void *)p->xProc(p->iTid, p->iArg);
! 725: }
! 726:
! 727: static void launch_thread_x(
! 728: Error *pErr, /* IN/OUT: Error code */
! 729: Threadset *pThreads, /* Thread set */
! 730: char *(*xProc)(int, int), /* Proc to run */
! 731: int iArg /* Argument passed to thread proc */
! 732: ){
! 733: if( pErr->rc==SQLITE_OK ){
! 734: int iTid = ++pThreads->iMaxTid;
! 735: Thread *p;
! 736: int rc;
! 737:
! 738: p = (Thread *)sqlite3_malloc(sizeof(Thread));
! 739: memset(p, 0, sizeof(Thread));
! 740: p->iTid = iTid;
! 741: p->iArg = iArg;
! 742: p->xProc = xProc;
! 743:
! 744: rc = pthread_create(&p->tid, NULL, launch_thread_main, (void *)p);
! 745: if( rc!=0 ){
! 746: system_error(pErr, rc);
! 747: sqlite3_free(p);
! 748: }else{
! 749: p->pNext = pThreads->pThread;
! 750: pThreads->pThread = p;
! 751: }
! 752: }
! 753: }
! 754:
! 755: static void join_all_threads_x(
! 756: Error *pErr, /* IN/OUT: Error code */
! 757: Threadset *pThreads /* Thread set */
! 758: ){
! 759: Thread *p;
! 760: Thread *pNext;
! 761: for(p=pThreads->pThread; p; p=pNext){
! 762: void *ret;
! 763: pNext = p->pNext;
! 764: int rc;
! 765: rc = pthread_join(p->tid, &ret);
! 766: if( rc!=0 ){
! 767: if( pErr->rc==SQLITE_OK ) system_error(pErr, rc);
! 768: }else{
! 769: printf("Thread %d says: %s\n", p->iTid, (ret==0 ? "..." : (char *)ret));
! 770: }
! 771: sqlite3_free(p);
! 772: }
! 773: pThreads->pThread = 0;
! 774: }
! 775:
! 776: static i64 filesize_x(
! 777: Error *pErr,
! 778: const char *zFile
! 779: ){
! 780: i64 iRet = 0;
! 781: if( pErr->rc==SQLITE_OK ){
! 782: struct stat sStat;
! 783: if( stat(zFile, &sStat) ){
! 784: iRet = -1;
! 785: }else{
! 786: iRet = sStat.st_size;
! 787: }
! 788: }
! 789: return iRet;
! 790: }
! 791:
! 792: static void filecopy_x(
! 793: Error *pErr,
! 794: const char *zFrom,
! 795: const char *zTo
! 796: ){
! 797: if( pErr->rc==SQLITE_OK ){
! 798: i64 nByte = filesize_x(pErr, zFrom);
! 799: if( nByte<0 ){
! 800: test_error_x(pErr, sqlite3_mprintf("no such file: %s", zFrom));
! 801: }else{
! 802: i64 iOff;
! 803: char aBuf[1024];
! 804: int fd1;
! 805: int fd2;
! 806: unlink(zTo);
! 807:
! 808: fd1 = open(zFrom, O_RDONLY);
! 809: if( fd1<0 ){
! 810: system_error(pErr, errno);
! 811: return;
! 812: }
! 813: fd2 = open(zTo, O_RDWR|O_CREAT|O_EXCL, 0644);
! 814: if( fd2<0 ){
! 815: system_error(pErr, errno);
! 816: close(fd1);
! 817: return;
! 818: }
! 819:
! 820: iOff = 0;
! 821: while( iOff<nByte ){
! 822: int nCopy = sizeof(aBuf);
! 823: if( nCopy+iOff>nByte ){
! 824: nCopy = nByte - iOff;
! 825: }
! 826: if( nCopy!=read(fd1, aBuf, nCopy) ){
! 827: system_error(pErr, errno);
! 828: break;
! 829: }
! 830: if( nCopy!=write(fd2, aBuf, nCopy) ){
! 831: system_error(pErr, errno);
! 832: break;
! 833: }
! 834: iOff += nCopy;
! 835: }
! 836:
! 837: close(fd1);
! 838: close(fd2);
! 839: }
! 840: }
! 841: }
! 842:
! 843: /*
! 844: ** Used by setstoptime() and timetostop().
! 845: */
! 846: static double timelimit = 0.0;
! 847: static sqlite3_vfs *pTimelimitVfs = 0;
! 848:
! 849: static void setstoptime_x(
! 850: Error *pErr, /* IN/OUT: Error code */
! 851: int nMs /* Milliseconds until "stop time" */
! 852: ){
! 853: if( pErr->rc==SQLITE_OK ){
! 854: double t;
! 855: int rc;
! 856: pTimelimitVfs = sqlite3_vfs_find(0);
! 857: rc = pTimelimitVfs->xCurrentTime(pTimelimitVfs, &t);
! 858: if( rc!=SQLITE_OK ){
! 859: pErr->rc = rc;
! 860: }else{
! 861: timelimit = t + ((double)nMs)/(1000.0*60.0*60.0*24.0);
! 862: }
! 863: }
! 864: }
! 865:
! 866: static int timetostop_x(
! 867: Error *pErr /* IN/OUT: Error code */
! 868: ){
! 869: int ret = 1;
! 870: if( pErr->rc==SQLITE_OK ){
! 871: double t;
! 872: int rc;
! 873: rc = pTimelimitVfs->xCurrentTime(pTimelimitVfs, &t);
! 874: if( rc!=SQLITE_OK ){
! 875: pErr->rc = rc;
! 876: }else{
! 877: ret = (t >= timelimit);
! 878: }
! 879: }
! 880: return ret;
! 881: }
! 882:
! 883: /*
! 884: ** The "Set Error Line" macro.
! 885: */
! 886: #define SEL(e) ((e)->iLine = ((e)->rc ? (e)->iLine : __LINE__))
! 887:
! 888:
! 889: /*************************************************************************
! 890: **************************************************************************
! 891: **************************************************************************
! 892: ** End infrastructure. Begin tests.
! 893: */
! 894:
! 895: #define WALTHREAD1_NTHREAD 10
! 896: #define WALTHREAD3_NTHREAD 6
! 897:
! 898: static char *walthread1_thread(int iTid, int iArg){
! 899: Error err = {0}; /* Error code and message */
! 900: Sqlite db = {0}; /* SQLite database connection */
! 901: int nIter = 0; /* Iterations so far */
! 902:
! 903: opendb(&err, &db, "test.db", 0);
! 904: while( !timetostop(&err) ){
! 905: const char *azSql[] = {
! 906: "SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1)",
! 907: "SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1)",
! 908: };
! 909: char *z1, *z2, *z3;
! 910:
! 911: execsql(&err, &db, "BEGIN");
! 912: integrity_check(&err, &db);
! 913: z1 = execsql_text(&err, &db, 1, azSql[0]);
! 914: z2 = execsql_text(&err, &db, 2, azSql[1]);
! 915: z3 = execsql_text(&err, &db, 3, azSql[0]);
! 916: execsql(&err, &db, "COMMIT");
! 917:
! 918: if( strcmp(z1, z2) || strcmp(z1, z3) ){
! 919: test_error(&err, "Failed read: %s %s %s", z1, z2, z3);
! 920: }
! 921:
! 922: sql_script(&err, &db,
! 923: "BEGIN;"
! 924: "INSERT INTO t1 VALUES(randomblob(100));"
! 925: "INSERT INTO t1 VALUES(randomblob(100));"
! 926: "INSERT INTO t1 SELECT md5sum(x) FROM t1;"
! 927: "COMMIT;"
! 928: );
! 929: nIter++;
! 930: }
! 931: closedb(&err, &db);
! 932:
! 933: print_and_free_err(&err);
! 934: return sqlite3_mprintf("%d iterations", nIter);
! 935: }
! 936:
! 937: static char *walthread1_ckpt_thread(int iTid, int iArg){
! 938: Error err = {0}; /* Error code and message */
! 939: Sqlite db = {0}; /* SQLite database connection */
! 940: int nCkpt = 0; /* Checkpoints so far */
! 941:
! 942: opendb(&err, &db, "test.db", 0);
! 943: while( !timetostop(&err) ){
! 944: usleep(500*1000);
! 945: execsql(&err, &db, "PRAGMA wal_checkpoint");
! 946: if( err.rc==SQLITE_OK ) nCkpt++;
! 947: clear_error(&err, SQLITE_BUSY);
! 948: }
! 949: closedb(&err, &db);
! 950:
! 951: print_and_free_err(&err);
! 952: return sqlite3_mprintf("%d checkpoints", nCkpt);
! 953: }
! 954:
! 955: static void walthread1(int nMs){
! 956: Error err = {0}; /* Error code and message */
! 957: Sqlite db = {0}; /* SQLite database connection */
! 958: Threadset threads = {0}; /* Test threads */
! 959: int i; /* Iterator variable */
! 960:
! 961: opendb(&err, &db, "test.db", 1);
! 962: sql_script(&err, &db,
! 963: "PRAGMA journal_mode = WAL;"
! 964: "CREATE TABLE t1(x PRIMARY KEY);"
! 965: "INSERT INTO t1 VALUES(randomblob(100));"
! 966: "INSERT INTO t1 VALUES(randomblob(100));"
! 967: "INSERT INTO t1 SELECT md5sum(x) FROM t1;"
! 968: );
! 969:
! 970: setstoptime(&err, nMs);
! 971: for(i=0; i<WALTHREAD1_NTHREAD; i++){
! 972: launch_thread(&err, &threads, walthread1_thread, 0);
! 973: }
! 974: launch_thread(&err, &threads, walthread1_ckpt_thread, 0);
! 975: join_all_threads(&err, &threads);
! 976:
! 977: print_and_free_err(&err);
! 978: }
! 979:
! 980: static char *walthread2_thread(int iTid, int iArg){
! 981: Error err = {0}; /* Error code and message */
! 982: Sqlite db = {0}; /* SQLite database connection */
! 983: int anTrans[2] = {0, 0}; /* Number of WAL and Rollback transactions */
! 984:
! 985: const char *zJournal = "PRAGMA journal_mode = WAL";
! 986: if( iArg ){ zJournal = "PRAGMA journal_mode = DELETE"; }
! 987:
! 988: while( !timetostop(&err) ){
! 989: int journal_exists = 0;
! 990: int wal_exists = 0;
! 991:
! 992: opendb(&err, &db, "test.db", 0);
! 993:
! 994: sql_script(&err, &db, zJournal);
! 995: clear_error(&err, SQLITE_BUSY);
! 996: sql_script(&err, &db, "BEGIN");
! 997: sql_script(&err, &db, "INSERT INTO t1 VALUES(NULL, randomblob(100))");
! 998:
! 999: journal_exists = (filesize(&err, "test.db-journal") >= 0);
! 1000: wal_exists = (filesize(&err, "test.db-wal") >= 0);
! 1001: if( (journal_exists+wal_exists)!=1 ){
! 1002: test_error(&err, "File system looks incorrect (%d, %d)",
! 1003: journal_exists, wal_exists
! 1004: );
! 1005: }
! 1006: anTrans[journal_exists]++;
! 1007:
! 1008: sql_script(&err, &db, "COMMIT");
! 1009: integrity_check(&err, &db);
! 1010: closedb(&err, &db);
! 1011: }
! 1012:
! 1013: print_and_free_err(&err);
! 1014: return sqlite3_mprintf("W %d R %d", anTrans[0], anTrans[1]);
! 1015: }
! 1016:
! 1017: static void walthread2(int nMs){
! 1018: Error err = {0};
! 1019: Sqlite db = {0};
! 1020: Threadset threads = {0};
! 1021:
! 1022: opendb(&err, &db, "test.db", 1);
! 1023: sql_script(&err, &db, "CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE)");
! 1024: closedb(&err, &db);
! 1025:
! 1026: setstoptime(&err, nMs);
! 1027: launch_thread(&err, &threads, walthread2_thread, 0);
! 1028: launch_thread(&err, &threads, walthread2_thread, 0);
! 1029: launch_thread(&err, &threads, walthread2_thread, 1);
! 1030: launch_thread(&err, &threads, walthread2_thread, 1);
! 1031: join_all_threads(&err, &threads);
! 1032:
! 1033: print_and_free_err(&err);
! 1034: }
! 1035:
! 1036: static char *walthread3_thread(int iTid, int iArg){
! 1037: Error err = {0}; /* Error code and message */
! 1038: Sqlite db = {0}; /* SQLite database connection */
! 1039: i64 iNextWrite; /* Next value this thread will write */
! 1040:
! 1041: opendb(&err, &db, "test.db", 0);
! 1042: sql_script(&err, &db, "PRAGMA wal_autocheckpoint = 10");
! 1043:
! 1044: iNextWrite = iArg+1;
! 1045: while( 1 ){
! 1046: i64 sum1;
! 1047: i64 sum2;
! 1048: int stop = 0; /* True to stop executing (test timed out) */
! 1049:
! 1050: while( 0==(stop = timetostop(&err)) ){
! 1051: i64 iMax = execsql_i64(&err, &db, "SELECT max(cnt) FROM t1");
! 1052: if( iMax+1==iNextWrite ) break;
! 1053: }
! 1054: if( stop ) break;
! 1055:
! 1056: sum1 = execsql_i64(&err, &db, "SELECT sum(cnt) FROM t1");
! 1057: sum2 = execsql_i64(&err, &db, "SELECT sum(sum1) FROM t1");
! 1058: execsql_i64(&err, &db,
! 1059: "INSERT INTO t1 VALUES(:iNextWrite, :iSum1, :iSum2)",
! 1060: &iNextWrite, &sum1, &sum2
! 1061: );
! 1062: integrity_check(&err, &db);
! 1063:
! 1064: iNextWrite += WALTHREAD3_NTHREAD;
! 1065: }
! 1066:
! 1067: closedb(&err, &db);
! 1068: print_and_free_err(&err);
! 1069: return 0;
! 1070: }
! 1071:
! 1072: static void walthread3(int nMs){
! 1073: Error err = {0};
! 1074: Sqlite db = {0};
! 1075: Threadset threads = {0};
! 1076: int i;
! 1077:
! 1078: opendb(&err, &db, "test.db", 1);
! 1079: sql_script(&err, &db,
! 1080: "PRAGMA journal_mode = WAL;"
! 1081: "CREATE TABLE t1(cnt PRIMARY KEY, sum1, sum2);"
! 1082: "CREATE INDEX i1 ON t1(sum1);"
! 1083: "CREATE INDEX i2 ON t1(sum2);"
! 1084: "INSERT INTO t1 VALUES(0, 0, 0);"
! 1085: );
! 1086: closedb(&err, &db);
! 1087:
! 1088: setstoptime(&err, nMs);
! 1089: for(i=0; i<WALTHREAD3_NTHREAD; i++){
! 1090: launch_thread(&err, &threads, walthread3_thread, i);
! 1091: }
! 1092: join_all_threads(&err, &threads);
! 1093:
! 1094: print_and_free_err(&err);
! 1095: }
! 1096:
! 1097: static char *walthread4_reader_thread(int iTid, int iArg){
! 1098: Error err = {0}; /* Error code and message */
! 1099: Sqlite db = {0}; /* SQLite database connection */
! 1100:
! 1101: opendb(&err, &db, "test.db", 0);
! 1102: while( !timetostop(&err) ){
! 1103: integrity_check(&err, &db);
! 1104: }
! 1105: closedb(&err, &db);
! 1106:
! 1107: print_and_free_err(&err);
! 1108: return 0;
! 1109: }
! 1110:
! 1111: static char *walthread4_writer_thread(int iTid, int iArg){
! 1112: Error err = {0}; /* Error code and message */
! 1113: Sqlite db = {0}; /* SQLite database connection */
! 1114: i64 iRow = 1;
! 1115:
! 1116: opendb(&err, &db, "test.db", 0);
! 1117: sql_script(&err, &db, "PRAGMA wal_autocheckpoint = 15;");
! 1118: while( !timetostop(&err) ){
! 1119: execsql_i64(
! 1120: &err, &db, "REPLACE INTO t1 VALUES(:iRow, randomblob(300))", &iRow
! 1121: );
! 1122: iRow++;
! 1123: if( iRow==10 ) iRow = 0;
! 1124: }
! 1125: closedb(&err, &db);
! 1126:
! 1127: print_and_free_err(&err);
! 1128: return 0;
! 1129: }
! 1130:
! 1131: static void walthread4(int nMs){
! 1132: Error err = {0};
! 1133: Sqlite db = {0};
! 1134: Threadset threads = {0};
! 1135:
! 1136: opendb(&err, &db, "test.db", 1);
! 1137: sql_script(&err, &db,
! 1138: "PRAGMA journal_mode = WAL;"
! 1139: "CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);"
! 1140: );
! 1141: closedb(&err, &db);
! 1142:
! 1143: setstoptime(&err, nMs);
! 1144: launch_thread(&err, &threads, walthread4_reader_thread, 0);
! 1145: launch_thread(&err, &threads, walthread4_writer_thread, 0);
! 1146: join_all_threads(&err, &threads);
! 1147:
! 1148: print_and_free_err(&err);
! 1149: }
! 1150:
! 1151: static char *walthread5_thread(int iTid, int iArg){
! 1152: Error err = {0}; /* Error code and message */
! 1153: Sqlite db = {0}; /* SQLite database connection */
! 1154: i64 nRow;
! 1155:
! 1156: opendb(&err, &db, "test.db", 0);
! 1157: nRow = execsql_i64(&err, &db, "SELECT count(*) FROM t1");
! 1158: closedb(&err, &db);
! 1159:
! 1160: if( nRow!=65536 ) test_error(&err, "Bad row count: %d", (int)nRow);
! 1161: print_and_free_err(&err);
! 1162: return 0;
! 1163: }
! 1164: static void walthread5(int nMs){
! 1165: Error err = {0};
! 1166: Sqlite db = {0};
! 1167: Threadset threads = {0};
! 1168:
! 1169: opendb(&err, &db, "test.db", 1);
! 1170: sql_script(&err, &db,
! 1171: "PRAGMA wal_autocheckpoint = 0;"
! 1172: "PRAGMA page_size = 1024;"
! 1173: "PRAGMA journal_mode = WAL;"
! 1174: "CREATE TABLE t1(x);"
! 1175: "BEGIN;"
! 1176: "INSERT INTO t1 VALUES(randomblob(900));"
! 1177: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */"
! 1178: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */"
! 1179: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */"
! 1180: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */"
! 1181: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */"
! 1182: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */"
! 1183: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */"
! 1184: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */"
! 1185: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */"
! 1186: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */"
! 1187: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */"
! 1188: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */"
! 1189: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */"
! 1190: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */"
! 1191: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32768 */"
! 1192: "INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 65536 */"
! 1193: "COMMIT;"
! 1194: );
! 1195: filecopy(&err, "test.db", "test_sv.db");
! 1196: filecopy(&err, "test.db-wal", "test_sv.db-wal");
! 1197: closedb(&err, &db);
! 1198:
! 1199: filecopy(&err, "test_sv.db", "test.db");
! 1200: filecopy(&err, "test_sv.db-wal", "test.db-wal");
! 1201:
! 1202: if( err.rc==SQLITE_OK ){
! 1203: printf(" WAL file is %d bytes,", (int)filesize(&err,"test.db-wal"));
! 1204: printf(" DB file is %d.\n", (int)filesize(&err,"test.db"));
! 1205: }
! 1206:
! 1207: setstoptime(&err, nMs);
! 1208: launch_thread(&err, &threads, walthread5_thread, 0);
! 1209: launch_thread(&err, &threads, walthread5_thread, 0);
! 1210: launch_thread(&err, &threads, walthread5_thread, 0);
! 1211: launch_thread(&err, &threads, walthread5_thread, 0);
! 1212: launch_thread(&err, &threads, walthread5_thread, 0);
! 1213: join_all_threads(&err, &threads);
! 1214:
! 1215: if( err.rc==SQLITE_OK ){
! 1216: printf(" WAL file is %d bytes,", (int)filesize(&err,"test.db-wal"));
! 1217: printf(" DB file is %d.\n", (int)filesize(&err,"test.db"));
! 1218: }
! 1219:
! 1220: print_and_free_err(&err);
! 1221: }
! 1222:
! 1223: /*------------------------------------------------------------------------
! 1224: ** Test case "cgt_pager_1"
! 1225: */
! 1226: #define CALLGRINDTEST1_NROW 10000
! 1227: static void cgt_pager_1_populate(Error *pErr, Sqlite *pDb){
! 1228: const char *zInsert = "INSERT INTO t1 VALUES(:iRow, zeroblob(:iBlob))";
! 1229: i64 iRow;
! 1230: sql_script(pErr, pDb, "BEGIN");
! 1231: for(iRow=1; iRow<=CALLGRINDTEST1_NROW; iRow++){
! 1232: i64 iBlob = 600 + (iRow%300);
! 1233: execsql(pErr, pDb, zInsert, &iRow, &iBlob);
! 1234: }
! 1235: sql_script(pErr, pDb, "COMMIT");
! 1236: }
! 1237: static void cgt_pager_1_update(Error *pErr, Sqlite *pDb){
! 1238: const char *zUpdate = "UPDATE t1 SET b = zeroblob(:iBlob) WHERE a = :iRow";
! 1239: i64 iRow;
! 1240: sql_script(pErr, pDb, "BEGIN");
! 1241: for(iRow=1; iRow<=CALLGRINDTEST1_NROW; iRow++){
! 1242: i64 iBlob = 600 + ((iRow+100)%300);
! 1243: execsql(pErr, pDb, zUpdate, &iBlob, &iRow);
! 1244: }
! 1245: sql_script(pErr, pDb, "COMMIT");
! 1246: }
! 1247: static void cgt_pager_1_read(Error *pErr, Sqlite *pDb){
! 1248: i64 iRow;
! 1249: sql_script(pErr, pDb, "BEGIN");
! 1250: for(iRow=1; iRow<=CALLGRINDTEST1_NROW; iRow++){
! 1251: execsql(pErr, pDb, "SELECT * FROM t1 WHERE a = :iRow", &iRow);
! 1252: }
! 1253: sql_script(pErr, pDb, "COMMIT");
! 1254: }
! 1255: static void cgt_pager_1(int nMs){
! 1256: void (*xSub)(Error *, Sqlite *);
! 1257: Error err = {0};
! 1258: Sqlite db = {0};
! 1259:
! 1260: opendb(&err, &db, "test.db", 1);
! 1261: sql_script(&err, &db,
! 1262: "PRAGMA cache_size = 2000;"
! 1263: "PRAGMA page_size = 1024;"
! 1264: "CREATE TABLE t1(a INTEGER PRIMARY KEY, b BLOB);"
! 1265: );
! 1266:
! 1267: xSub = cgt_pager_1_populate; xSub(&err, &db);
! 1268: xSub = cgt_pager_1_update; xSub(&err, &db);
! 1269: xSub = cgt_pager_1_read; xSub(&err, &db);
! 1270:
! 1271: closedb(&err, &db);
! 1272: print_and_free_err(&err);
! 1273: }
! 1274:
! 1275: /*------------------------------------------------------------------------
! 1276: ** Test case "dynamic_triggers"
! 1277: **
! 1278: ** Two threads executing statements that cause deeply nested triggers
! 1279: ** to fire. And one thread busily creating and deleting triggers. This
! 1280: ** is an attempt to find a bug reported to us.
! 1281: */
! 1282:
! 1283: static char *dynamic_triggers_1(int iTid, int iArg){
! 1284: Error err = {0}; /* Error code and message */
! 1285: Sqlite db = {0}; /* SQLite database connection */
! 1286: int nDrop = 0;
! 1287: int nCreate = 0;
! 1288:
! 1289: opendb(&err, &db, "test.db", 0);
! 1290: while( !timetostop(&err) ){
! 1291: int i;
! 1292:
! 1293: for(i=1; i<9; i++){
! 1294: char *zSql = sqlite3_mprintf(
! 1295: "CREATE TRIGGER itr%d BEFORE INSERT ON t%d BEGIN "
! 1296: "INSERT INTO t%d VALUES(new.x, new.y);"
! 1297: "END;", i, i, i+1
! 1298: );
! 1299: execsql(&err, &db, zSql);
! 1300: sqlite3_free(zSql);
! 1301: nCreate++;
! 1302: }
! 1303:
! 1304: for(i=1; i<9; i++){
! 1305: char *zSql = sqlite3_mprintf(
! 1306: "CREATE TRIGGER dtr%d BEFORE DELETE ON t%d BEGIN "
! 1307: "DELETE FROM t%d WHERE x = old.x; "
! 1308: "END;", i, i, i+1
! 1309: );
! 1310: execsql(&err, &db, zSql);
! 1311: sqlite3_free(zSql);
! 1312: nCreate++;
! 1313: }
! 1314:
! 1315: for(i=1; i<9; i++){
! 1316: char *zSql = sqlite3_mprintf("DROP TRIGGER itr%d", i);
! 1317: execsql(&err, &db, zSql);
! 1318: sqlite3_free(zSql);
! 1319: nDrop++;
! 1320: }
! 1321:
! 1322: for(i=1; i<9; i++){
! 1323: char *zSql = sqlite3_mprintf("DROP TRIGGER dtr%d", i);
! 1324: execsql(&err, &db, zSql);
! 1325: sqlite3_free(zSql);
! 1326: nDrop++;
! 1327: }
! 1328: }
! 1329:
! 1330: print_and_free_err(&err);
! 1331: return sqlite3_mprintf("%d created, %d dropped", nCreate, nDrop);
! 1332: }
! 1333:
! 1334: static char *dynamic_triggers_2(int iTid, int iArg){
! 1335: Error err = {0}; /* Error code and message */
! 1336: Sqlite db = {0}; /* SQLite database connection */
! 1337: i64 iVal = 0;
! 1338: int nInsert = 0;
! 1339: int nDelete = 0;
! 1340:
! 1341: opendb(&err, &db, "test.db", 0);
! 1342: while( !timetostop(&err) ){
! 1343: do {
! 1344: iVal = (iVal+1)%100;
! 1345: execsql(&err, &db, "INSERT INTO t1 VALUES(:iX, :iY+1)", &iVal, &iVal);
! 1346: nInsert++;
! 1347: } while( iVal );
! 1348:
! 1349: do {
! 1350: iVal = (iVal+1)%100;
! 1351: execsql(&err, &db, "DELETE FROM t1 WHERE x = :iX", &iVal);
! 1352: nDelete++;
! 1353: } while( iVal );
! 1354: }
! 1355:
! 1356: print_and_free_err(&err);
! 1357: return sqlite3_mprintf("%d inserts, %d deletes", nInsert, nDelete);
! 1358: }
! 1359:
! 1360: static void dynamic_triggers(int nMs){
! 1361: Error err = {0};
! 1362: Sqlite db = {0};
! 1363: Threadset threads = {0};
! 1364:
! 1365: opendb(&err, &db, "test.db", 1);
! 1366: sql_script(&err, &db,
! 1367: "PRAGMA page_size = 1024;"
! 1368: "PRAGMA journal_mode = WAL;"
! 1369: "CREATE TABLE t1(x, y);"
! 1370: "CREATE TABLE t2(x, y);"
! 1371: "CREATE TABLE t3(x, y);"
! 1372: "CREATE TABLE t4(x, y);"
! 1373: "CREATE TABLE t5(x, y);"
! 1374: "CREATE TABLE t6(x, y);"
! 1375: "CREATE TABLE t7(x, y);"
! 1376: "CREATE TABLE t8(x, y);"
! 1377: "CREATE TABLE t9(x, y);"
! 1378: );
! 1379:
! 1380: setstoptime(&err, nMs);
! 1381:
! 1382: sqlite3_enable_shared_cache(1);
! 1383: launch_thread(&err, &threads, dynamic_triggers_2, 0);
! 1384: launch_thread(&err, &threads, dynamic_triggers_2, 0);
! 1385: sqlite3_enable_shared_cache(0);
! 1386:
! 1387: sleep(2);
! 1388:
! 1389: launch_thread(&err, &threads, dynamic_triggers_2, 0);
! 1390: launch_thread(&err, &threads, dynamic_triggers_1, 0);
! 1391:
! 1392: join_all_threads(&err, &threads);
! 1393:
! 1394: print_and_free_err(&err);
! 1395: }
! 1396:
! 1397: #include "tt3_checkpoint.c"
! 1398:
! 1399: int main(int argc, char **argv){
! 1400: struct ThreadTest {
! 1401: void (*xTest)(int);
! 1402: const char *zTest;
! 1403: int nMs;
! 1404: } aTest[] = {
! 1405: { walthread1, "walthread1", 20000 },
! 1406: { walthread2, "walthread2", 20000 },
! 1407: { walthread3, "walthread3", 20000 },
! 1408: { walthread4, "walthread4", 20000 },
! 1409: { walthread5, "walthread5", 1000 },
! 1410: { walthread5, "walthread5", 1000 },
! 1411:
! 1412: { cgt_pager_1, "cgt_pager_1", 0 },
! 1413: { dynamic_triggers, "dynamic_triggers", 20000 },
! 1414:
! 1415: { checkpoint_starvation_1, "checkpoint_starvation_1", 10000 },
! 1416: { checkpoint_starvation_2, "checkpoint_starvation_2", 10000 },
! 1417: };
! 1418:
! 1419: int i;
! 1420: char *zTest = 0;
! 1421: int nTest = 0;
! 1422: int bTestfound = 0;
! 1423: int bPrefix = 0;
! 1424:
! 1425: if( argc>2 ) goto usage;
! 1426: if( argc==2 ){
! 1427: zTest = argv[1];
! 1428: nTest = strlen(zTest);
! 1429: if( zTest[nTest-1]=='*' ){
! 1430: nTest--;
! 1431: bPrefix = 1;
! 1432: }
! 1433: }
! 1434:
! 1435: sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
! 1436:
! 1437: for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
! 1438: char const *z = aTest[i].zTest;
! 1439: int n = strlen(z);
! 1440: if( !zTest || ((bPrefix || n==nTest) && 0==strncmp(zTest, z, nTest)) ){
! 1441: printf("Running %s for %d seconds...\n", z, aTest[i].nMs/1000);
! 1442: aTest[i].xTest(aTest[i].nMs);
! 1443: bTestfound++;
! 1444: }
! 1445: }
! 1446: if( bTestfound==0 ) goto usage;
! 1447:
! 1448: printf("Total of %d errors across all tests\n", nGlobalErr);
! 1449: return (nGlobalErr>0 ? 255 : 0);
! 1450:
! 1451: usage:
! 1452: printf("Usage: %s [testname|testprefix*]\n", argv[0]);
! 1453: printf("Available tests are:\n");
! 1454: for(i=0; i<sizeof(aTest)/sizeof(aTest[0]); i++){
! 1455: printf(" %s\n", aTest[i].zTest);
! 1456: }
! 1457:
! 1458: return 254;
! 1459: }
! 1460:
! 1461:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>