sqllogictest
Check-in [96bff359ee]
Not logged in

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

Overview
Comment:Use a single -odbc CONNSTR command-line argument instead of -engine ODBC3 and -connection CONNSTR. Added hash-threshold and halt record types. Added the OMIT_ODBC compile-time option.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 96bff359eea2e4fbb48b92778f986db0df9bb5d4
User & Date: drh 2008-12-01 20:02:20
Context
2008-12-01
20:33
Add the select2 test. Similar to select1 but adds NULL values and omits the ORDER BY clauses. check-in: 37156697d4 user: drh tags: trunk
20:02
Use a single -odbc CONNSTR command-line argument instead of -engine ODBC3 and -connection CONNSTR. Added hash-threshold and halt record types. Added the OMIT_ODBC compile-time option. check-in: 96bff359ee user: drh tags: trunk
14:42
Modified select1.* scripts to work with MS SQL (namely, removing OFFSET/LIMIT clauses); Fixed handle leak in slt_odbc3.c. check-in: 5c63a5e855 user: shaneh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to about.wiki.

    97     97   <h2>Test-Script Format</h2>
    98     98   
    99     99   Test scripts are line-oriented ASCII text files.  
   100    100   No provision is made for Unicode; the purpose
   101    101   of sqllogictest is to test the query and join logic of the database engine, not
   102    102   its support for localization and internationalization.
   103    103   
   104         -Test scripts consist of zero or more records.  A record represents either a
   105         -single statement or query.  Each record
          104  +Test scripts consist of zero or more records.  A record is a
          105  +single statement or query or a control record.  Each record
   106    106   is separated from its neighbors by one or more blank line.  Records
   107    107   are evaluated in order, starting from the beginning of the
   108    108   script and working toward the end.
   109    109   
   110    110   Lines of the test script that begin with the sharp character
   111    111   ("#", ASCII code 35)
   112    112   are comment lines and are ignored.  Comment lines are not considered blank
................................................................................
   115    115   allowed to occur in the middle of a record.  Comments
   116    116   that occurs in the middle of an SQL statement are stripped from the
   117    117   statement prior to the statement being sent to the database engine for
   118    118   evaluation.  Comments are logically removed from the script by a preprocessor.
   119    119   Hence, when we speak of the "first line of a record" we
   120    120   really mean the "first non-comment line of a record".
   121    121   
   122         -Each record is either a statement or a query.  A statement is an SQL
          122  +Most records are either a statement or a query.  A statement is an SQL
   123    123   command that is to be evaluated but from which we do not expect to get
   124    124   results (other than success or failure).  A statement might be a
   125    125   CREATE TABLE or an INSERT or an UPDATE or a DROP INDEX.  A query is an
   126    126   SQL command from which we expect to receive results.  The result set
   127    127   might be empty.
   128    128   
   129    129   A statement record begins with one of the following two lines:
................................................................................
   182    182   
   183    183   In the results section, integer values are rendered as if by
   184    184   printf("%d").  Floating point values are rendered as if by
   185    185   printf("%.3f").  NULL values are rendered as "NULL".  Empty
   186    186   strings are rendered as "(empty)".  Within non-empty strings,
   187    187   all control characters and unprintable characters are rendered as "@".
   188    188   
          189  +<h3>Control Records</h3>
          190  +
          191  +The test script might also contain control records.  A control record is
          192  +one of the following:
          193  +
          194  +<blockquote>
          195  +<tt>halt</tt><br>
          196  +<tt>hash-threshold</td> &lt;max-result-set-size&gt;
          197  +</blockquote>
          198  +
          199  +A "halt" record is intended for debugging use only.  A halt record merely
          200  +causes sqllogictest to ignore the rest of the test script.  A halt record
          201  +can be inserted after a query that is giving an anomalous result, causing
          202  +the database to be left in the state where it gives the unexpected answer.
          203  +After sqllogictest exist, manually debugging can then proceed.
          204  +
          205  +The "hash-threshold" record sets a limit on the number of values that can
          206  +appear in a result set.  If the number of values exceeds this, then instead
          207  +of recording each individual value in the full test script, an MD5 hash of
          208  +all values is computed in stored.  This makes the full test scripts much
          209  +shorter, but at the cost of obscuring the results.  If the hash-threshold
          210  +is 0, then results are never hashed.  A hash-threshold of 10 or 20 is
          211  +recommended.  During debugging, it is advantage to set the hash-threshold
          212  +to zero so that all results can be seen.
          213  +
   189    214   <h2>Suggestions For Generating Test-Scripts</h2>
   190    215   
   191    216   When sqllogictest runs a test script, it begins with a completely
   192    217   empty database.  So the first few records of any test script will
   193    218   typically be CREATE statements of various kinds and expecially
   194    219   CREATE TABLE statements.  In order to maximize the portability of
   195    220   scripts across database engines, it is suggested that test scripts stick 
