FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
-FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c
+FUZZCHECK_SRC = $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c $(TOP)/test/fuzzinvariants.c
DBFUZZ_OPT =
# This is the default Makefile target. The objects listed here
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_DBSTAT_VTAB
FUZZCHECK_OPTS = $(FUZZCHECK_OPTS) -DSQLITE_ENABLE_BYTECODE_VTAB
-FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c
+FUZZCHECK_SRC = $(TOP)\test\fuzzcheck.c $(TOP)\test\ossfuzz.c $(TOP)\test\fuzzinvariants.c
OSSSHELL_SRC = $(TOP)\test\ossshell.c $(TOP)\test\ossfuzz.c
DBFUZZ_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
KV_COMPILE_OPTS = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
FUZZCHECK_OPT += -DSQLITE_ENABLE_GEOPOLY
FUZZCHECK_OPT += -DSQLITE_ENABLE_DBSTAT_VTAB
FUZZCHECK_OPT += -DSQLITE_ENABLE_BYTECODE_VTAB
+FUZZSRC += $(TOP)/test/fuzzcheck.c
+FUZZSRC += $(TOP)/test/ossfuzz.c
+FUZZSRC += $(TOP)/test/fuzzinvariants.c
DBFUZZ_OPT =
KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
ST_OPT = -DSQLITE_THREADSAFE=0
$(TCCX) -I. -g -O0 -DSTANDALONE -o dbfuzz2$(EXE) \
$(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c $(TLIBS) $(THREADLIB)
-fuzzcheck$(EXE): $(TOP)/test/fuzzcheck.c sqlite3.c sqlite3.h $(TOP)/test/ossfuzz.c
+fuzzcheck$(EXE): $(FUZZSRC) sqlite3.c sqlite3.h
$(TCCX) -o fuzzcheck$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_ENABLE_MEMSYS5 $(FUZZCHECK_OPT) -DSQLITE_OSS_FUZZ \
- $(TOP)/test/fuzzcheck.c $(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS) $(THREADLIB)
+ $(FUZZSRC) sqlite3.c $(TLIBS) $(THREADLIB)
ossshell$(EXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
$(TCCX) -o ossshell$(EXE) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
-C Do\snot\sremove\sthe\sEP_CanBeNull\sflag\sfrom\sexpressions\sduring\sa\sLEFT\sJOIN\nstrength\sreduction\sif\sthe\squery\salso\scontains\sa\sRIGHT\sJOIN.\sFix\sfor\nthe\sproblem\sidentified\sby\n[forum/forumpost/b40696f50145d21c|forum\spost\sb40696f50145d21c].
-D 2022-06-13T12:42:24.645
+C Attempt\sto\senhance\sfuzzcheck\sto\sdo\ssome\ssimple\sinvariant\stesting\son\squeries.\nThis\sis\san\sincremental\scheck-in\sfor\sa\swork-in-progress.
+D 2022-06-14T19:12:25.257
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in e45046a016985c8fedfc5e7aad88ee396033a1e9b4e929fd6e1033e4c39dba95
+F Makefile.in bccb0ed3f05fc41aee15da77c844c48b5da419cbb9af35b8a147536c9ad1c822
F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
-F Makefile.msc b28a8a7a977e7312f6859f560348e1eb110c21bd6cf9fab0d16537c0a514eef3
+F Makefile.msc de7cb3e095ce2fdc33513ccd76ebdaeda1483d0ddab0410fe65cbdeadd4c0ee1
F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e
F VERSION fa8e7d2d1cc962f9e14c6d410387cf75860ee139462763fda887c1be4261f824
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 80ac3d2f82eb21c6fb4423f9c7f8e207abb51be6676845a00e246cfb22f9c77c
+F main.mk a5412510e5ec952915a7fda34e02079bb4e6ff8f97903f2a3d9ad2dee3e18044
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F test/fuzz4.test c229bcdb45518a89e1d208a21343e061503460ac69fae1539320a89f572eb634
F test/fuzz_common.tcl b7197de6ed1ee8250a4f82d67876f4561b42ee8cbbfc6160dcb66331bad3f830
F test/fuzz_malloc.test f348276e732e814802e39f042b1f6da6362a610af73a528d8f76898fde6b22f2
-F test/fuzzcheck.c e34696a5db46738118b2efd14fb71f8458ecf0f482df8bbae18fa1d64db9ab7b
+F test/fuzzcheck.c 08d629c91b8307630273eb1e2e9a12bf0cf83fa2934e2b94681e11bf43819ef2
F test/fuzzdata1.db 3e86d9cf5aea68ddb8e27c02d7dfdaa226347426c7eb814918e4d95475bf8517
F test/fuzzdata2.db 128b3feeb78918d075c9b14b48610145a0dd4c8d6f1ca7c2870c7e425f5bf31f
F test/fuzzdata3.db c6586d3e3cef0fbc18108f9bb649aa77bfc38aba
F test/fuzzer1.test 3d4c4b7e547aba5e5511a2991e3e3d07166cfbb8
F test/fuzzer2.test a85ef814ce071293bce1ad8dffa217cbbaad4c14
F test/fuzzerfault.test f64c4aef4c9e9edf1d6dc0d3f1e65dcc81e67c996403c88d14f09b74807a42bc
+F test/fuzzinvariants.c 37b1f99fe4a6335983651b6b97a250dacf03b9ef81a189e2b52aeeace88ab1b1
F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
F test/gencol1.test cc0dbb0ee116e5602e18ea7d47f2a0f76b26e09a823b7c36ef254370c2b0f3c1
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 342c501f532523347e6c339351e02043dd6ee9e11a291224b65ea72bd6c2ba40
-R a249dd322f3d0191f7349eb16a9ed1fc
+P b1be2259e2e08ec22a88bc9a18b3ab4d83246ad4c635c05cdf80d3eff84df06a
+R d9a53a2b707897d91318ee68037f4638
+T *branch * query-invariant-tests
+T *sym-query-invariant-tests *
+T -sym-trunk *
U drh
-Z 7bab454819c3e667fed9b310a0634268
+Z 709cfc900b1da90b6bf9416298b653df
# Remove this line to create a well-formed Fossil manifest.
-b1be2259e2e08ec22a88bc9a18b3ab4d83246ad4c635c05cdf80d3eff84df06a
\ No newline at end of file
+ce2d780163b3a28486904860a1815acc4169c09b971cfd199bb58d1e9a57b000
\ No newline at end of file
return rc;
}
+/*
+** Flag bits set by block_troublesome_sql()
+*/
+#define BTS_SELECT 0x000001
+#define BTS_NONSELECT 0x000002
+#define BTS_BADFUNC 0x000004
+
/*
** Disallow debugging pragmas such as "PRAGMA vdbe_debug" and
** "PRAGMA parser_trace" since they can dramatically increase the
** Also block ATTACH if attaching a file from the filesystem.
*/
static int block_troublesome_sql(
- void *Notused,
+ void *pClientData,
int eCode,
const char *zArg1,
const char *zArg2,
const char *zArg3,
const char *zArg4
){
- (void)Notused;
- (void)zArg2;
+ unsigned int *pFlags = (unsigned int*)pClientData;
(void)zArg3;
(void)zArg4;
- if( eCode==SQLITE_PRAGMA ){
- if( sqlite3_stricmp("busy_timeout",zArg1)==0
- && (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100)
- ){
- return SQLITE_DENY;
- }else if( eVerbosity==0 ){
- if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0
- || sqlite3_stricmp("parser_trace", zArg1)==0
- || sqlite3_stricmp("temp_store_directory", zArg1)==0
+ switch( eCode ){
+ case SQLITE_PRAGMA: {
+ if( sqlite3_stricmp("busy_timeout",zArg1)==0
+ && (zArg2==0 || strtoll(zArg2,0,0)>100 || strtoll(zArg2,0,10)>100)
){
return SQLITE_DENY;
+ }else if( eVerbosity==0 ){
+ if( sqlite3_strnicmp("vdbe_", zArg1, 5)==0
+ || sqlite3_stricmp("parser_trace", zArg1)==0
+ || sqlite3_stricmp("temp_store_directory", zArg1)==0
+ ){
+ return SQLITE_DENY;
+ }
+ }else if( sqlite3_stricmp("oom",zArg1)==0
+ && zArg2!=0 && zArg2[0]!=0 ){
+ oomCounter = atoi(zArg2);
+ }
+ *pFlags |= BTS_NONSELECT;
+ break;
+ }
+ case SQLITE_ATTACH: {
+ /* Deny the ATTACH if it is attaching anything other than an in-memory
+ ** database. */
+ *pFlags |= BTS_NONSELECT;
+ if( zArg1==0 ) return SQLITE_DENY;
+ if( strcmp(zArg1,":memory:")==0 ) return SQLITE_OK;
+ if( sqlite3_strglob("file:*[?]vfs=memdb", zArg1)==0
+ && sqlite3_strglob("file:*[^/a-zA-Z0-9_.]*[?]vfs=memdb", zArg1)!=0
+ ){
+ return SQLITE_OK;
+ }
+ return SQLITE_DENY;
+ }
+ case SQLITE_SELECT: {
+ *pFlags |= BTS_SELECT;
+ break;
+ }
+ case SQLITE_FUNCTION: {
+ static const char *azBadFuncs[] = {
+ "random",
+ "randomblob",
+ "rtreedepth",
+ };
+ int i;
+ for(i=0; i<sizeof(azBadFuncs)/sizeof(azBadFuncs[0]); i++){
+ if( sqlite3_stricmp(azBadFuncs[i], zArg2)==0 ){
+ *pFlags |= BTS_BADFUNC;
+ break;
+ }
}
- }else if( sqlite3_stricmp("oom",zArg1)==0
- && zArg2!=0 && zArg2[0]!=0 ){
- oomCounter = atoi(zArg2);
+ break;
+ }
+ case SQLITE_READ: {
+ /* Benign */
+ break;
}
- }else if( eCode==SQLITE_ATTACH ){
- /* Deny the ATTACH if it is attaching anything other than an in-memory
- ** database. */
- if( zArg1==0 ) return SQLITE_DENY;
- if( strcmp(zArg1,":memory:")==0 ) return SQLITE_OK;
- if( sqlite3_strglob("file:*[?]vfs=memdb", zArg1)==0
- && sqlite3_strglob("file:*[^/a-zA-Z0-9_.]*[?]vfs=memdb", zArg1)!=0
- ){
- return SQLITE_OK;
+ default: {
+ *pFlags |= BTS_NONSELECT;
}
- return SQLITE_DENY;
}
return SQLITE_OK;
}
+/* Implementation found in fuzzinvariant.c */
+int fuzz_invariant(
+ sqlite3 *db, /* The database connection */
+ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */
+ int iCnt, /* Invariant sequence number, starting at 0 */
+ int iRow, /* The row number for pStmt */
+ int *pbCorrupt /* IN/OUT: Flag indicating a corrupt database file */
+);
+
/*
** Run the SQL text
*/
-static int runDbSql(sqlite3 *db, const char *zSql){
+static int runDbSql(sqlite3 *db, const char *zSql, unsigned int *pBtsFlags){
int rc;
sqlite3_stmt *pStmt;
+ int bCorrupt = 0;
while( isspace(zSql[0]&0x7f) ) zSql++;
if( zSql[0]==0 ) return SQLITE_OK;
if( eVerbosity>=4 ){
printf("RUNNING-SQL: [%s]\n", zSql);
fflush(stdout);
}
+ (*pBtsFlags) = 0;
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc==SQLITE_OK ){
+ int nRow = 0;
while( (rc = sqlite3_step(pStmt))==SQLITE_ROW ){
+ nRow++;
+ if( (*pBtsFlags)==BTS_SELECT ){
+ int iCnt = 0;
+ for(iCnt=0; iCnt<99999; iCnt++){
+ rc = fuzz_invariant(db, pStmt, iCnt, nRow, &bCorrupt);
+ if( rc==SQLITE_DONE ) break;
+ if( eVerbosity>0 ){
+ if( rc==SQLITE_OK ){
+ printf("invariant-check: ok\n");
+ }else if( rc==SQLITE_CORRUPT ){
+ printf("invariant-check: failed due to database corruption\n");
+ }
+ }
+ }
+ }
if( eVerbosity>=5 ){
int j;
for(j=0; j<sqlite3_column_count(pStmt); j++){
char *zSql = 0; /* SQL text to run */
int nSql; /* Bytes of SQL text */
FuzzCtx cx; /* Fuzzing context */
+ unsigned int btsFlags = 0; /* Parsing flags */
if( nByte<10 ) return 0;
if( sqlite3_initialize() ) return 0;
/* Block debug pragmas and ATTACH/DETACH. But wait until after
** deserialize to do this because deserialize depends on ATTACH */
- sqlite3_set_authorizer(cx.db, block_troublesome_sql, 0);
+ sqlite3_set_authorizer(cx.db, block_troublesome_sql, &btsFlags);
#ifdef VT02_SOURCES
sqlite3_vt02_init(cx.db, 0, 0);
char cSaved = zSql[i+1];
zSql[i+1] = 0;
if( sqlite3_complete(zSql+j) ){
- rc = runDbSql(cx.db, zSql+j);
+ rc = runDbSql(cx.db, zSql+j, &btsFlags);
j = i+1;
}
zSql[i+1] = cSaved;
}
}
if( j<i ){
- runDbSql(cx.db, zSql+j);
+ runDbSql(cx.db, zSql+j, &btsFlags);
}
}
testrun_finished:
--- /dev/null
+/*
+** 2022-06-14
+**
+** 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 library is used by fuzzcheck to test query invariants.
+**
+** An sqlite3_stmt is passed in that has just returned SQLITE_ROW. This
+** routine does:
+**
+** * Record the output of the current row
+** * Construct an alternative query that should return the same row
+** * Run the alternative query and verify that it does in fact return
+** the same row
+**
+*/
+#include "sqlite3.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/* Forward references */
+static char *fuzz_invariant_sql(sqlite3_stmt*, int);
+static int sameValue(sqlite3_value*,sqlite3_value*);
+static void reportInvariantFailed(sqlite3_stmt*,sqlite3_stmt*,int);
+
+/*
+** Do an invariant check on pStmt. iCnt determines which invariant check to
+** perform. The first check is iCnt==0.
+**
+** *pbCorrupt is a flag that, if true, indicates that the database file
+** is known to be corrupt. A value of non-zero means "yes, the database
+** is corrupt". A zero value means "we do not know whether or not the
+** database is corrupt". The value might be set prior to entry, or this
+** routine might set the value.
+**
+** Return values:
+**
+** SQLITE_OK This check was successful.
+**
+** SQLITE_DONE iCnt is out of range.
+**
+** SQLITE_CORRUPT The invariant failed, but the underlying database
+** file is indicating that it is corrupt, which might
+** be the cause of the malfunction.
+**
+** SQLITE_INTERNAL The invariant failed, and the database file is not
+** corrupt. (This never happens because this function
+** will call abort() following an invariant failure.)
+**
+** (other) Some other kind of error occurred.
+*/
+int fuzz_invariant(
+ sqlite3 *db, /* The database connection */
+ sqlite3_stmt *pStmt, /* Test statement stopped on an SQLITE_ROW */
+ int iCnt, /* Invariant sequence number, starting at 0 */
+ int iRow, /* The row number for pStmt */
+ int *pbCorrupt /* IN/OUT: Flag indicating a corrupt database file */
+){
+ char *zTest;
+ sqlite3_stmt *pTestStmt = 0;
+ int rc;
+ int i;
+ int nCol;
+
+ if( *pbCorrupt ) return SQLITE_DONE;
+ zTest = fuzz_invariant_sql(pStmt, iCnt);
+ if( zTest==0 ) return SQLITE_DONE;
+ rc = sqlite3_prepare_v2(db, zTest, -1, &pTestStmt, 0);
+ sqlite3_free(zTest);
+ if( rc ){
+ sqlite3_finalize(pTestStmt);
+ return rc;
+ }
+ nCol = sqlite3_column_count(pStmt);
+ for(i=0; i<nCol; i++){
+ sqlite3_bind_value(pTestStmt, i+1, sqlite3_column_value(pStmt,i));
+ }
+ while( (rc = sqlite3_step(pTestStmt))==SQLITE_ROW ){
+ for(i=0; i<nCol; i++){
+ if( !sameValue(sqlite3_column_value(pStmt,i),
+ sqlite3_column_value(pTestStmt,i)) ) break;
+ }
+ if( i>=nCol ) break;
+ }
+ if( rc!=SQLITE_ROW ){
+ /* No matching output row found */
+ sqlite3_stmt *pCk = 0;
+ rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pCk, 0);
+ if( rc ){
+ sqlite3_finalize(pCk);
+ sqlite3_finalize(pTestStmt);
+ return rc;
+ }
+ rc = sqlite3_step(pCk);
+ if( rc!=SQLITE_ROW
+ || sqlite3_column_text(pCk, 0)==0
+ || strcmp((const char*)sqlite3_column_text(pCk,0),"ok")!=0
+ ){
+ *pbCorrupt = 1;
+ sqlite3_finalize(pCk);
+ sqlite3_finalize(pTestStmt);
+ return SQLITE_CORRUPT;
+ }
+ sqlite3_finalize(pCk);
+ reportInvariantFailed(pStmt, pTestStmt, iRow);
+ return SQLITE_INTERNAL;
+ }
+ sqlite3_finalize(pTestStmt);
+ return SQLITE_OK;
+}
+
+
+/*
+** Generate SQL used to test a statement invariant.
+**
+** Return 0 if the iCnt is out of range.
+*/
+static char *fuzz_invariant_sql(sqlite3_stmt *pStmt, int iCnt){
+ const char *zIn;
+ size_t nIn;
+ const char *zAnd = "WHERE";
+ int i;
+ sqlite3_str *pTest;
+ sqlite3_stmt *pBase = 0;
+ sqlite3 *db = sqlite3_db_handle(pStmt);
+ int rc;
+
+ if( iCnt<0 || iCnt>0 ) return 0;
+ zIn = sqlite3_sql(pStmt);
+ if( zIn==0 ) return 0;
+ nIn = strlen(zIn);
+ while( nIn>0 && (isspace(zIn[nIn-1]) || zIn[nIn-1]==';') ) nIn--;
+ if( strchr(zIn, '?') ) return 0;
+ pTest = sqlite3_str_new(0);
+ sqlite3_str_appendf(pTest, "SELECT * FROM (%.*s)", (int)nIn, zIn);
+ rc = sqlite3_prepare_v2(db, sqlite3_str_value(pTest), -1, &pBase, 0);
+ if( rc ){
+ sqlite3_finalize(pBase);
+ pBase = pStmt;
+ }
+ for(i=0; i<sqlite3_column_count(pStmt); i++){
+ if( sqlite3_column_type(pStmt, i)==SQLITE_NULL ){
+ sqlite3_str_appendf(pTest, " %s \"%w\" ISNULL", zAnd,
+ sqlite3_column_name(pBase,i));
+ }else{
+ sqlite3_str_appendf(pTest, " %s \"%s\"=?%d", zAnd,
+ sqlite3_column_name(pBase, i), i+1);
+ }
+ zAnd = "AND";
+ }
+ if( pBase!=pStmt ) sqlite3_finalize(pBase);
+ return sqlite3_str_finish(pTest);
+}
+
+/*
+** Return true if and only if v1 and is the same as v2.
+*/
+static int sameValue(sqlite3_value *v1, sqlite3_value *v2){
+ int x = 1;
+ if( sqlite3_value_type(v1)!=sqlite3_value_type(v2) ) return 0;
+ switch( sqlite3_value_type(v1) ){
+ case SQLITE_INTEGER: {
+ x = sqlite3_value_int64(v1)==sqlite3_value_int64(v2);
+ break;
+ }
+ case SQLITE_FLOAT: {
+ x = sqlite3_value_double(v1)==sqlite3_value_double(v2);
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *z1 = (const char*)sqlite3_value_text(v1);
+ const char *z2 = (const char*)sqlite3_value_text(v2);
+ x = ((z1==0 && z2==0) || (z1!=0 && z2!=0 && strcmp(z1,z1)==0));
+ break;
+ }
+ case SQLITE_BLOB: {
+ int len1 = sqlite3_value_bytes(v1);
+ const unsigned char *b1 = sqlite3_value_blob(v1);
+ int len2 = sqlite3_value_bytes(v2);
+ const unsigned char *b2 = sqlite3_value_blob(v2);
+ if( len1!=len2 ){
+ x = 0;
+ }else if( len1==0 ){
+ x = 1;
+ }else{
+ x = (b1!=0 && b2!=0 && memcmp(b1,b2,len1)==0);
+ }
+ break;
+ }
+ }
+ return x;
+}
+
+/*
+** Print a single row from the prepared statement
+*/
+static void printRow(sqlite3_stmt *pStmt, int iRow){
+ int i, nCol;
+ nCol = sqlite3_column_count(pStmt);
+ for(i=0; i<nCol; i++){
+ printf("row%d.col%d] = ", iRow, i);
+ switch( sqlite3_column_type(pStmt, i) ){
+ case SQLITE_NULL: {
+ printf("NULL\n");
+ break;
+ }
+ case SQLITE_INTEGER: {
+ printf("(integer) %lld\n", sqlite3_column_int64(pStmt, i));
+ break;
+ }
+ case SQLITE_FLOAT: {
+ printf("(float) %f\n", sqlite3_column_double(pStmt, i));
+ break;
+ }
+ case SQLITE_TEXT: {
+ printf("(text) \"%s\"\n", sqlite3_column_text(pStmt, i));
+ break;
+ }
+ case SQLITE_BLOB: {
+ int n = sqlite3_column_bytes(pStmt, i);
+ int j;
+ unsigned const char *data = sqlite3_column_blob(pStmt, i);
+ printf("(blob %d bytes) x'", n);
+ for(j=0; j<20 && j<n; j++){
+ printf("%02x", data[j]);
+ }
+ if( j<n ) printf("...");
+ printf("'\n");
+ break;
+ }
+ }
+ }
+}
+
+/*
+** Report a failure of the invariant: The current output row of pOrig
+** does not appear in any row of the output from pTest.
+*/
+static void reportInvariantFailed(
+ sqlite3_stmt *pOrig, /* The original query */
+ sqlite3_stmt *pTest, /* The alternative test query with a missing row */
+ int iRow /* Row number in pOrig */
+){
+ int iTestRow = 0;
+ printf("Invariant check failed on row %d.\n", iRow);
+ printf("Original query --------------------------------------------------\n");
+ printf("%s\n", sqlite3_expanded_sql(pOrig));
+ printf("Alternative query -----------------------------------------------\n");
+ printf("%s\n", sqlite3_expanded_sql(pTest));
+ printf("Result row that is missing from the alternative -----------------\n");
+ printRow(pOrig, iRow);
+ printf("Complete results from the alternative query ---------------------\n");
+ sqlite3_reset(pTest);
+ while( sqlite3_step(pTest)==SQLITE_ROW ){
+ iTestRow++;
+ printRow(pTest, iTestRow);
+ }
+ sqlite3_finalize(pTest);
+ abort();
+}