]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the initial version of the "dbselftest" utility program.
authordrh <drh@noemail.net>
Tue, 7 Feb 2017 17:36:12 +0000 (17:36 +0000)
committerdrh <drh@noemail.net>
Tue, 7 Feb 2017 17:36:12 +0000 (17:36 +0000)
FossilOrigin-Name: 1fcac8365e0f7bcfd55442d718da6626c864d45a

Makefile.in
main.mk
manifest
manifest.uuid
test/dbselftest.c [new file with mode: 0644]

index d5fa831e6b6a8f34f708cfd5ef648b60b9bcae00..740b49c08f1baaed1596927f07eccd9b5421fc5d 100644 (file)
@@ -1185,6 +1185,11 @@ KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
 kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
        $(LTLINK) $(KV_OPT) -o $@ $(TOP)/test/kvtest.c sqlite3.c $(TLIBS)
 
+DBSELFTEST_OPT += -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4
+
+dbselftest$(TEXE):     $(TOP)/test/dbselftest.c sqlite3.c
+       $(LTLINK) $(DBSELFTEST_OPT) -o $@ $(TOP)/test/dbselftest.c sqlite3.c $(TLIBS)
+
 rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.lo 
        $(LTLINK) -I. -o $@ $(TOP)/ext/rbu/rbu.c sqlite3.lo $(TLIBS)
 
diff --git a/main.mk b/main.mk
index 17e6468e1031f313134ea4dcace03f4a574fbb7c..85ed76a143c146c46b33e93a22907bb4235ea4b1 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -479,6 +479,8 @@ FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
 FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
 DBFUZZ_OPT =
 KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
+DBSELFTEST_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
+DBSELFTEST_OPT += -DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
 ST_OPT = -DSQLITE_THREADSAFE=0
 
 # This is the default Makefile target.  The objects listed here
@@ -898,7 +900,10 @@ speedtest1$(EXE):  $(TOP)/test/speedtest1.c sqlite3.c
        $(TCCX) -I. $(ST_OPT) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.c $(THREADLIB) 
 
 kvtest$(EXE):  $(TOP)/test/kvtest.c sqlite3.c
-       $(TCCX) -I. $(KV+OPT) -o kvtest$(EXE) $(TOP)/test/kvtest.c sqlite3.c $(THREADLIB) 
+       $(TCCX) -I. $(KV_OPT) -o kvtest$(EXE) $(TOP)/test/kvtest.c sqlite3.c $(THREADLIB) 
+
+dbselftest$(EXE):      $(TOP)/test/dbselftest.c sqlite3.c
+       $(TCCX) -I. $(DBSELFTEST_OPT) -o dbselftest$(EXE) $(TOP)/test/dbselftest.c sqlite3.c $(THREADLIB)
 
 rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.o 
        $(TCC) -I. -o rbu$(EXE) $(TOP)/ext/rbu/rbu.c sqlite3.o \
index 0378ad6c62498215d0fbe8fca4b4686b3e7c9cb1..2432d3968f4a449d8dd1c1543de6468dff0b7ab6 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
-C Update\san\serror\smessage\sin\scorruptC.test.
-D 2017-02-07T16:15:48.783
-F Makefile.in 5f415e7867296d678fed2e6779aea10c1318b4bc
+C Add\sthe\sinitial\sversion\sof\sthe\s"dbselftest"\sutility\sprogram.
+D 2017-02-07T17:36:12.948
+F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc ba953c8921fc7e18333f61898007206de7e23964
 F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
@@ -318,7 +318,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk afc52937b4e5fe08678e8d5a4fe4487d44e3bc61
+F main.mk 08e0b151abb46353c677ac4974b50c6077f85eee
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -636,6 +636,7 @@ F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
 F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
 F test/date.test a6a5a48b90907bca9fbcc79a30be5a715c1ab2fc
 F test/dbfuzz.c 8cc2bdb818b4483a052f9f80f96be74cbd9a6e1d