................................................................................
   224    249   
   225    250     *  Generate a random WHERE clause.
   226    251   
   227    252     *  Generate a random string literal of some maximum length.
   228    253   
   229    254     *  Generate a random identifier which is not a keyword.
   230    255   
   231         -Use LIMIT and OFFSET clauses to keep results set sizes under control.
   232         -It is recommented that most queries use an ORDER BY clause so that
   233         -the order of values in the output is deterministic.  Of course, it
   234         -is also desirable to have some test cases that omit ORDER BY.  In those
   235         -cases use either the "rowsort" or "valuesort" modifiers at the beginning
          256  +Segregate queries that use LIMIT and OFFSET into separate test scripts
          257  +which are only run on database engines that support LIMIT and OFFSET.
          258  +
          259  +All queries should use either an ORDER BY clause so that
          260  +the order of values in the output is deterministic, or else
          261  +the "rowsort" or "valuesort" modifiers at the beginning
   236    262   of the query record to ensure that the output appears in the same order
   237    263   on all database engines.
   238    264   
   239    265   A typical test script will begin with some CREATE statements followed by
   240    266   some INSERT statements to add initial data.  This is followed by
   241    267   thousands of randomly generate UPDATE, DELETE, and INSERT statements.
   242    268   Several SELECT statements typical follow each UPDATE, DELETE, or INSERT

Changes to src/Makefile.

    14     14   #
    15     15   LIB = $(LDFLAGS)
    16     16   
    17     17   # You should not need to change anything below this line
    18     18   ###############################################################################
    19     19   #
    20     20   OBJ = \
           21  +  md5.o \
    21     22     sqlite3.o
    22     23   
    23     24   sqllogictest$(E):	sqllogictest.c sqllogictest.h $(OBJ)
    24     25   	$(CC) -o sqllogictest$(E) sqllogictest.c $(OBJ)
    25     26   
           27  +md5.o:	md5.c
           28  +	$(CC) -c md5.c
           29  +
    26     30   sqlite3.o:	sqlite3.c sqlite3.h
    27     31   	$(CC) -c sqlite3.c -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
    28     32   
    29     33   clean:
    30     34   	rm -f $(OBJ)

