mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Merge latest begin-concurrent changes into this branch.
FossilOrigin-Name: a93ca38b432e0c9fb2e00499cfc88d09f59f27f908f377d8ae99b6717e548a60
This commit is contained in:
140
tool/dbtotxt.c
Normal file
140
tool/dbtotxt.c
Normal 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
56
tool/dbtotxt.md
Normal 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.
|
Reference in New Issue
Block a user