+F test/dbselftest.c eeb95d09932b4dc79e5886bd8cf5d812e7d28d94
 F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
 F test/dbstatus2.test e93ab03bfae6d62d4d935f20de928c19ca0ed0ab
 F test/default.test 0cb49b1c315a0d81c81d775e407f66906a2a604d
@@ -1554,7 +1555,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P bb7f445ba1df53cd4a169612b18fc533016102b7
-R 1228beaef5172fbebdd4d0250089ef1e
-U dan
-Z cdab5e1aea1876be0fe4dd420440969d
+P 722e57fe61162a82b8001c7b1d3e06174caa90b9
+R 9f073837e9cdb1ff055e4a2a2994d017
+U drh
+Z f48b3d2fea77878a00660f4925462068
index 9aa189d3e7f957331bdf215f2266c47786066f92..e2570868a3804c5dc85236f724170f7edc027cd0 100644 (file)
@@ -1 +1 @@
-722e57fe61162a82b8001c7b1d3e06174caa90b9
\ No newline at end of file
+1fcac8365e0f7bcfd55442d718da6626c864d45a
\ No newline at end of file
diff --git a/test/dbselftest.c b/test/dbselftest.c
new file mode 100644 (file)
index 0000000..1c6f8d8
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+** 2017-02-07
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This program implements an SQLite database self-verification utility.
+** Usage:
+** 
+**       dbselftest DATABASE
+**
+** This program reads the "selftest" table in DATABASE, in rowid order,
+** and runs each of the tests described there, reporting results at the
+** end.
+**
+** The intent of this program is to have a set of test database files that
+** can be run using future versions of SQLite in order to verify that
+** legacy database files continue to be readable.  In other words, the
+** intent is to confirm that there have been no breaking changes in the 
+** file format.  The program can also be used to verify that database files
+** are fully compatible between different architectures.
+**
+** The selftest table looks like this:
+**
+**     CREATE TABLE selftest (
+**       id INTEGER PRIMARY KEY,    -- Run tests in ascending order
+**       op TEXT,                   -- "test", "regexp", "print", etc.
+**       cmdtxt TEXT,               -- Usually the SQL to be run
+**       expected TEXT              -- Expected results
+**     );
+**
+*/
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sqlite3.h"
+
+static const char zHelp[] =
+  "Usage: dbselftest [OPTIONS] DBFILE\n"
+  "\n"
+  "    --init         Create the selftest table\n"
+  "    -q             Suppress most output.  Errors only\n"
+  "    -v             Show extra output\n"
+;
+
+
+/******************************************************************************
+** The following code from ext/misc/sha1.c
+**
+** Context for the SHA1 hash 
+*/
+typedef struct SHA1Context SHA1Context;
+struct SHA1Context {
+  unsigned int state[5];
+  unsigned int count[2];
+  unsigned char buffer[64];
+};
+
+
+#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
+/*
+ * GCC by itself only generates left rotates.  Use right rotates if
+ * possible to be kinder to dinky implementations with iterative rotate
+ * instructions.
+ */
+#define SHA_ROT(op, x, k) \
+        ({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
+#define rol(x,k) SHA_ROT("roll", x, k)
+#define ror(x,k) SHA_ROT("rorl", x, k)
+
+#else
+/* Generic C equivalent */
+#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
+#define rol(x,k) SHA_ROT(x,k,32-(k))
+#define ror(x,k) SHA_ROT(x,32-(k),k)
+#endif
+
+
+#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \
+    |(rol(block[i],8)&0x00FF00FF))
+#define blk0be(i) block[i]
+#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
+    ^block[(i+2)&15]^block[i&15],1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ *
+ * Rl0() for little-endian and Rb0() for big-endian.  Endianness is
+ * determined at run-time.
+ */
+#define Rl0(v,w,x,y,z,i) \
+    z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
+#define Rb0(v,w,x,y,z,i) \
+    z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
+#define R1(v,w,x,y,z,i) \
+    z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
+#define R2(v,w,x,y,z,i) \
+    z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
+#define R3(v,w,x,y,z,i) \
+    z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
+#define R4(v,w,x,y,z,i) \
+    z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){
+  unsigned int qq[5]; /* a, b, c, d, e; */
+  static int one = 1;
+  unsigned int block[16];
+  memcpy(block, buffer, 64);
+  memcpy(qq,state,5*sizeof(unsigned int));
+
+#define a qq[0]
+#define b qq[1]
+#define c qq[2]
+#define d qq[3]
+#define e qq[4]
+
+  /* Copy p->state[] to working vars */
+  /*
+  a = state[0];
+  b = state[1];
+  c = state[2];
+  d = state[3];
+  e = state[4];
+  */
+
+  /* 4 rounds of 20 operations each. Loop unrolled. */
+  if( 1 == *(unsigned char*)&one ){
+    Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
+    Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
+    Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
+    Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
+  }else{
+    Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
+    Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
+    Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
+    Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
+  }
+  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+  /* Add the working vars back into context.state[] */
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+  state[4] += e;
+
+#undef a
+#undef b
+#undef c
+#undef d
+#undef e
+}
+
+
+/* Initialize a SHA1 context */
+static void hash_init(SHA1Context *p){
+  /* SHA1 initialization constants */
+  p->state[0] = 0x67452301;
+  p->state[1] = 0xEFCDAB89;
+  p->state[2] = 0x98BADCFE;
+  p->state[3] = 0x10325476;
+  p->state[4] = 0xC3D2E1F0;
+  p->count[0] = p->count[1] = 0;
+}
+
+/* Add new content to the SHA1 hash */
+static void hash_step(
+  SHA1Context *p,                 /* Add content to this context */
+  const unsigned char *data,      /* Data to be added */
+  unsigned int len                /* Number of bytes in data */
+){
+  unsigned int i, j;
+
+  j = p->count[0];
+  if( (p->count[0] += len << 3) < j ){
+    p->count[1] += (len>>29)+1;
+  }
+  j = (j >> 3) & 63;
+  if( (j + len) > 63 ){
+    (void)memcpy(&p->buffer[j], data, (i = 64-j));
+    SHA1Transform(p->state, p->buffer);
+    for(; i + 63 < len; i += 64){
+      SHA1Transform(p->state, &data[i]);
+    }
+    j = 0;
+  }else{
+    i = 0;
+  }
+  (void)memcpy(&p->buffer[j], &data[i], len - i);
+}
+
+/* Compute a string using sqlite3_vsnprintf() and hash it */
+static void hash_step_vformat(
+  SHA1Context *p,                 /* Add content to this context */
+  const char *zFormat,
+  ...
+){
+  va_list ap;
+  int n;
+  char zBuf[50];
+  va_start(ap, zFormat);
+  sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap);
+  va_end(ap);
+  n = (int)strlen(zBuf);
+  hash_step(p, (unsigned char*)zBuf, n);
+}
+
+
+/* Add padding and compute the message digest.  Render the
+** message digest as lower-case hexadecimal and put it into
+** zOut[].  zOut[] must be at least 41 bytes long. */
+static void hash_finish(
+  SHA1Context *p,           /* The SHA1 context to finish and render */
+  char *zOut                /* Store hexadecimal hash here */
+){
+  unsigned int i;
+  unsigned char finalcount[8];
+  unsigned char digest[20];
+  static const char zEncode[] = "0123456789abcdef";
+
+  for (i = 0; i < 8; i++){
+    finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)]
+       >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+  }
+  hash_step(p, (const unsigned char *)"\200", 1);
+  while ((p->count[0] & 504) != 448){
+    hash_step(p, (const unsigned char *)"\0", 1);
+  }
+  hash_step(p, finalcount, 8);  /* Should cause a SHA1Transform() */
+  for (i = 0; i < 20; i++){
+    digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+  }
+  for(i=0; i<20; i++){
+    zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
+    zOut[i*2+1] = zEncode[digest[i] & 0xf];
+  }
+  zOut[i*2]= 0;
+}
+
+/*
+** Implementation of the sha1(X) function.
+**
+** Return a lower-case hexadecimal rendering of the SHA1 hash of the
+** argument X.  If X is a BLOB, it is hashed as is.  For all other
+** types of input, X is converted into a UTF-8 string and the string
+** is hash without the trailing 0x00 terminator.  The hash of a NULL
+** value is NULL.
+*/
+static void sha1Func(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  SHA1Context cx;
+  int eType = sqlite3_value_type(argv[0]);
+  int nByte = sqlite3_value_bytes(argv[0]);
+  char zOut[44];
+
+  assert( argc==1 );
+  if( eType==SQLITE_NULL ) return;
+  hash_init(&cx);
+  if( eType==SQLITE_BLOB ){
+    hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
+  }else{
+    hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
+  }
+  hash_finish(&cx, zOut);
+  sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
+}
+
+/*
+** Implementation of the sha1_query(SQL) function.
+**
+** This function compiles and runs the SQL statement(s) given in the
+** argument. The results are hashed using SHA1 and that hash is returned.
+**
+** The original SQL text is included as part of the hash.
+**
+** The hash is not just a concatenation of the outputs.  Each query
+** is delimited and each row and value within the query is delimited,
+** with all values being marked with their datatypes.
+*/
+static void sha1QueryFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  sqlite3 *db = sqlite3_context_db_handle(context);
+  const char *zSql = (const char*)sqlite3_value_text(argv[0]);
+  sqlite3_stmt *pStmt = 0;
+  int nCol;                   /* Number of columns in the result set */
+  int i;                      /* Loop counter */
+  int rc;
+  int n;
+  const char *z;
+  SHA1Context cx;
+  char zOut[44];
+
+  assert( argc==1 );
+  if( zSql==0 ) return;
+  hash_init(&cx);
+  while( zSql[0] ){
+    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
+    if( rc ){
+      char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s",
+                                   zSql, sqlite3_errmsg(db));
+      sqlite3_finalize(pStmt);
+      sqlite3_result_error(context, zMsg, -1);
+      sqlite3_free(zMsg);
+      return;
+    }
+    if( !sqlite3_stmt_readonly(pStmt) ){
+      char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt));
+      sqlite3_finalize(pStmt);
+      sqlite3_result_error(context, zMsg, -1);
+      sqlite3_free(zMsg);
+      return;
+    }
+    nCol = sqlite3_column_count(pStmt);
+    z = sqlite3_sql(pStmt);
+    n = (int)strlen(z);
+    hash_step_vformat(&cx,"S%d:",n);
+    hash_step(&cx,(unsigned char*)z,n);
+
+    /* Compute a hash over the result of the query */
+    while( SQLITE_ROW==sqlite3_step(pStmt) ){
+      hash_step(&cx,(const unsigned char*)"R",1);
+      for(i=0; i<nCol; i++){
+        switch( sqlite3_column_type(pStmt,i) ){
+          case SQLITE_NULL: {
+            hash_step(&cx, (const unsigned char*)"N",1);
+            break;
+          }
+          case SQLITE_INTEGER: {
+            sqlite3_uint64 u;
+            int j;
+            unsigned char x[9];
+            sqlite3_int64 v = sqlite3_column_int64(pStmt,i);
+            memcpy(&u, &v, 8);
+            for(j=8; j>=1; j--){
+              x[j] = u & 0xff;
+              u >>= 8;
+            }
+            x[0] = 'I';
+            hash_step(&cx, x, 9);
+            break;
+          }
+          case SQLITE_FLOAT: {
+            sqlite3_uint64 u;
+            int j;
+            unsigned char x[9];
+            double r = sqlite3_column_double(pStmt,i);
+            memcpy(&u, &r, 8);
+            for(j=8; j>=1; j--){
+              x[j] = u & 0xff;
+              u >>= 8;
+            }
+            x[0] = 'F';
+            hash_step(&cx,x,9);
+            break;
+          }
+          case SQLITE_TEXT: {
+            int n2 = sqlite3_column_bytes(pStmt, i);
+            const unsigned char *z2 = sqlite3_column_text(pStmt, i);
+            hash_step_vformat(&cx,"T%d:",n2);
+            hash_step(&cx, z2, n2);
+            break;
+          }
+          case SQLITE_BLOB: {
+            int n2 = sqlite3_column_bytes(pStmt, i);
+            const unsigned char *z2 = sqlite3_column_blob(pStmt, i);
+            hash_step_vformat(&cx,"B%d:",n2);
+            hash_step(&cx, z2, n2);
+            break;
+          }
+        }
+      }
+    }
+    sqlite3_finalize(pStmt);
+  }
+  hash_finish(&cx, zOut);
+  sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
+}
+/* End of ext/misc/sha1.c
+******************************************************************************/
+
+/* How much output to display */
+#define VOLUME_MIN          0
+#define VOLUME_OFF          0
+#define VOLUME_ERROR_ONLY   1
+#define VOLUME_LOW          2
+#define VOLUME_ECHO         3
+#define VOLUME_VERBOSE      4
+#define VOLUME_MAX          4
+
+/* A string accumulator
+*/
+typedef struct Str {
+  char *z;             /* Accumulated text */
+  int n;               /* Bytes of z[] used so far */
+  int nAlloc;          /* Bytes allocated for z[] */
+} Str;
+
+/* Append text to the Str object
+*/
+static void strAppend(Str *p, const char *z){
+  int n = (int)strlen(z);
+  if( p->n+n >= p->nAlloc ){
+    p->nAlloc += p->n+n + 100;
+    p->z = sqlite3_realloc(p->z, p->nAlloc);
+    if( z==0 ){
+      printf("Could not allocate %d bytes\n", p->nAlloc);
+      exit(1);
+    }
+  }
+  memcpy(p->z+n, z, n+1);
+  p->n += n;
+}
+
+/* This is an sqlite3_exec() callback that will capture all
+** output in a Str.
+**
+** Columns are separated by ",".  Rows are separated by "|".
+*/
+static int execCallback(void *pStr, int argc, char **argv, char **colv){
+  int i;
+  Str *p = (Str*)pStr;
+  if( p->n ) strAppend(p, "|");
+  for(i=0; i<argc; i++){
+    const char *z = (const char*)argv[i];
+    if( z==0 ) z = "NULL";
+    if( i>0 ) strAppend(p, ",");
+    strAppend(p, z);
+  }
+  return 0;
+}
+
+int main(int argc, char **argv){
+  int eVolume = VOLUME_LOW;    /* How much output to display */
+  const char *zDb = 0;         /* Name of the database file */
+  int doInit = 0;              /* True if --init is present */
+  sqlite3 *db = 0;             /* Open database connection */
+  int rc;                      /* Return code from API calls */
+  char *zErrMsg = 0;           /* An error message return */
+  char **azTest;               /* Content of the selftest table */
+  int nRow = 0, nCol = 0;      /* Rows and columns in azTest[] */
+  int i;                       /* Loop counter */
+  int nErr = 0;                /* Number of errors */
+  Str str;                     /* Result accumulator */
+
+  for(i=1; i<argc; i++){
+    const char *z = argv[i];
+    if( z[0]=='-' ){
+      if( z[1]=='-' ) z++;
+      if( strcmp(z, "-help")==0 ){
+        printf("%s", zHelp);
+        return 0;
+      }else
+      if( strcmp(z, "-init")==0 ){
+        doInit = 1;
+      }else
+      if( strcmp(z, "-a")==0 ){
+        if( eVolume>VOLUME_MIN) eVolume--;
+      }else
+      if( strcmp(z, "-v")==0 ){
+        if( eVolume<VOLUME_MAX) eVolume++;
+      }else
+      {
+        printf("unknown option: \"%s\"\nUse --help for more information\n",
+               argv[i]);
+        return 1;
+      }
+    }else if( zDb!=0 ){
+      printf("More than one database specified.  Use --help for more info.\n");
+      return 1;
+    }else{
+      zDb = argv[i];
+    }
+  }
+  if( zDb==0 ){
+    printf("No database specified.  Use --help for more info\n");
+    return 1;
+  }
+  rc = sqlite3_open(zDb, &db);
+  if( rc ){
+    printf("Cannot open \"%s\": %s\n", zDb, sqlite3_errmsg(db));
+    return 1;
+  }
+  rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0,
+                               sha1Func, 0, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0,
+                                 sha1QueryFunc, 0, 0);
+  }
+  if( rc ){
+    printf("Initialization error: %s\n", sqlite3_errmsg(db));
+    sqlite3_close(db);
+    return 1;
+  }
+  if( doInit ){
+    rc = sqlite3_exec(db,
+      "CREATE TABLE IF NOT EXISTS selftest(\n"
+      "  tno INTEGER PRIMARY KEY,  -- test number\n"
+      "  op TEXT,                  -- what kind of test\n"
+      "  sql TEXT,                 -- SQL text for the test\n"
+      "  ans TEXT                  -- expected answer\n"
+      ");", 0, 0, &zErrMsg);
+    if( rc || zErrMsg ){
+      printf("CREATE TABLE selftest failed: %s\n", zErrMsg);
+      sqlite3_close(db);
+      return 1;
+    }
+  }
+  if( sqlite3_table_column_metadata(db,"main","selftest","sql",0,0,0,0,0) ){
+    printf("No such table: selftest\nSee the --init option.\n");
+    return 1;
+  }
+  rc = sqlite3_get_table(db, 
+      "SELECT tno,op,sql,ans FROM selftest ORDRE BY tno",
+      &azTest, &nRow, &nCol, &zErrMsg);
+  if( rc || zErrMsg ){
+    printf("Error querying selftest: %s\n", zErrMsg);
+    return 1;
+  }
+  if( eVolume>=VOLUME_LOW ){
+    printf("SQLite %s\n", sqlite3_sourceid());
+  }
+  memset(&str, 0, sizeof(str));
+  strAppend(&str, "\n");
+  for(i=1; i<=nRow; i++){
+    int tno = atoi(azTest[i*nCol]);
+    const char *zOp = azTest[i*nCol+1];
+    const char *zSql = azTest[i*nCol+2];
+    const char *zAns = azTest[i*nCol+3];
+
+    if( eVolume>=VOLUME_ECHO ){
+      char *zQuote = sqlite3_mprintf("%q", zSql);
+      printf("%d: %s %s\n", tno, zOp, zSql);
+      sqlite3_free(zQuote);
+    }
+    if( strcmp(zOp,"memo")==0 ){
+      if( eVolume>=VOLUME_LOW ){
+        printf("%s\n", zSql);
+      }
+    }else
+    if( strcmp(zOp,"run")==0 ){
+      str.n = 0;
+      str.z[0] = 0;
+      zErrMsg = 0;
+      rc = sqlite3_exec(db, zSql, execCallback, &str, &zErrMsg);
+      if( eVolume>=VOLUME_VERBOSE ){
+        printf("Result: %s\n", str.z);
+      }
+      if( rc || zErrMsg ){
+        nErr++;
+        if( eVolume>=VOLUME_ERROR_ONLY ){
+          printf("%d: error-code-%d: %s\n", tno, rc, zErrMsg);
+        }
+        sqlite3_free(zErrMsg);
+      }else if( strcmp(zAns,str.z)!=0 ){
+        nErr++;
+        if( eVolume>=VOLUME_ERROR_ONLY ){
+          printf("%d: Expected: [%s]\n", tno, zAns);
+          printf("%d:      Got: [%s]\n", tno, str.z);
+        }
+      }
+    }else
+    {
+      printf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
+      return 1;
+    }
+  }
+  sqlite3_free_table(azTest);
+  sqlite3_close(db);
+  if( eVolume>=VOLUME_LOW || (nErr>0 && eVolume>=VOLUME_ERROR_ONLY) ){
+    printf("%d errors out of %d tests\n", nErr, nRow);
+  }
+  return nErr;
+}