Added src/md5.c.

            1  +/*
            2  +** This code taken from the SQLite test library.  Originally found on
            3  +** the internet.  The original header comment follows this comment.
            4  +** The code is largerly unchanged, but there have been some modifications.
            5  +*/
            6  +/*
            7  + * This code implements the MD5 message-digest algorithm.
            8  + * The algorithm is due to Ron Rivest.  This code was
            9  + * written by Colin Plumb in 1993, no copyright is claimed.
           10  + * This code is in the public domain; do with it what you wish.
           11  + *
           12  + * Equivalent code is available from RSA Data Security, Inc.
           13  + * This code has been tested against that, and is equivalent,
           14  + * except that you don't need to include two pages of legalese
           15  + * with every copy.
           16  + *
           17  + * To compute the message digest of a chunk of bytes, declare an
           18  + * MD5Context structure, pass it to MD5Init, call MD5Update as
           19  + * needed on buffers full of bytes, and then call MD5Final, which
           20  + * will fill a supplied 16-byte array with the digest.
           21  + */
           22  +#include <string.h>
           23  +
           24  +/*
           25  + * If compiled on a machine that doesn't have a 32-bit integer,
           26  + * you just set "uint32" to the appropriate datatype for an
           27  + * unsigned 32-bit integer.  For example:
           28  + *
           29  + *       cc -Duint32='unsigned long' md5.c
           30  + *
           31  + */
           32  +#ifndef uint32
           33  +#  define uint32 unsigned int
           34  +#endif
           35  +
           36  +struct Context {
           37  +  int isInit;
           38  +  uint32 buf[4];
           39  +  uint32 bits[2];
           40  +  unsigned char in[64];
           41  +};
           42  +typedef struct Context MD5Context;
           43  +
           44  +/*
           45  + * Note: this code is harmless on little-endian machines.
           46  + */
           47  +static void byteReverse (unsigned char *buf, unsigned longs){
           48  +        uint32 t;
           49  +        do {
           50  +                t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
           51  +                            ((unsigned)buf[1]<<8 | buf[0]);
           52  +                *(uint32 *)buf = t;
           53  +                buf += 4;
           54  +        } while (--longs);
           55  +}
           56  +/* The four core functions - F1 is optimized somewhat */
           57  +
           58  +/* #define F1(x, y, z) (x & y | ~x & z) */
           59  +#define F1(x, y, z) (z ^ (x & (y ^ z)))
           60  +#define F2(x, y, z) F1(z, x, y)
           61  +#define F3(x, y, z) (x ^ y ^ z)
           62  +#define F4(x, y, z) (y ^ (x | ~z))
           63  +
           64  +/* This is the central step in the MD5 algorithm. */
           65  +#define MD5STEP(f, w, x, y, z, data, s) \
           66  +        ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
           67  +
           68  +/*
           69  + * The core of the MD5 algorithm, this alters an existing MD5 hash to
           70  + * reflect the addition of 16 longwords of new data.  MD5Update blocks
           71  + * the data and converts bytes into longwords for this routine.
           72  + */
           73  +static void MD5Transform(uint32 buf[4], const uint32 in[16]){
           74  +        register uint32 a, b, c, d;
           75  +
           76  +        a = buf[0];
           77  +        b = buf[1];
           78  +        c = buf[2];
           79  +        d = buf[3];
           80  +
           81  +        MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478,  7);
           82  +        MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
           83  +        MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
           84  +        MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
           85  +        MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf,  7);
           86  +        MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
           87  +        MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
           88  +        MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
           89  +        MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8,  7);
           90  +        MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
           91  +        MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
           92  +        MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
           93  +        MD5STEP(F1, a, b, c, d, in[12]+0x6b901122,  7);
           94  +        MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
           95  +        MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
           96  +        MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
           97  +
           98  +        MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562,  5);
           99  +        MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340,  9);
          100  +        MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
          101  +        MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
          102  +        MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d,  5);
          103  +        MD5STEP(F2, d, a, b, c, in[10]+0x02441453,  9);
          104  +        MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
          105  +        MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
          106  +        MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6,  5);
          107  +        MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6,  9);
          108  +        MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
          109  +        MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
          110  +        MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905,  5);
          111  +        MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8,  9);
          112  +        MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
          113  +        MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
          114  +
          115  +        MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942,  4);
          116  +        MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
          117  +        MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
          118  +        MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
          119  +        MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44,  4);
          120  +        MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
          121  +        MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
          122  +        MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
          123  +        MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6,  4);
          124  +        MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
          125  +        MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
          126  +        MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
          127  +        MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039,  4);
          128  +        MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
          129  +        MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
          130  +        MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
          131  +
          132  +        MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244,  6);
          133  +        MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
          134  +        MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
          135  +        MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
          136  +        MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3,  6);
          137  +        MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
          138  +        MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
          139  +        MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
          140  +        MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f,  6);
          141  +        MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
          142  +        MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
          143  +        MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
          144  +        MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82,  6);
          145  +        MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
          146  +        MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
          147  +        MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
          148  +
          149  +        buf[0] += a;
          150  +        buf[1] += b;
          151  +        buf[2] += c;
          152  +        buf[3] += d;
          153  +}
          154  +
          155  +/*
          156  + * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
          157  + * initialization constants.
          158  + */
          159  +static void MD5Init(MD5Context *ctx){
          160  +        ctx->isInit = 1;
          161  +        ctx->buf[0] = 0x67452301;
          162  +        ctx->buf[1] = 0xefcdab89;
          163  +        ctx->buf[2] = 0x98badcfe;
          164  +        ctx->buf[3] = 0x10325476;
          165  +        ctx->bits[0] = 0;
          166  +        ctx->bits[1] = 0;
          167  +}
          168  +
          169  +/*
          170  + * Update context to reflect the concatenation of another buffer full
          171  + * of bytes.
          172  + */
          173  +static 
          174  +void MD5Update(MD5Context *pCtx, const unsigned char *buf, unsigned int len){
          175  +        struct Context *ctx = (struct Context *)pCtx;
          176  +        uint32 t;
          177  +
          178  +        /* Update bitcount */
          179  +
          180  +        t = ctx->bits[0];
          181  +        if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
          182  +                ctx->bits[1]++; /* Carry from low to high */
          183  +        ctx->bits[1] += len >> 29;
          184  +
          185  +        t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
          186  +
          187  +        /* Handle any leading odd-sized chunks */
          188  +
          189  +        if ( t ) {
          190  +                unsigned char *p = (unsigned char *)ctx->in + t;
          191  +
          192  +                t = 64-t;
          193  +                if (len < t) {
          194  +                        memcpy(p, buf, len);
          195  +                        return;
          196  +                }
          197  +                memcpy(p, buf, t);
          198  +                byteReverse(ctx->in, 16);
          199  +                MD5Transform(ctx->buf, (uint32 *)ctx->in);
          200  +                buf += t;
          201  +                len -= t;
          202  +        }
          203  +
          204  +        /* Process data in 64-byte chunks */
          205  +
          206  +        while (len >= 64) {
          207  +                memcpy(ctx->in, buf, 64);
          208  +                byteReverse(ctx->in, 16);
          209  +                MD5Transform(ctx->buf, (uint32 *)ctx->in);
          210  +                buf += 64;
          211  +                len -= 64;
          212  +        }
          213  +
          214  +        /* Handle any remaining bytes of data. */
          215  +
          216  +        memcpy(ctx->in, buf, len);
          217  +}
          218  +
          219  +/*
          220  + * Final wrapup - pad to 64-byte boundary with the bit pattern 
          221  + * 1 0* (64-bit count of bits processed, MSB-first)
          222  + */
          223  +static void MD5Final(unsigned char digest[16], MD5Context *pCtx){
          224  +        struct Context *ctx = (struct Context *)pCtx;
          225  +        unsigned count;
          226  +        unsigned char *p;
          227  +
          228  +        /* Compute number of bytes mod 64 */
          229  +        count = (ctx->bits[0] >> 3) & 0x3F;
          230  +
          231  +        /* Set the first char of padding to 0x80.  This is safe since there is
          232  +           always at least one byte free */
          233  +        p = ctx->in + count;
          234  +        *p++ = 0x80;
          235  +
          236  +        /* Bytes of padding needed to make 64 bytes */
          237  +        count = 64 - 1 - count;
          238  +
          239  +        /* Pad out to 56 mod 64 */
          240  +        if (count < 8) {
          241  +                /* Two lots of padding:  Pad the first block to 64 bytes */
          242  +                memset(p, 0, count);
          243  +                byteReverse(ctx->in, 16);
          244  +                MD5Transform(ctx->buf, (uint32 *)ctx->in);
          245  +
          246  +                /* Now fill the next block with 56 bytes */
          247  +                memset(ctx->in, 0, 56);
          248  +        } else {
          249  +                /* Pad block to 56 bytes */
          250  +                memset(p, 0, count-8);
          251  +        }
          252  +        byteReverse(ctx->in, 14);
          253  +
          254  +        /* Append length in bits and transform */
          255  +        ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
          256  +        ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
          257  +
          258  +        MD5Transform(ctx->buf, (uint32 *)ctx->in);
          259  +        byteReverse((unsigned char *)ctx->buf, 4);
          260  +        memcpy(digest, ctx->buf, 16);
          261  +        memset(ctx, 0, sizeof(ctx));    /* In case it is sensitive */
          262  +}
          263  +
          264  +/*
          265  +** Convert a digest into base-16.  digest should be declared as
          266  +** "unsigned char digest[16]" in the calling function.  The MD5
          267  +** digest is stored in the first 16 bytes.  zBuf should
          268  +** be "char zBuf[33]".
          269  +*/
          270  +static void DigestToBase16(unsigned char *digest, char *zBuf){
          271  +  static char const zEncode[] = "0123456789abcdef";
          272  +  int i, j;
          273  +
          274  +  for(j=i=0; i<16; i++){
          275  +    int a = digest[i];
          276  +    zBuf[j++] = zEncode[(a>>4)&0xf];
          277  +    zBuf[j++] = zEncode[a & 0xf];
          278  +  }
          279  +  zBuf[j] = 0;
          280  +}
          281  +
          282  +/*
          283  +** Status of an MD5 hash.
          284  +*/
          285  +static MD5Context ctx;
          286  +static int isInit = 0;
          287  +static char zResult[34] = "";
          288  +
          289  +/*
          290  +** Add additional text to the current MD5 hash.
          291  +*/
          292  +void md5_add(const char *z){
          293  +  if( !isInit ){
          294  +    MD5Init(&ctx);
          295  +    isInit = 1;
          296  +  }
          297  +  MD5Update(&ctx, (unsigned char*)z, (unsigned)strlen(z));
          298  +}
          299  +
          300  +/*
          301  +** Compute the final signature.  Reset the hash generator in preparation
          302  +** for the next round.
          303  +*/
          304  +const char *md5_finish(void){
          305  +  if( isInit ){
          306  +    unsigned char digest[16];
          307  +    MD5Final(digest, &ctx);
          308  +    isInit = 0;
          309  +    DigestToBase16(digest, zResult);
          310  +  }
          311  +  return zResult;
          312  +}

