Merge the dbtotxt enhancement from trunk.

FossilOrigin-Name: b386fce9a23e628dce7362dcca2904b8d0af6da58a6fe6eb7f12f058a8363e49
This commit is contained in:
drh 2018-12-13 15:52:31 +00:00
commit 51caa04836
8 changed files with 320 additions and 19 deletions

View File

@ -669,7 +669,7 @@ ossshell$(TEXE): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.
$(TOP)/test/ossfuzz.c sqlite3.c $(TLIBS)
sessionfuzz$(TEXE): $(TOP)/test/sessionfuzz.c sqlite3.c sqlite3.h
$(CC) $(CFLAGS) -I. -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS)
$(LTLINK) -o $@ $(TOP)/test/sessionfuzz.c $(TLIBS)
dbfuzz$(TEXE): $(TOP)/test/dbfuzz.c sqlite3.c sqlite3.h
$(LTLINK) -o $@ $(DBFUZZ_OPT) $(TOP)/test/dbfuzz.c sqlite3.c $(TLIBS)
@ -685,7 +685,8 @@ DBFUZZ2_OPTS = \
-DSQLITE_EANBLE_FTS5
dbfuzz2: $(TOP)/test/dbfuzz2.c sqlite3.c sqlite3.h
clang-6.0 -I. -g -O0 -fsanitize=fuzzer,undefined,address -o dbfuzz2 \
clang-6.0 $(OPT_FEATURE_FLAGS) $(OPTS) -I. -g -O0 \
-fsanitize=fuzzer,undefined,address -o dbfuzz2 \
$(DBFUZZ2_OPTS) $(TOP)/test/dbfuzz2.c sqlite3.c
mkdir -p dbfuzz2-dir
cp $(TOP)/test/dbfuzz2-seed* dbfuzz2-dir
@ -1291,6 +1292,9 @@ dbdump$(TEXE): $(TOP)/ext/misc/dbdump.c sqlite3.lo
$(LTLINK) -DDBDUMP_STANDALONE -o $@ \
$(TOP)/ext/misc/dbdump.c sqlite3.lo $(TLIBS)
dbtotxt$(TEXE): $(TOP)/tool/dbtotxt.c
$(LTLINK)-o $@ $(TOP)/tool/dbtotxt.c
showdb$(TEXE): $(TOP)/tool/showdb.c sqlite3.lo
$(LTLINK) -o $@ $(TOP)/tool/showdb.c sqlite3.lo $(TLIBS)

View File

@ -2425,6 +2425,9 @@ testloadext.lo: $(TOP)\src\test_loadext.c $(SQLITE3H)
testloadext.dll: testloadext.lo
$(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /OUT:$@ testloadext.lo
dbtotxt.exe: $(TOP)\tool\dbtotxt.c
$(LTLINK) $(NO_WARN) $(TOP)\tool\dbtotxt.c /link $(LDFLAGS) $(LTLINKOPTS)
showdb.exe: $(TOP)\tool\showdb.c $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
$(TOP)\tool\showdb.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)

View File

@ -976,6 +976,9 @@ $(TEST_EXTENSION): $(TOP)/src/test_loadext.c
extensiontest: testfixture$(EXE) $(TEST_EXTENSION)
./testfixture$(EXE) $(TOP)/test/loadext.test
dbtotxt$(EXE): $(TOP)/tool/dbtotxt.c
$(TCC) -o dbtotxt$(EXE) $(TOP)/tool/dbtotxt.c
showdb$(EXE): $(TOP)/tool/showdb.c sqlite3.o
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o showdb$(EXE) \
$(TOP)/tool/showdb.c sqlite3.o $(THREADLIB)

View File