Changes to src/slt_odbc3.c.

    28     28   ** On connect, it will attempt to "DROP" all existing tables 
    29     29   ** from the database name 'slt' to reset it to a known status.
    30     30   **
    31     31   ** The DSN name and DB name are controlled by the defines
    32     32   ** SLT_DSN and SLT_DB.
    33     33   **
    34     34   */
           35  +#ifndef OMIT_ODBC  /* Omit this module if OMIT_ODBC is defined */
           36  +
    35     37   #ifdef WIN32
    36     38   #include <windows.h>
    37     39   #endif
    38     40   #define SQL_NOUNICODEMAP
    39     41   #include <sql.h>
    40     42   #include <sqlext.h>
    41     43   
................................................................................
   523    525     };
   524    526     sqllogictestRegisterEngine(&ODBC3DbEngine);
   525    527   }
   526    528   
   527    529   /*
   528    530   **************** End of the ODBC3 database engine interface *****************
   529    531   *****************************************************************************/
          532  +#endif /* OMIT_ODBC */

Changes to src/sqllogictest.c.

   242    242   /*
   243    243   ** This is the main routine.  This routine runs first.  It processes
   244    244   ** command-line arguments then runs the test.
   245    245   */
   246    246   int main(int argc, char **argv){
   247    247     int verifyMode = 0;                  /* True if in -verify mode */
   248    248     const char *zScriptFile = 0;         /* Input script filename */
   249         -  const char *zDbEngine = NULL;        /* Name of database engine */
          249  +  const char *zDbEngine = "SQLite";    /* Name of database engine */
   250    250     const char *zConnection = 0;         /* Connection string on DB engine */
   251    251     const DbEngine *pEngine = 0;         /* Pointer to DbEngine object */
   252    252     int i;                               /* Loop counter */
   253    253     char *zScript;                       /* Content of the script */
   254    254     long nScript;                        /* Size of the script in bytes */
   255    255     void *pConn;                         /* Connection to the database engine */
   256    256     int rc;                              /* Result code from subroutine call */
   257    257     int nErr = 0;                        /* Number of errors */
   258    258     int nCmd = 0;                        /* Number of SQL statements processed */
   259    259     int nResult;                         /* Number of query results */
   260    260     char **azResult;                     /* Query result vector */
   261    261     Script sScript;                      /* Script parsing status */
   262    262     FILE *in;                            /* For reading script */
          263  +  int hashThreshold = 0;               /* Hash result if this long or longer */
   263    264     
   264    265   
   265    266     /* Add calls to the registration procedures for new database engine
   266    267     ** interfaces here
   267    268     */
   268    269     registerSqlite();
          270  +#ifndef OMIT_ODBC
   269    271     registerODBC3();
          272  +#endif
   270    273   
   271    274     /* Report an error if no registered engines
   272    275     */
   273    276     if( nEngine == 0 ){
   274    277       fprintf(stderr, "%s: no registered database engines\n", argv[0]);
   275    278       usage(argv[0]);
   276    279     }
................................................................................
   283    286   
   284    287     /* Scan the command-line and process arguments
   285    288     */
   286    289     for(i=1; i<argc; i++){
   287    290       int n = (int)strlen(argv[i]);
   288    291       if( strncmp(argv[i], "-verify",n)==0 ){
   289    292         verifyMode = 1;
          293  +#if 0  /* Obsolete code */
   290    294       }else if( strncmp(argv[i], "-engine",n)==0 ){
   291    295         zDbEngine = argv[++i];
   292    296       }else if( strncmp(argv[i], "-connection",n)==0 ){
   293    297         zConnection = argv[++i];
          298  +#endif
          299  +    }else if( strncmp(argv[i], "-odbc",n)==0 ){
          300  +      zDbEngine = "ODBC3";
          301  +      zConnection = argv[++i];
   294    302       }else if( zScriptFile==0 ){
   295    303         zScriptFile = argv[i];
   296    304       }else{
   297    305         fprintf(stderr, "%s: unknown argument: %s\n", argv[0], argv[i]);
   298    306         usage(argv[0]);
   299    307       }
   300    308     }
................................................................................
   462    470           nColumn = 1;
   463    471           qsort(azResult, nResult, sizeof(azResult[0]), rowCompare);
   464    472         }else{
   465    473           fprintf(stderr, "%s:%d: unknown sort method: '%s'\n",
   466    474                   zScriptFile, sScript.startLine, sScript.azToken[2]);
   467    475           nErr++;
   468    476         }
          477  +
          478  +      /* Hash the results if we are over the hash threshold */
          479  +      if( hashThreshold>0 && nResult>hashThreshold ){
          480  +        for(i=0; i<nResult; i++){
          481  +          md5_add(azResult[i]);
          482  +          md5_add("\n");
          483  +        }
          484  +      }
   469    485   
   470    486         if( verifyMode ){
   471    487           /* In verify mode, first skip over the ---- line if we are still
   472    488           ** pointing at it. */
   473    489           if( strcmp(sScript.zLine, "----")==0 ) nextLine(&sScript);
   474    490   
   475    491           /* Compare subsequent lines of the script against the results
   476    492           ** from the query.  Report an error if any differences are found.
   477    493           */
   478         -        for(i=0; i<nResult && sScript.zLine[0]; nextLine(&sScript), i++){
   479         -          if( strcmp(sScript.zLine, azResult[i])!=0 ){
   480         -            fprintf(stderr,"%s:%d: wrong result\n", zScriptFile,
   481         -                    sScript.nLine);
          494  +        if( hashThreshold==0 || nResult<=hashThreshold ){
          495  +          for(i=0; i<nResult && sScript.zLine[0]; nextLine(&sScript), i++){
          496  +            if( strcmp(sScript.zLine, azResult[i])!=0 ){
          497  +              fprintf(stderr,"%s:%d: wrong result\n", zScriptFile,
          498  +                      sScript.nLine);
          499  +              nErr++;
          500  +              break;
          501  +            }
          502  +          }
          503  +        }else{
          504  +          if( strcmp(sScript.zLine, md5_finish())!=0 ){
          505  +            fprintf(stderr, "%s:%d: wrong result hash\n",
          506  +                    zScriptFile, sScript.nLine);
   482    507               nErr++;
   483         -            break;
   484    508             }
   485    509           }
   486    510         }else{
   487    511           /* In completion mode, first make sure we have output an ---- line.
   488    512           ** Output such a line now if we have not already done so.
   489    513           */
   490    514           if( strcmp(sScript.zLine, "----")!=0 ){
   491    515             printf("----\n");
   492    516           }
   493    517   
   494    518           /* Output the results obtained by running the query
   495    519           */
   496         -        for(i=0; i<nResult; i++){
   497         -          printf("%s\n", azResult[i]);
          520  +        if( hashThreshold==0 || nResult<=hashThreshold ){
          521  +          for(i=0; i<nResult; i++){
          522  +            printf("%s\n", azResult[i]);
          523  +          }
          524  +        }else{
          525  +          printf("%s\n", md5_finish());
   498    526           }
   499    527           printf("\n");
   500    528   
   501    529           /* Skip over any existing results.  They will be ignored.
   502    530           */
   503    531           sScript.copyFlag = 0;
   504    532           while( sScript.zLine[0]!=0 && sScript.iCur<sScript.iEnd ){
................................................................................
   505    533             nextLine(&sScript);
   506    534           }
   507    535           sScript.copyFlag = 1;
   508    536         }
   509    537   
   510    538         /* Free the query results */
   511    539         pEngine->xFreeResults(pConn, azResult, nResult);
          540  +    }else if( strcmp(sScript.azToken[0],"hash-threshold")==0 ){
          541  +      /* Set the maximum number of result values that will be accepted
          542  +      ** for a query.  If the number of result values exceeds this number,
          543  +      ** then an MD5 hash is computed of all values, and the resulting hash
          544  +      ** is the only result.
          545  +      **
          546  +      ** If the threshold is 0, then hashing is never used.
          547  +      */
          548  +      hashThreshold = atoi(sScript.azToken[1]);
          549  +    }else if( strcmp(sScript.azToken[0],"halt")==0 ){
          550  +      /* Used for debugging.  Stop reading the test script and shut down.
          551  +      ** A "halt" record can be inserted in the middle of a test script in
          552  +      ** to run the script up to a particular point that is giving a
          553  +      ** faulty result, then terminate at that point for analysis.
          554  +      */
          555  +      fprintf(stderr, "%s:%d: halt\n", zScriptFile, sScript.startLine);
          556  +      break;
   512    557       }else{
   513    558         /* An unrecognized record type is an error */
   514    559         fprintf(stderr, "%s:%d: unknown record type: '%s'\n",
   515    560                 zScriptFile, sScript.startLine, sScript.azToken[0]);
   516    561         nErr++;
          562  +      break;
   517    563       }
   518    564     }
   519    565   
   520    566   
   521    567     /* Shutdown the database connection.
   522    568     */
   523    569     rc = pEngine->xDisconnect(pConn);

Changes to src/sqllogictest.h.

    42     42   };
    43     43   
    44     44   /*
    45     45   ** Each database engine interface invokes the following routine
    46     46   ** to register itself with the main sqllogictest driver.
    47     47   */
    48     48   void sqllogictestRegisterEngine(const DbEngine*);
           49  +
           50  +
           51  +/*
           52  +** MD5 hashing routines.
           53  +*/
           54  +void md5_add(const char *z);
           55  +const char *md5_finish(void);