@ -1,10 +1,10 @@
C New\sdatabase\scorruption\stest\scases\sdiscovered\sby\sdbfuzz2.\s\sThe\snew\scases\nhave\sbeen\sadded\sto\stest/fuzzdata7.db,\sbut\shave\snot\syet\sall\sbeen\sfixed,\nso\stests\swill\snot\scurrently\spass.
D 2018-12-13T03:36:13.086
C Merge\sthe\sdbtotxt\senhancement\sfrom\strunk.
D 2018-12-13T15:52:31.619
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 68d0ba0f0b533d5bc84c78c13a6ce84ee81183a67014caa47a969e67f028fa1c
F Makefile.in 2f1b61ac62689ca4e9cbff9fdb359578ea37ddd9252355ec0b7b9700ad56fe90
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc b7d4a710fa3f0b8cfc532ff195b85dc1ba2a8ad34343cb3d67639f28f0a24306
F Makefile.msc 2ef13d6845b899eaaa6122c69b74175656a97e26666567af795f4cfe41b7a673
F README.md 377233394b905d3b2e2b33741289e093bc93f2e7adbe00923b2c5958c9a9edee
F VERSION 453e2f4529ca208196d5567db28d549d7151f79efd33f6e6cfe6e613e583a0be
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@ -429,7 +429,7 @@ F ext/userauth/userauth.c f81aa5a3ecacf406f170c62a144405858f6f6de51dbdc0920134e6
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F main.mk 55f94164ecc194b067d9c55e106f37fd3c9b39f9668e8b568c98f008b6f9ec90
F main.mk cca1ecdd5dfe4579ded84e5abdd38e1866bbb8b8cf58d1a24496e9b1b65580d7
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@ -508,7 +508,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c abd65c518c198400193c6319a70c0d722fa30a35be89dc898917ff6489edf017
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
F src/select.c 8c7317d5ee920516a56b8b4ca79fbfca70a1f8b52d67e884c808ea3a016c04e3
F src/shell.c.in 5f38bd0e127c2cc4e506b5c3565c10879ddfae6c2d867bb5972563e40717c19c
F src/shell.c.in 6c06ff4077ab38b30a307c058ecc650e958bfdb6da1c255ebbd8779447e0cae9
F src/sqlite.h.in 92fd656c26cc76de9fa8c5bf1a473066e3b5c6da345a447679f0f44de1aa4edd
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683
@ -1694,6 +1694,8 @@ F tool/build-shell.sh 950f47c6174f1eea171319438b93ba67ff5bf367
F tool/cg_anno.tcl f95b0006c52cf7f0496b506343415b6ee3cdcdd3 x
F tool/checkSpacing.c 810e51703529a204fc4e1eb060e9ab663e3c06d2
F tool/dbhash.c a06228aa21ebc4e6ea8daa486601d938499238a5
F tool/dbtotxt.c 1655f60fd7b24a3e7a25d01cdb3a6a4785f30112213b08ff83a27ae7ef2dd13e
F tool/dbtotxt.md c9a57af8739957ef36d2cfad5c4b1443ff3688ed33e4901ee200c8b651f43f3c
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
F tool/fast_vacuum.c 5ba0d6f5963a0a63bdc42840f678bad75b2ebce1
F tool/fragck.tcl 5265a95126abcf6ab357f7efa544787e5963f439
@ -1784,10 +1786,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 2b690dbdffe144bd69ca0aa291c230faf3d9d73f7a2985d50f016fe54003f9a9
R 44eae090dbc00ffed1ffd6b71877bb22
T *branch * dbfuzz2-cases
T *sym-dbfuzz2-cases *
T -sym-trunk *
P b4210d320c6426d2838d4cc08709fdfd015c1c95fcad9a5f9e8fd390c23b9fad e3bf1d3ea5f748c5142c2403813fdace5aedc1fc68f0dcd5eae40a2fe763fedb
R f29a990b635857a4ac73167c7e81e845
U drh
Z 83e3bf755a107d9d471054676d38303f
Z 899a9ea598b74391d5cfbd03300f9f81

View File

@ -1 +1 @@
b4210d320c6426d2838d4cc08709fdfd015c1c95fcad9a5f9e8fd390c23b9fad
b386fce9a23e628dce7362dcca2904b8d0af6da58a6fe6eb7f12f058a8363e49

View File

@ -1066,6 +1066,7 @@ struct ShellState {
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
/* Allowed values for ShellState.eTraceType
*/
@ -3444,6 +3445,7 @@ static const char *(azHelp[]) = {
" --append Use appendvfs to append database to the end of FILE",
#ifdef SQLITE_ENABLE_DESERIALIZE
" --deserialize Load into memory useing sqlite3_deserialize()",
" --hexdb Load the output of \"dbtotxt\" as an in-memory database",
#endif
" --new Initialize FILE to an empty database",
" --readonly Open FILE readonly",
@ -3723,6 +3725,87 @@ int deduceDatabaseType(const char *zName, int dfltZip){
return rc;
}
#ifdef SQLITE_ENABLE_DESERIALIZE
/*
** Reconstruct an in-memory database using the output from the "dbtotxt"
** program. Read content from the file in p->zDbFilename. If p->zDbFilename
** is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
int nLine = 1;
int n = 0;
int pgsz = 0;
int iOffset = 0;
int j, k;
int rc;
FILE *in;
unsigned char x[16];
char zLine[100];
if( p->zDbFilename ){
in = fopen(p->zDbFilename, "r");
if( in==0 ){
utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename);
return 0;
}
}else{
in = stdin;
}
*pnData = 0;
if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<=0 ) goto readHexDb_error;
a = sqlite3_malloc( n );
if( a==0 ){
utf8_printf(stderr, "Out of memory!\n");
goto readHexDb_error;
}
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
utf8_printf(stderr, "invalid pagesize\n");
goto readHexDb_error;
}
for(nLine=2; fgets(zLine, sizeof(zLine), in)!=0; nLine++){
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
continue;
}
if( strncmp(zLine, "| end ", 6)==0 ){
break;
}
rc = sscanf(zLine,"| %d: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx"
" %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
if( k+16>n ){
utf8_printf(stderr, "continue exceeds file size\n");
goto readHexDb_error;
}
memcpy(a+k, x, 16);
}
}
*pnData = n;
if( in!=stdin ) fclose(in);
return a;
readHexDb_error:
if( in!=stdin ){
fclose(in);
}else{
while( fgets(zLine, sizeof(zLine), in)!=0 ){
if(strncmp(zLine, "| end ", 6)==0 ) break;
}
}
sqlite3_free(a);
utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_ENABLE_DESERIALIZE */
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
@ -3756,6 +3839,7 @@ static void open_db(ShellState *p, int openFlags){
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "apndvfs");
break;
}
case SHELL_OPEN_HEXDB:
case SHELL_OPEN_DESERIALIZE: {
sqlite3_open(0, &p->db);
break;
@ -3810,9 +3894,19 @@ static void open_db(ShellState *p, int openFlags){
sqlite3_free(zSql);
}
#ifdef SQLITE_ENABLE_DESERIALIZE
else if( p->openMode==SHELL_OPEN_DESERIALIZE ){
else
if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){
int nData = 0;
unsigned char *aData = (unsigned char*)readFile(p->zDbFilename, &nData);
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
aData = (unsigned char*)readFile(p->zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
if( aData==0 ){
utf8_printf(stderr, "Error in hexdb input\n");
return;
}
}
int rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
@ -6749,7 +6843,9 @@ static int do_meta_command(char *zLine, ShellState *p){
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
p->openMode = SHELL_OPEN_DESERIALIZE;
#endif
}else if( optionMatch(z, "hexdb") ){
p->openMode = SHELL_OPEN_HEXDB;
#endif /* SQLITE_ENABLE_DESERIALIZE */
}else if( z[0]=='-' ){
utf8_printf(stderr, "unknown option: %s\n", z);
rc = 1;
@ -6758,7 +6854,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
/* If a filename is specified, try to open it first */
zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0;
if( zNewFilename ){
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag ) shellDeleteFile(zNewFilename);
p->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);

140
tool/dbtotxt.c Normal file
View File

@ -0,0 +1,140 @@
/*
** Copyright 2008 D. Richard Hipp and Hipp, Wyrick & Company, Inc.
** All Rights Reserved
**
******************************************************************************
**
** This file implements a stand-alone utility program that converts
** a binary file (usually an SQLite database) into a text format that
** is compact and friendly to human-readers.
**
** Usage:
**
** dbtotxt [--pagesize N] FILENAME
**
** The translation of the database appears on standard output. If the
** --pagesize command-line option is omitted, then the page size is taken
** from the database header.
**
** Compactness is achieved by suppressing lines of all zero bytes. This
** works well at compressing test databases that are mostly empty. But
** the output will probably be lengthy for a real database containing lots
** of real content. For maximum compactness, it is suggested that test
** databases be constructed with "zeroblob()" rather than "randomblob()"
** used for filler content and with "PRAGMA secure_delete=ON" selected to
** zero-out deleted content.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* Return true if the line is all zeros */
static int allZero(unsigned char *aLine){
int i;
for(i=0; i<16 && aLine[i]==0; i++){}
return i==16;
}
int main(int argc, char **argv){
int pgsz = 0; /* page size */
long szFile; /* Size of the input file in bytes */
FILE *in; /* Input file */
int i, j; /* Loop counters */
int nErr = 0; /* Number of errors */
const char *zInputFile = 0; /* Name of the input file */
const char *zBaseName = 0; /* Base name of the file */
int lastPage = 0; /* Last page number shown */
int iPage; /* Current page number */
unsigned char aLine[16]; /* A single line of the file */
unsigned char aHdr[100]; /* File header */
for(i=1; i<argc; i++){
if( argv[i][0]=='-' ){
const char *z = argv[i];
z++;
if( z[0]=='-' ) z++;
if( strcmp(z,"pagesize")==0 ){
i++;
pgsz = atoi(argv[i]);
if( pgsz<512 || pgsz>65536 || (pgsz&(pgsz-1))!=0 ){
fprintf(stderr, "Page size must be a power of two between"
" 512 and 65536.\n");
nErr++;
}
continue;
}
fprintf(stderr, "Unknown option: %s\n", argv[i]);
nErr++;
}else if( zInputFile ){
fprintf(stderr, "Already using a different input file: [%s]\n", argv[i]);
nErr++;
}else{
zInputFile = argv[i];
}
}
if( zInputFile==0 ){
fprintf(stderr, "No input file specified.\n");
nErr++;
}
if( nErr ){
fprintf(stderr, "Usage: %s [--pagesize N] FILENAME\n", argv[0]);
exit(1);
}
in = fopen(zInputFile, "rb");
if( in==0 ){
fprintf(stderr, "Cannot open input file [%s]\n", zInputFile);
exit(1);
}
fseek(in, 0, SEEK_END);
szFile = ftell(in);
rewind(in);
if( szFile<512 ){
fprintf(stderr, "File too short. Minimum size is 512 bytes.\n");
exit(1);
}
if( fread(aHdr, 100, 1, in)!=1 ){
fprintf(stderr, "Cannot read file header\n");
exit(1);
}
rewind(in);
if( pgsz==0 ){
pgsz = (aHdr[16]<<8) | aHdr[17];
if( pgsz==1 ) pgsz = 65536;
if( pgsz<512 || (pgsz&(pgsz-1))!=0 ){
fprintf(stderr, "Invalid page size in header: %d\n", pgsz);
exit(1);
}
}
zBaseName = zInputFile;
for(i=0; zInputFile[i]; i++){
if( zInputFile[i]=='/' && zInputFile[i+1]!=0 ) zBaseName = zInputFile+1;
}
printf("| size %d pagesize %d filename %s\n",(int)szFile,pgsz,zBaseName);
for(i=0; i<szFile; i+=16){
int got = (int)fread(aLine, 1, 16, in);
if( got!=16 ){
static int once = 1;
if( once ){
fprintf(stderr, "Could not read input file starting at byte %d\n",
i+got);
}
memset(aLine+got, 0, 16-got);
}
if( allZero(aLine) ) continue;
iPage = i/pgsz + 1;
if( lastPage!=iPage ){
printf("| page %d offset %d\n", iPage, (iPage-1)*pgsz);
lastPage = iPage;
}
printf("| %5d:", i-(iPage-1)*pgsz);
for(j=0; j<16; j++) printf(" %02x", aLine[j]);
printf(" ");
for(j=0; j<16; j++){
char c = aLine[j];
fputc(c>=0x20 && c<=0x7e ? c : '.', stdout);
}
fputc('\n', stdout);
}
fclose(in);
printf("| end %s\n", zBaseName);
return 0;
}

56
tool/dbtotxt.md Normal file
View File

@ -0,0 +1,56 @@
<h1 align="center">The dbtotxt Tool</h1>
The dbtotxt utility program reads an SQLite database file and writes its
raw binary content to screen as a hex dump for testing and debugging
purposes.
The hex-dump output is formatted in such a way as to be easily readable
both by humans and by software. The dbtotxt utility has long been a part
of the TH3 test suite. The output of dbtotxt can be embedded in TH3 test
scripts and used to generate very specific database files, perhaps with
deliberately introduced corruption. The cov1/corrupt*.test modules in
TH3 make extensive use of dbtotxt.
More recently (2018-12-13) the dbtotxt utility has been added to the SQLite
core and the command-line shell (CLI) has been augmented to be able to read
dbtotxt output. The CLI dot-command is:
> .open --hexdb ?OPTIONAL-FILENAME?
If the OPTIONAL-FILENAME is included, then content is read from that file.
If OPTIONAL-FILENAME is omitted, then the text is taken from the input stream,
terminated by the "| end" line of the dbtotxt text. This allows small test
databases to be embedded directly in scripts. Consider this example:
>
.open --hexdb
| size 8192 pagesize 4096 filename x9.db
| page 1 offset 0
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
| 16: 10 00 01 01 00 40 20 20 00 00 00 04 00 00 00 02 .....@ ........
| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 04 ................
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 ................
| 96: 00 2e 30 38 0d 00 00 00 01 0f c0 00 0f c0 00 00 ..08............
| 4032: 3e 01 06 17 11 11 01 69 74 61 62 6c 65 74 31 74 >......itablet1t
| 4048: 31 02 43 52 45 41 54 45 20 54 41 42 4c 45 20 74 1.CREATE TABLE t
| 4064: 31 28 78 2c 79 20 44 45 46 41 55 4c 54 20 78 27 1(x,y DEFAULT x'
| 4080: 66 66 27 2c 7a 20 44 45 46 41 55 4c 54 20 30 29 ff',z DEFAULT 0)
| page 2 offset 4096
| 0: 0d 08 14 00 04 00 10 00 0e 05 0a 0f 04 15 00 10 ................
| 16: 88 02 03 05 90 04 0e 08 00 00 00 00 00 00 00 00 ................
| 1040: 00 00 00 00 ff 87 7c 02 05 8f 78 0e 08 00 00 00 ......|...x.....
| 2064: 00 00 00 ff 0c 0a 01 fb 00 00 00 00 00 00 00 00 ................
| 2560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 83 ................
| 2576: 78 01 05 87 70 0e 08 00 00 00 00 00 00 00 00 00 x...p...........
| 3072: 00 00 00 00 00 00 00 00 00 ff 00 00 01 fb 00 00 ................
| 3584: 00 00 00 00 00 83 78 00 05 87 70 0e 08 00 00 00 ......x...p.....
| 4080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ................
| end x9.db
SELECT rowid FROM t1;
PRAGMA integrity_check;
You can run this script to see that the database file is correctly decoded
and loaded. Furthermore, you can make subtle corruptions to the input
database simply by editing the hexadecimal description, then rerun the
script to verify that SQLite correctly handles the corruption.