2002-07-06 16:32:14 +00:00
/*
* * A utility for printing all or part of an SQLite database file .
*/
# include <stdio.h>
# include <ctype.h>
2015-10-29 13:48:15 +00:00
# define ISDIGIT(X) isdigit((unsigned char)(X))
# define ISPRINT(X) isprint((unsigned char)(X))
2002-07-06 16:32:14 +00:00
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
2012-10-13 09:31:20 +00:00
# if !defined(_MSC_VER)
2002-07-06 16:32:14 +00:00
# include <unistd.h>
2014-07-18 21:16:37 +00:00
# else
# include <io.h>
2012-10-13 09:31:20 +00:00
# endif
2002-07-06 16:32:14 +00:00
# include <stdlib.h>
2010-04-26 15:44:07 +00:00
# include <string.h>
2015-08-03 17:03:31 +00:00
# include <assert.h>
2012-04-03 14:59:50 +00:00
# include "sqlite3.h"
2002-07-06 16:32:14 +00:00
2020-07-22 11:42:50 +00:00
typedef unsigned char u8 ; /* unsigned 8-bit */
typedef unsigned int u32 ; /* unsigned 32-bit */
typedef sqlite3_int64 i64 ; /* signed 64-bit */
typedef sqlite3_uint64 u64 ; /* unsigned 64-bit */
2002-07-06 16:32:14 +00:00
2015-08-03 17:03:31 +00:00
static struct GlobalData {
2020-07-22 11:42:50 +00:00
u32 pagesize ; /* Size of a database page */
2015-08-03 17:03:31 +00:00
int dbfd ; /* File descriptor for reading the DB */
2020-07-22 11:42:50 +00:00
u32 mxPage ; /* Last page number */
2015-08-03 17:03:31 +00:00
int perLine ; /* HEX elements to print per line */
2015-08-04 15:23:49 +00:00
int bRaw ; /* True to access db file via OS APIs */
2015-08-04 15:29:43 +00:00
sqlite3_file * pFd ; /* File descriptor for non-raw mode */
2015-08-03 17:03:31 +00:00
sqlite3 * pDb ; /* Database handle that owns pFd */
} g = { 1024 , - 1 , 0 , 16 , 0 , 0 , 0 } ;
2010-04-26 15:44:07 +00:00
/*
* * Convert the var - int format into i64 . Return the number of bytes
* * in the var - int . Write the var - int value into * pVal .
*/
2010-04-26 16:47:12 +00:00
static int decodeVarint ( const unsigned char * z , i64 * pVal ) {
2010-04-26 15:44:07 +00:00
i64 v = 0 ;
2010-04-26 16:47:12 +00:00
int i ;
for ( i = 0 ; i < 8 ; i + + ) {
2010-04-26 15:44:07 +00:00
v = ( v < < 7 ) + ( z [ i ] & 0x7f ) ;
if ( ( z [ i ] & 0x80 ) = = 0 ) { * pVal = v ; return i + 1 ; }
}
v = ( v < < 8 ) + ( z [ i ] & 0xff ) ;
* pVal = v ;
return 9 ;
}
2010-08-23 15:26:49 +00:00
/*
* * Extract a big - endian 32 - bit integer
*/
2020-07-22 11:42:50 +00:00
static u32 decodeInt32 ( const u8 * z ) {
2010-08-23 15:26:49 +00:00
return ( z [ 0 ] < < 24 ) + ( z [ 1 ] < < 16 ) + ( z [ 2 ] < < 8 ) + z [ 3 ] ;
}
2010-04-26 15:44:07 +00:00
/* Report an out-of-memory error and die.
*/
2002-07-06 16:32:14 +00:00
static void out_of_memory ( void ) {
fprintf ( stderr , " Out of memory... \n " ) ;
exit ( 1 ) ;
}
2015-08-03 17:03:31 +00:00
/*
* * Open a database connection .
*/
static sqlite3 * openDatabase ( const char * zPrg , const char * zName ) {
sqlite3 * db = 0 ;
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI ;
int rc = sqlite3_open_v2 ( zName , & db , flags , 0 ) ;
if ( rc ! = SQLITE_OK ) {
const char * zErr = sqlite3_errmsg ( db ) ;
fprintf ( stderr , " %s: can't open %s (%s) \n " , zPrg , zName , zErr ) ;
sqlite3_close ( db ) ;
exit ( 1 ) ;
}
return db ;
}
/**************************************************************************
* * Beginning of low - level file access functions .
* *
* * All low - level access to the database file read by this program is
* * performed using the following four functions :
* *
* * fileOpen ( ) - open the db file
* * fileClose ( ) - close the db file
* * fileRead ( ) - read raw data from the db file
* * fileGetsize ( ) - return the size of the db file in bytes
*/
/*
* * Open the database file .
*/
static void fileOpen ( const char * zPrg , const char * zName ) {
assert ( g . dbfd < 0 ) ;
2015-08-04 15:23:49 +00:00
if ( g . bRaw = = 0 ) {
2015-08-03 17:03:31 +00:00
int rc ;
void * pArg = ( void * ) ( & g . pFd ) ;
g . pDb = openDatabase ( zPrg , zName ) ;
rc = sqlite3_file_control ( g . pDb , " main " , SQLITE_FCNTL_FILE_POINTER , pArg ) ;
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr ,
" %s: failed to obtain fd for %s (SQLite too old?) \n " , zPrg , zName
) ;
exit ( 1 ) ;
}
} else {
g . dbfd = open ( zName , O_RDONLY ) ;
if ( g . dbfd < 0 ) {
fprintf ( stderr , " %s: can't open %s \n " , zPrg , zName ) ;
exit ( 1 ) ;
}
}
}
/*
* * Close the database file opened by fileOpen ( )
*/
static void fileClose ( ) {
2015-08-04 15:23:49 +00:00
if ( g . bRaw = = 0 ) {
2015-08-03 17:03:31 +00:00
sqlite3_close ( g . pDb ) ;
g . pDb = 0 ;
g . pFd = 0 ;
} else {
close ( g . dbfd ) ;
g . dbfd = - 1 ;
}
}
2010-04-26 15:44:07 +00:00
/*
* * Read content from the file .
* *
2015-08-03 17:03:31 +00:00
* * Space to hold the content is obtained from sqlite3_malloc ( ) and needs
* * to be freed by the caller .
2010-04-26 15:44:07 +00:00
*/
2015-08-03 17:03:31 +00:00
static unsigned char * fileRead ( sqlite3_int64 ofst , int nByte ) {
2002-07-06 16:32:14 +00:00
unsigned char * aData ;
2015-04-15 15:29:05 +00:00
int got ;
2020-07-22 11:42:50 +00:00
aData = sqlite3_malloc64 ( 32 + ( i64 ) nByte ) ;
2002-07-06 16:32:14 +00:00
if ( aData = = 0 ) out_of_memory ( ) ;
2011-01-06 15:51:18 +00:00
memset ( aData , 0 , nByte + 32 ) ;
2015-08-04 15:23:49 +00:00
if ( g . bRaw = = 0 ) {
2015-08-03 17:03:31 +00:00
int rc = g . pFd - > pMethods - > xRead ( g . pFd , ( void * ) aData , nByte , ofst ) ;
if ( rc ! = SQLITE_OK & & rc ! = SQLITE_IOERR_SHORT_READ ) {
fprintf ( stderr , " error in xRead() - %d \n " , rc ) ;
exit ( 1 ) ;
}
} else {
2016-04-12 20:05:06 +00:00
lseek ( g . dbfd , ( long ) ofst , SEEK_SET ) ;
2015-08-03 17:03:31 +00:00
got = read ( g . dbfd , aData , nByte ) ;
if ( got > 0 & & got < nByte ) memset ( aData + got , 0 , nByte - got ) ;
}
2010-04-26 15:44:07 +00:00
return aData ;
}
2015-08-03 17:03:31 +00:00
/*
* * Return the size of the file in byte .
*/
2020-07-22 11:42:50 +00:00
static i64 fileGetsize ( void ) {
i64 res = 0 ;
2015-08-04 15:23:49 +00:00
if ( g . bRaw = = 0 ) {
2015-08-03 17:03:31 +00:00
int rc = g . pFd - > pMethods - > xFileSize ( g . pFd , & res ) ;
if ( rc ! = SQLITE_OK ) {
fprintf ( stderr , " error in xFileSize() - %d \n " , rc ) ;
exit ( 1 ) ;
}
} else {
struct stat sbuf ;
fstat ( g . dbfd , & sbuf ) ;
res = ( sqlite3_int64 ) ( sbuf . st_size ) ;
}
return res ;
}
/*
* * End of low - level file access functions .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-04-26 15:44:07 +00:00
/*
* * Print a range of bytes as hex and as ascii .
*/
static unsigned char * print_byte_range (
2020-07-22 11:42:50 +00:00
sqlite3_int64 ofst , /* First byte in the range of bytes to print */
int nByte , /* Number of bytes to print */
int printOfst /* Add this amount to the index on the left column */
2010-04-26 15:44:07 +00:00
) {
unsigned char * aData ;
int i , j ;
const char * zOfstFmt ;
if ( ( ( printOfst + nByte ) & ~ 0xfff ) = = 0 ) {
zOfstFmt = " %03x: " ;
} else if ( ( ( printOfst + nByte ) & ~ 0xffff ) = = 0 ) {
zOfstFmt = " %04x: " ;
} else if ( ( ( printOfst + nByte ) & ~ 0xfffff ) = = 0 ) {
zOfstFmt = " %05x: " ;
} else if ( ( ( printOfst + nByte ) & ~ 0xffffff ) = = 0 ) {
zOfstFmt = " %06x: " ;
} else {
zOfstFmt = " %08x: " ;
}
2015-08-03 17:03:31 +00:00
aData = fileRead ( ofst , nByte ) ;
for ( i = 0 ; i < nByte ; i + = g . perLine ) {
2020-07-22 11:42:50 +00:00
int go = 0 ;
for ( j = 0 ; j < g . perLine ; j + + ) {
if ( i + j > nByte ) { break ; }
if ( aData [ i + j ] ) { go = 1 ; break ; }
}
if ( ! go & & i > 0 & & i + g . perLine < nByte ) continue ;
2010-04-26 15:44:07 +00:00
fprintf ( stdout , zOfstFmt , i + printOfst ) ;
2015-08-03 17:03:31 +00:00
for ( j = 0 ; j < g . perLine ; j + + ) {
2010-04-26 15:44:07 +00:00
if ( i + j > nByte ) {
fprintf ( stdout , " " ) ;
} else {
fprintf ( stdout , " %02x " , aData [ i + j ] ) ;
}
2002-07-06 16:32:14 +00:00
}
2015-08-03 17:03:31 +00:00
for ( j = 0 ; j < g . perLine ; j + + ) {
2010-04-26 15:44:07 +00:00
if ( i + j > nByte ) {
fprintf ( stdout , " " ) ;
} else {
2015-10-29 13:48:15 +00:00
fprintf ( stdout , " %c " , ISPRINT ( aData [ i + j ] ) ? aData [ i + j ] : ' . ' ) ;
2010-04-26 15:44:07 +00:00
}
2002-07-06 16:32:14 +00:00
}
fprintf ( stdout , " \n " ) ;
}
2010-04-26 15:44:07 +00:00
return aData ;
}
/*
* * Print an entire page of content as hex
*/
2020-07-22 11:42:50 +00:00
static void print_page ( u32 iPg ) {
i64 iStart ;
2010-04-26 15:44:07 +00:00
unsigned char * aData ;
2020-07-22 11:42:50 +00:00
iStart = ( ( i64 ) ( iPg - 1 ) ) * g . pagesize ;
fprintf ( stdout , " Page %u: (offsets 0x%llx..0x%llx) \n " ,
2015-08-03 17:03:31 +00:00
iPg , iStart , iStart + g . pagesize - 1 ) ;
aData = print_byte_range ( iStart , g . pagesize , 0 ) ;
sqlite3_free ( aData ) ;
2002-07-06 16:32:14 +00:00
}
2014-06-19 23:38:53 +00:00
2020-07-22 11:42:50 +00:00
/* Print a line of decoded output showing a 4-byte unsigned integer.
2010-04-26 15:44:07 +00:00
*/
2014-01-28 20:36:22 +00:00
static void print_decode_line (
2010-04-26 15:44:07 +00:00
unsigned char * aData , /* Content being decoded */
int ofst , int nByte , /* Start and size of decode */
const char * zMsg /* Message to append */
) {
int i , j ;
2020-07-22 11:42:50 +00:00
u32 val = aData [ ofst ] ;
2010-04-26 15:44:07 +00:00
char zBuf [ 100 ] ;
sprintf ( zBuf , " %03x: %02x " , ofst , aData [ ofst ] ) ;
2014-07-19 15:40:39 +00:00
i = ( int ) strlen ( zBuf ) ;
2010-04-26 15:44:07 +00:00
for ( j = 1 ; j < 4 ; j + + ) {
if ( j > = nByte ) {
sprintf ( & zBuf [ i ] , " " ) ;
} else {
sprintf ( & zBuf [ i ] , " %02x " , aData [ ofst + j ] ) ;
val = val * 256 + aData [ ofst + j ] ;
}
2014-07-19 15:40:39 +00:00
i + = ( int ) strlen ( & zBuf [ i ] ) ;
2010-04-26 15:44:07 +00:00
}
2020-07-22 11:42:50 +00:00
sprintf ( & zBuf [ i ] , " %10u " , val ) ;
2010-04-26 16:47:12 +00:00
printf ( " %s %s \n " , zBuf , zMsg ) ;
2010-04-26 15:44:07 +00:00
}
/*
* * Decode the database header .
*/
2010-04-26 16:47:12 +00:00
static void print_db_header ( void ) {
2010-04-26 15:44:07 +00:00
unsigned char * aData ;
aData = print_byte_range ( 0 , 100 , 0 ) ;
printf ( " Decoded: \n " ) ;
print_decode_line ( aData , 16 , 2 , " Database page size " ) ;
print_decode_line ( aData , 18 , 1 , " File format write version " ) ;
print_decode_line ( aData , 19 , 1 , " File format read version " ) ;
print_decode_line ( aData , 20 , 1 , " Reserved space at end of page " ) ;
print_decode_line ( aData , 24 , 4 , " File change counter " ) ;
print_decode_line ( aData , 28 , 4 , " Size of database in pages " ) ;
print_decode_line ( aData , 32 , 4 , " Page number of first freelist page " ) ;
print_decode_line ( aData , 36 , 4 , " Number of freelist pages " ) ;
print_decode_line ( aData , 40 , 4 , " Schema cookie " ) ;
print_decode_line ( aData , 44 , 4 , " Schema format version " ) ;
print_decode_line ( aData , 48 , 4 , " Default page cache size " ) ;
print_decode_line ( aData , 52 , 4 , " Largest auto-vac root page " ) ;
print_decode_line ( aData , 56 , 4 , " Text encoding " ) ;
print_decode_line ( aData , 60 , 4 , " User version " ) ;
print_decode_line ( aData , 64 , 4 , " Incremental-vacuum mode " ) ;
2013-05-01 19:49:27 +00:00
print_decode_line ( aData , 68 , 4 , " Application ID " ) ;
2010-04-26 15:44:07 +00:00
print_decode_line ( aData , 72 , 4 , " meta[8] " ) ;
print_decode_line ( aData , 76 , 4 , " meta[9] " ) ;
print_decode_line ( aData , 80 , 4 , " meta[10] " ) ;
print_decode_line ( aData , 84 , 4 , " meta[11] " ) ;
print_decode_line ( aData , 88 , 4 , " meta[12] " ) ;
2010-06-17 02:13:39 +00:00
print_decode_line ( aData , 92 , 4 , " Change counter for version number " ) ;
2010-04-26 17:30:52 +00:00
print_decode_line ( aData , 96 , 4 , " SQLite version number " ) ;
2020-07-22 11:42:50 +00:00
sqlite3_free ( aData ) ;
2010-04-26 15:44:07 +00:00
}
2011-01-05 21:20:52 +00:00
/*
* * Describe cell content .
*/
2014-07-18 21:16:37 +00:00
static i64 describeContent (
2011-01-06 01:26:38 +00:00
unsigned char * a , /* Cell content */
2014-07-18 21:16:37 +00:00
i64 nLocal , /* Bytes in a[] */
2011-01-06 01:26:38 +00:00
char * zDesc /* Write description here */
2011-01-05 21:20:52 +00:00
) {
2014-07-18 21:16:37 +00:00
i64 nDesc = 0 ;
int n , j ;
i64 i , x , v ;
2011-01-05 21:20:52 +00:00
const unsigned char * pData ;
2011-01-06 01:26:38 +00:00
const unsigned char * pLimit ;
2011-01-05 21:20:52 +00:00
char sep = ' ' ;
2011-01-06 01:26:38 +00:00
pLimit = & a [ nLocal ] ;
2011-01-05 21:20:52 +00:00
n = decodeVarint ( a , & x ) ;
pData = & a [ x ] ;
a + = n ;
i = x - n ;
2011-01-06 01:26:38 +00:00
while ( i > 0 & & pData < = pLimit ) {
2011-01-05 21:20:52 +00:00
n = decodeVarint ( a , & x ) ;
a + = n ;
i - = n ;
2011-01-06 01:26:38 +00:00
nLocal - = n ;
2011-01-05 21:20:52 +00:00
zDesc [ 0 ] = sep ;
sep = ' , ' ;
nDesc + + ;
zDesc + + ;
if ( x = = 0 ) {
2011-01-06 01:26:38 +00:00
sprintf ( zDesc , " * " ) ; /* NULL is a "*" */
2011-01-05 21:20:52 +00:00
} else if ( x > = 1 & & x < = 6 ) {
v = ( signed char ) pData [ 0 ] ;
pData + + ;
switch ( x ) {
case 6 : v = ( v < < 16 ) + ( pData [ 0 ] < < 8 ) + pData [ 1 ] ; pData + = 2 ;
case 5 : v = ( v < < 16 ) + ( pData [ 0 ] < < 8 ) + pData [ 1 ] ; pData + = 2 ;
case 4 : v = ( v < < 8 ) + pData [ 0 ] ; pData + + ;
case 3 : v = ( v < < 8 ) + pData [ 0 ] ; pData + + ;
case 2 : v = ( v < < 8 ) + pData [ 0 ] ; pData + + ;
}
sprintf ( zDesc , " %lld " , v ) ;
} else if ( x = = 7 ) {
sprintf ( zDesc , " real " ) ;
pData + = 8 ;
} else if ( x = = 8 ) {
sprintf ( zDesc , " 0 " ) ;
} else if ( x = = 9 ) {
sprintf ( zDesc , " 1 " ) ;
} else if ( x > = 12 ) {
2014-07-18 21:16:37 +00:00
i64 size = ( x - 12 ) / 2 ;
2011-01-05 21:46:52 +00:00
if ( ( x & 1 ) = = 0 ) {
2014-07-19 15:30:01 +00:00
sprintf ( zDesc , " blob(%lld) " , size ) ;
2011-01-05 21:20:52 +00:00
} else {
2014-07-19 15:30:01 +00:00
sprintf ( zDesc , " txt(%lld) " , size ) ;
2011-01-05 21:20:52 +00:00
}
pData + = size ;
}
2014-07-19 15:40:39 +00:00
j = ( int ) strlen ( zDesc ) ;
2011-01-05 21:20:52 +00:00
zDesc + = j ;
nDesc + = j ;
}
return nDesc ;
}
2011-01-06 01:26:38 +00:00
/*
* * Compute the local payload size given the total payload size and
* * the page size .
*/
2014-07-18 21:16:37 +00:00
static i64 localPayload ( i64 nPayload , char cType ) {
i64 maxLocal ;
i64 minLocal ;
i64 surplus ;
i64 nLocal ;
2011-01-06 01:26:38 +00:00
if ( cType = = 13 ) {
/* Table leaf */
2015-08-03 17:03:31 +00:00
maxLocal = g . pagesize - 35 ;
minLocal = ( g . pagesize - 12 ) * 32 / 255 - 23 ;
2011-01-06 01:26:38 +00:00
} else {
2015-08-03 17:03:31 +00:00
maxLocal = ( g . pagesize - 12 ) * 64 / 255 - 23 ;
minLocal = ( g . pagesize - 12 ) * 32 / 255 - 23 ;
2011-01-06 01:26:38 +00:00
}
if ( nPayload > maxLocal ) {
2015-08-03 17:03:31 +00:00
surplus = minLocal + ( nPayload - minLocal ) % ( g . pagesize - 4 ) ;
2011-01-06 01:26:38 +00:00
if ( surplus < = maxLocal ) {
nLocal = surplus ;
} else {
nLocal = minLocal ;
}
} else {
nLocal = nPayload ;
}
return nLocal ;
}
2011-01-05 21:20:52 +00:00
2010-04-26 16:47:12 +00:00
/*
* * Create a description for a single cell .
2011-01-06 01:26:38 +00:00
* *
* * The return value is the local cell size .
2010-04-26 16:47:12 +00:00
*/
2014-07-18 21:16:37 +00:00
static i64 describeCell (
2011-01-05 21:20:52 +00:00
unsigned char cType , /* Page type */
unsigned char * a , /* Cell content */
int showCellContent , /* Show cell content if true */
char * * pzDesc /* Store description here */
) {
2010-04-26 16:47:12 +00:00
int i ;
2014-07-18 21:16:37 +00:00
i64 nDesc = 0 ;
2010-04-26 16:47:12 +00:00
int n = 0 ;
2020-07-22 11:42:50 +00:00
u32 leftChild ;
2010-04-26 16:47:12 +00:00
i64 nPayload ;
i64 rowid ;
2014-07-18 21:16:37 +00:00
i64 nLocal ;
2011-01-05 21:20:52 +00:00
static char zDesc [ 1000 ] ;
2010-04-26 16:47:12 +00:00
i = 0 ;
if ( cType < = 5 ) {
leftChild = ( ( a [ 0 ] * 256 + a [ 1 ] ) * 256 + a [ 2 ] ) * 256 + a [ 3 ] ;
a + = 4 ;
n + = 4 ;
2020-07-22 11:42:50 +00:00
sprintf ( zDesc , " lx: %u " , leftChild ) ;
2010-04-26 16:47:12 +00:00
nDesc = strlen ( zDesc ) ;
}
if ( cType ! = 5 ) {
i = decodeVarint ( a , & nPayload ) ;
a + = i ;
n + = i ;
2011-01-05 21:20:52 +00:00
sprintf ( & zDesc [ nDesc ] , " n: %lld " , nPayload ) ;
2010-04-26 16:47:12 +00:00
nDesc + = strlen ( & zDesc [ nDesc ] ) ;
2011-01-06 01:26:38 +00:00
nLocal = localPayload ( nPayload , cType ) ;
} else {
nPayload = nLocal = 0 ;
2010-04-26 16:47:12 +00:00
}
if ( cType = = 5 | | cType = = 13 ) {
i = decodeVarint ( a , & rowid ) ;
a + = i ;
n + = i ;
2011-01-05 21:20:52 +00:00
sprintf ( & zDesc [ nDesc ] , " r: %lld " , rowid ) ;
2010-04-26 16:47:12 +00:00
nDesc + = strlen ( & zDesc [ nDesc ] ) ;
}
2011-01-06 15:51:18 +00:00
if ( nLocal < nPayload ) {
2020-07-22 11:42:50 +00:00
u32 ovfl ;
2011-01-06 15:51:18 +00:00
unsigned char * b = & a [ nLocal ] ;
ovfl = ( ( b [ 0 ] * 256 + b [ 1 ] ) * 256 + b [ 2 ] ) * 256 + b [ 3 ] ;
2020-07-22 11:42:50 +00:00
sprintf ( & zDesc [ nDesc ] , " ov: %u " , ovfl ) ;
2011-01-06 15:51:18 +00:00
nDesc + = strlen ( & zDesc [ nDesc ] ) ;
n + = 4 ;
}
2011-01-05 21:20:52 +00:00
if ( showCellContent & & cType ! = 5 ) {
2011-01-06 01:26:38 +00:00
nDesc + = describeContent ( a , nLocal , & zDesc [ nDesc - 1 ] ) ;
2011-01-05 21:20:52 +00:00
}
2010-04-26 16:47:12 +00:00
* pzDesc = zDesc ;
2011-01-06 01:26:38 +00:00
return nLocal + n ;
2010-04-26 16:47:12 +00:00
}
2014-06-19 23:38:53 +00:00
/* Print an offset followed by nByte bytes. Add extra white-space
* * at the end so that subsequent text is aligned .
*/
static void printBytes (
unsigned char * aData , /* Content being decoded */
unsigned char * aStart , /* Start of content to be printed */
int nByte /* Number of bytes to print */
) {
int j ;
printf ( " %03x: " , ( int ) ( aStart - aData ) ) ;
for ( j = 0 ; j < 9 ; j + + ) {
if ( j > = nByte ) {
printf ( " " ) ;
} else {
printf ( " %02x " , aStart [ j ] ) ;
}
}
}
/*
* * Write a full decode on stdout for the cell at a [ ofst ] .
* * Assume the page contains a header of size szPgHdr bytes .
*/
static void decodeCell (
unsigned char * a , /* Page content (without the page-1 header) */
unsigned pgno , /* Page number */
int iCell , /* Cell index */
int szPgHdr , /* Size of the page header. 0 or 100 */
int ofst /* Cell begins at a[ofst] */
) {
2015-03-21 02:22:37 +00:00
int i , j = 0 ;
2020-07-22 11:42:50 +00:00
u32 leftChild ;
2014-07-18 21:16:37 +00:00
i64 k ;
2014-06-19 23:38:53 +00:00
i64 nPayload ;
i64 rowid ;
i64 nHdr ;
i64 iType ;
2014-07-18 21:16:37 +00:00
i64 nLocal ;
2014-06-19 23:38:53 +00:00
unsigned char * x = a + ofst ;
unsigned char * end ;
unsigned char cType = a [ 0 ] ;
2014-06-20 01:32:42 +00:00
int nCol = 0 ;
int szCol [ 2000 ] ;
int ofstCol [ 2000 ] ;
int typeCol [ 2000 ] ;
2014-06-19 23:38:53 +00:00
2014-06-20 01:32:42 +00:00
printf ( " Cell[%d]: \n " , iCell ) ;
2014-06-19 23:38:53 +00:00
if ( cType < = 5 ) {
leftChild = ( ( x [ 0 ] * 256 + x [ 1 ] ) * 256 + x [ 2 ] ) * 256 + x [ 3 ] ;
printBytes ( a , x , 4 ) ;
2020-07-22 11:42:50 +00:00
printf ( " left child page:: %u \n " , leftChild ) ;
2014-06-19 23:38:53 +00:00
x + = 4 ;
}
if ( cType ! = 5 ) {
i = decodeVarint ( x , & nPayload ) ;
printBytes ( a , x , i ) ;
nLocal = localPayload ( nPayload , cType ) ;
2014-06-20 01:32:42 +00:00
if ( nLocal = = nPayload ) {
2014-07-19 15:30:01 +00:00
printf ( " payload-size: %lld \n " , nPayload ) ;
2014-06-20 01:32:42 +00:00
} else {
2014-07-19 15:30:01 +00:00
printf ( " payload-size: %lld (%lld local, %lld overflow) \n " ,
nPayload , nLocal , nPayload - nLocal ) ;
2014-06-20 01:32:42 +00:00
}
2014-06-19 23:38:53 +00:00
x + = i ;
} else {
nPayload = nLocal = 0 ;
}
end = x + nLocal ;
if ( cType = = 5 | | cType = = 13 ) {
i = decodeVarint ( x , & rowid ) ;
printBytes ( a , x , i ) ;
printf ( " rowid: %lld \n " , rowid ) ;
x + = i ;
}
if ( nLocal > 0 ) {
i = decodeVarint ( x , & nHdr ) ;
printBytes ( a , x , i ) ;
2014-06-20 01:32:42 +00:00
printf ( " record-header-size: %d \n " , ( int ) nHdr ) ;
2014-06-19 23:38:53 +00:00
j = i ;
2014-06-20 01:32:42 +00:00
nCol = 0 ;
k = nHdr ;
2017-03-16 13:14:03 +00:00
while ( x + j < = end & & j < nHdr ) {
2014-06-19 23:38:53 +00:00
const char * zTypeName ;
int sz = 0 ;
char zNm [ 30 ] ;
i = decodeVarint ( x + j , & iType ) ;
printBytes ( a , x + j , i ) ;
2014-06-20 01:32:42 +00:00
printf ( " typecode[%d]: %d - " , nCol , ( int ) iType ) ;
2014-06-19 23:38:53 +00:00
switch ( iType ) {
case 0 : zTypeName = " NULL " ; sz = 0 ; break ;
case 1 : zTypeName = " int8 " ; sz = 1 ; break ;
case 2 : zTypeName = " int16 " ; sz = 2 ; break ;
case 3 : zTypeName = " int24 " ; sz = 3 ; break ;
case 4 : zTypeName = " int32 " ; sz = 4 ; break ;
case 5 : zTypeName = " int48 " ; sz = 6 ; break ;
case 6 : zTypeName = " int64 " ; sz = 8 ; break ;
case 7 : zTypeName = " double " ; sz = 8 ; break ;
case 8 : zTypeName = " zero " ; sz = 0 ; break ;
case 9 : zTypeName = " one " ; sz = 0 ; break ;
case 10 :
case 11 : zTypeName = " error " ; sz = 0 ; break ;
default : {
sz = ( int ) ( iType - 12 ) / 2 ;
sprintf ( zNm , ( iType & 1 ) = = 0 ? " blob(%d) " : " text(%d) " , sz ) ;
zTypeName = zNm ;
break ;
}
}
printf ( " %s \n " , zTypeName ) ;
2014-06-20 01:32:42 +00:00
szCol [ nCol ] = sz ;
2014-07-18 21:16:37 +00:00
ofstCol [ nCol ] = ( int ) k ;
2014-06-20 01:32:42 +00:00
typeCol [ nCol ] = ( int ) iType ;
k + = sz ;
nCol + + ;
2014-06-19 23:38:53 +00:00
j + = i ;
}
2014-06-20 01:32:42 +00:00
for ( i = 0 ; i < nCol & & ofstCol [ i ] + szCol [ i ] < = nLocal ; i + + ) {
int s = ofstCol [ i ] ;
i64 v ;
const unsigned char * pData ;
if ( szCol [ i ] = = 0 ) continue ;
printBytes ( a , x + s , szCol [ i ] ) ;
printf ( " data[%d]: " , i ) ;
pData = x + s ;
if ( typeCol [ i ] < = 7 ) {
v = ( signed char ) pData [ 0 ] ;
for ( k = 1 ; k < szCol [ i ] ; k + + ) {
v = ( v < < 8 ) + pData [ k ] ;
}
if ( typeCol [ i ] = = 7 ) {
double r ;
memcpy ( & r , & v , sizeof ( r ) ) ;
printf ( " %#g \n " , r ) ;
} else {
printf ( " %lld \n " , v ) ;
}
} else {
2014-06-20 13:55:06 +00:00
int ii , jj ;
char zConst [ 32 ] ;
if ( ( typeCol [ i ] & 1 ) = = 0 ) {
zConst [ 0 ] = ' x ' ;
zConst [ 1 ] = ' \' ' ;
for ( ii = 2 , jj = 0 ; jj < szCol [ i ] & & ii < 24 ; jj + + , ii + = 2 ) {
sprintf ( zConst + ii , " %02x " , pData [ jj ] ) ;
}
} else {
zConst [ 0 ] = ' \' ' ;
for ( ii = 1 , jj = 0 ; jj < szCol [ i ] & & ii < 24 ; jj + + , ii + + ) {
2015-10-29 13:48:15 +00:00
zConst [ ii ] = ISPRINT ( pData [ jj ] ) ? pData [ jj ] : ' . ' ;
2014-06-20 13:55:06 +00:00
}
zConst [ ii ] = 0 ;
}
if ( jj < szCol [ i ] ) {
memcpy ( zConst + ii , " ...' " , 5 ) ;
} else {
memcpy ( zConst + ii , " ' " , 2 ) ;
}
printf ( " %s \n " , zConst ) ;
2014-06-20 01:32:42 +00:00
}
j = ofstCol [ i ] + szCol [ i ] ;
}
2014-06-19 23:38:53 +00:00
}
if ( j < nLocal ) {
printBytes ( a , x + j , 0 ) ;
2014-07-19 15:30:01 +00:00
printf ( " ... %lld bytes of content ... \n " , nLocal - j ) ;
2014-06-19 23:38:53 +00:00
}
if ( nLocal < nPayload ) {
printBytes ( a , x + nLocal , 4 ) ;
2020-07-22 11:42:50 +00:00
printf ( " overflow-page: %u \n " , decodeInt32 ( x + nLocal ) ) ;
2014-06-19 23:38:53 +00:00
}
}
2010-04-26 16:47:12 +00:00
/*
* * Decode a btree page
*/
2011-01-05 21:20:52 +00:00
static void decode_btree_page (
unsigned char * a , /* Page content */
int pgno , /* Page number */
int hdrSize , /* Size of the page header. 0 or 100 */
char * zArgs /* Flags to control formatting */
) {
2010-04-26 16:47:12 +00:00
const char * zType = " unknown " ;
int nCell ;
2011-01-06 01:26:38 +00:00
int i , j ;
2010-04-26 16:47:12 +00:00
int iCellPtr ;
2011-01-05 21:20:52 +00:00
int showCellContent = 0 ;
2011-01-06 01:26:38 +00:00
int showMap = 0 ;
2014-06-19 23:38:53 +00:00
int cellToDecode = - 2 ;
2011-01-06 01:26:38 +00:00
char * zMap = 0 ;
2010-04-26 16:47:12 +00:00
switch ( a [ 0 ] ) {
case 2 : zType = " index interior node " ; break ;
case 5 : zType = " table interior node " ; break ;
case 10 : zType = " index leaf " ; break ;
case 13 : zType = " table leaf " ; break ;
}
2011-01-05 21:20:52 +00:00
while ( zArgs [ 0 ] ) {
switch ( zArgs [ 0 ] ) {
case ' c ' : showCellContent = 1 ; break ;
2011-01-06 01:26:38 +00:00
case ' m ' : showMap = 1 ; break ;
2014-06-19 23:38:53 +00:00
case ' d ' : {
2015-10-29 13:48:15 +00:00
if ( ! ISDIGIT ( zArgs [ 1 ] ) ) {
2014-06-19 23:38:53 +00:00
cellToDecode = - 1 ;
} else {
cellToDecode = 0 ;
2015-10-29 13:48:15 +00:00
while ( ISDIGIT ( zArgs [ 1 ] ) ) {
2014-06-19 23:38:53 +00:00
zArgs + + ;
cellToDecode = cellToDecode * 10 + zArgs [ 0 ] - ' 0 ' ;
}
}
break ;
}
2011-01-05 21:20:52 +00:00
}
zArgs + + ;
}
2010-04-26 16:47:12 +00:00
nCell = a [ 3 ] * 256 + a [ 4 ] ;
2014-06-19 23:38:53 +00:00
iCellPtr = ( a [ 0 ] = = 2 | | a [ 0 ] = = 5 ) ? 12 : 8 ;
2014-06-20 01:32:42 +00:00
if ( cellToDecode > = nCell ) {
2014-06-19 23:38:53 +00:00
printf ( " Page %d has only %d cells \n " , pgno , nCell ) ;
return ;
2011-01-06 01:26:38 +00:00
}
2014-06-20 01:32:42 +00:00
printf ( " Header on btree page %d: \n " , pgno ) ;
print_decode_line ( a , 0 , 1 , zType ) ;
print_decode_line ( a , 1 , 2 , " Offset to first freeblock " ) ;
print_decode_line ( a , 3 , 2 , " Number of cells on this page " ) ;
print_decode_line ( a , 5 , 2 , " Offset to cell content area " ) ;
print_decode_line ( a , 7 , 1 , " Fragmented byte count " ) ;
if ( a [ 0 ] = = 2 | | a [ 0 ] = = 5 ) {
print_decode_line ( a , 8 , 4 , " Right child " ) ;
}
if ( cellToDecode = = ( - 2 ) & & nCell > 0 ) {
printf ( " key: lx=left-child n=payload-size r=rowid \n " ) ;
}
2011-01-06 01:26:38 +00:00
if ( showMap ) {
2015-08-03 17:03:31 +00:00
zMap = sqlite3_malloc ( g . pagesize ) ;
memset ( zMap , ' . ' , g . pagesize ) ;
2011-01-06 01:26:38 +00:00
memset ( zMap , ' 1 ' , hdrSize ) ;
memset ( & zMap [ hdrSize ] , ' H ' , iCellPtr ) ;
memset ( & zMap [ hdrSize + iCellPtr ] , ' P ' , 2 * nCell ) ;
}
2010-04-26 16:47:12 +00:00
for ( i = 0 ; i < nCell ; i + + ) {
int cofst = iCellPtr + i * 2 ;
char * zDesc ;
2014-07-18 21:16:37 +00:00
i64 n ;
2011-01-06 01:26:38 +00:00
2010-04-26 16:47:12 +00:00
cofst = a [ cofst ] * 256 + a [ cofst + 1 ] ;
2011-01-06 01:26:38 +00:00
n = describeCell ( a [ 0 ] , & a [ cofst - hdrSize ] , showCellContent , & zDesc ) ;
if ( showMap ) {
char zBuf [ 30 ] ;
2014-07-18 21:16:37 +00:00
memset ( & zMap [ cofst ] , ' * ' , ( size_t ) n ) ;
2011-01-06 01:26:38 +00:00
zMap [ cofst ] = ' [ ' ;
zMap [ cofst + n - 1 ] = ' ] ' ;
sprintf ( zBuf , " %d " , i ) ;
2014-07-19 15:40:39 +00:00
j = ( int ) strlen ( zBuf ) ;
2011-01-06 01:26:38 +00:00
if ( j < = n - 2 ) memcpy ( & zMap [ cofst + 1 ] , zBuf , j ) ;
}
2014-06-19 23:38:53 +00:00
if ( cellToDecode = = ( - 2 ) ) {
printf ( " %03x: cell[%d] %s \n " , cofst , i , zDesc ) ;
} else if ( cellToDecode = = ( - 1 ) | | cellToDecode = = i ) {
decodeCell ( a , pgno , i , hdrSize , cofst - hdrSize ) ;
}
2010-04-26 16:47:12 +00:00
}
2011-01-06 01:26:38 +00:00
if ( showMap ) {
2014-06-20 01:32:42 +00:00
printf ( " Page map: (H=header P=cell-index 1=page-1-header .=free-space) \n " ) ;
2021-12-31 18:26:50 +00:00
for ( i = 0 ; ( u32 ) i < g . pagesize ; i + = 64 ) {
2011-01-06 01:26:38 +00:00
printf ( " %03x: %.64s \n " , i , & zMap [ i ] ) ;
}
2015-08-03 17:03:31 +00:00
sqlite3_free ( zMap ) ;
2014-06-19 23:38:53 +00:00
}
2010-04-26 16:47:12 +00:00
}
2010-08-23 15:26:49 +00:00
/*
* * Decode a freelist trunk page .
*/
static void decode_trunk_page (
2020-07-22 11:42:50 +00:00
u32 pgno , /* The page number */
2010-08-23 15:26:49 +00:00
int detail , /* Show leaf pages if true */
int recursive /* Follow the trunk change if true */
) {
2020-07-22 11:42:50 +00:00
u32 i ;
u32 n ;
2010-08-23 15:26:49 +00:00
unsigned char * a ;
while ( pgno > 0 ) {
2015-08-03 17:03:31 +00:00
a = fileRead ( ( pgno - 1 ) * g . pagesize , g . pagesize ) ;
2010-08-23 15:26:49 +00:00
printf ( " Decode of freelist trunk page %d: \n " , pgno ) ;
print_decode_line ( a , 0 , 4 , " Next freelist trunk page " ) ;
print_decode_line ( a , 4 , 4 , " Number of entries on this page " ) ;
if ( detail ) {
2020-07-22 11:42:50 +00:00
n = decodeInt32 ( & a [ 4 ] ) ;
for ( i = 0 ; i < n & & i < g . pagesize / 4 ; i + + ) {
u32 x = decodeInt32 ( & a [ 8 + 4 * i ] ) ;
2021-06-16 13:26:40 +00:00
char zIdx [ 13 ] ;
2010-08-23 15:26:49 +00:00
sprintf ( zIdx , " [%d] " , i ) ;
printf ( " %5s %7u " , zIdx , x ) ;
if ( i % 5 = = 4 ) printf ( " \n " ) ;
}
if ( i % 5 ! = 0 ) printf ( " \n " ) ;
}
if ( ! recursive ) {
pgno = 0 ;
} else {
2020-07-22 11:42:50 +00:00
pgno = decodeInt32 ( & a [ 0 ] ) ;
2010-08-23 15:26:49 +00:00
}
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2010-08-23 15:26:49 +00:00
}
}
2012-04-03 14:59:50 +00:00
/*
* * A short text comment on the use of each page .
*/
static char * * zPageUse ;
/*
* * Add a comment on the use of a page .
*/
2020-07-22 11:42:50 +00:00
static void page_usage_msg ( u32 pgno , const char * zFormat , . . . ) {
2012-04-03 14:59:50 +00:00
va_list ap ;
char * zMsg ;
va_start ( ap , zFormat ) ;
zMsg = sqlite3_vmprintf ( zFormat , ap ) ;
va_end ( ap ) ;
2015-08-03 17:03:31 +00:00
if ( pgno < = 0 | | pgno > g . mxPage ) {
2020-07-22 11:42:50 +00:00
printf ( " ERROR: page %d out of range 1..%u: %s \n " ,
2015-08-03 17:03:31 +00:00
pgno , g . mxPage , zMsg ) ;
2012-04-03 14:59:50 +00:00
sqlite3_free ( zMsg ) ;
return ;
}
if ( zPageUse [ pgno ] ! = 0 ) {
printf ( " ERROR: page %d used multiple times: \n " , pgno ) ;
printf ( " ERROR: previous: %s \n " , zPageUse [ pgno ] ) ;
2013-02-19 20:25:16 +00:00
printf ( " ERROR: current: %s \n " , zMsg ) ;
2012-04-03 14:59:50 +00:00
sqlite3_free ( zPageUse [ pgno ] ) ;
}
zPageUse [ pgno ] = zMsg ;
}
/*
* * Find overflow pages of a cell and describe their usage .
*/
static void page_usage_cell (
unsigned char cType , /* Page type */
unsigned char * a , /* Cell content */
2020-07-22 11:42:50 +00:00
u32 pgno , /* page containing the cell */
2012-04-03 14:59:50 +00:00
int cellno /* Index of the cell on the page */
) {
int i ;
int n = 0 ;
i64 nPayload ;
i64 rowid ;
2014-07-18 21:16:37 +00:00
i64 nLocal ;
2012-04-03 14:59:50 +00:00
i = 0 ;
if ( cType < = 5 ) {
a + = 4 ;
n + = 4 ;
}
if ( cType ! = 5 ) {
i = decodeVarint ( a , & nPayload ) ;
a + = i ;
n + = i ;
nLocal = localPayload ( nPayload , cType ) ;
} else {
nPayload = nLocal = 0 ;
}
if ( cType = = 5 | | cType = = 13 ) {
i = decodeVarint ( a , & rowid ) ;
a + = i ;
n + = i ;
}
if ( nLocal < nPayload ) {
2020-07-22 11:42:50 +00:00
u32 ovfl = decodeInt32 ( a + nLocal ) ;
u32 cnt = 0 ;
2015-08-03 17:03:31 +00:00
while ( ovfl & & ( cnt + + ) < g . mxPage ) {
2020-07-22 11:42:50 +00:00
page_usage_msg ( ovfl , " overflow %d from cell %d of page %u " ,
2012-04-03 14:59:50 +00:00
cnt , cellno , pgno ) ;
2019-03-05 23:49:17 +00:00
a = fileRead ( ( ovfl - 1 ) * ( sqlite3_int64 ) g . pagesize , 4 ) ;
2012-04-03 14:59:50 +00:00
ovfl = decodeInt32 ( a ) ;
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2012-04-03 14:59:50 +00:00
}
}
}
2019-04-17 12:29:45 +00:00
/*
* * True if the memory is all zeros
*/
static int allZero ( unsigned char * a , int n ) {
while ( n & & ( a + + ) [ 0 ] = = 0 ) { n - - ; }
return n = = 0 ;
}
2012-04-03 14:59:50 +00:00
/*
2019-04-17 12:29:45 +00:00
* * Describe the usages of a b - tree page .
* *
* * If parent = = 0 , then this is the root of a btree . If parent < 0 then
* * this is an orphan page .
2012-04-03 14:59:50 +00:00
*/
static void page_usage_btree (
2020-07-22 11:42:50 +00:00
u32 pgno , /* Page to describe */
2021-12-31 18:26:50 +00:00
int parent , /* Parent of this page. 0 for root pages */
2012-04-03 14:59:50 +00:00
int idx , /* Which child of the parent */
const char * zName /* Name of the table */
) {
unsigned char * a ;
const char * zType = " corrupt node " ;
int nCell ;
int i ;
int hdr = pgno = = 1 ? 100 : 0 ;
2019-04-17 13:23:28 +00:00
char zEntry [ 30 ] ;
2012-04-03 14:59:50 +00:00
2015-08-03 17:03:31 +00:00
if ( pgno < = 0 | | pgno > g . mxPage ) return ;
a = fileRead ( ( pgno - 1 ) * g . pagesize , g . pagesize ) ;
2012-04-03 14:59:50 +00:00
switch ( a [ hdr ] ) {
2019-04-17 12:29:45 +00:00
case 0 : {
if ( allZero ( a , g . pagesize ) ) {
zType = " zeroed page " ;
} else if ( parent < 0 ) {
return ;
} else {
zType = " corrupt node " ;
}
break ;
}
2012-04-03 14:59:50 +00:00
case 2 : zType = " interior node of index " ; break ;
case 5 : zType = " interior node of table " ; break ;
case 10 : zType = " leaf of index " ; break ;
case 13 : zType = " leaf of table " ; break ;
2019-04-17 12:29:45 +00:00
default : {
if ( parent < 0 ) return ;
zType = " corrupt node " ;
}
2012-04-03 14:59:50 +00:00
}
2019-04-17 13:23:28 +00:00
nCell = a [ hdr + 3 ] * 256 + a [ hdr + 4 ] ;
if ( nCell = = 1 ) {
sqlite3_snprintf ( sizeof ( zEntry ) , zEntry , " 1 row " ) ;
} else {
sqlite3_snprintf ( sizeof ( zEntry ) , zEntry , " %d rows " , nCell ) ;
}
2019-04-17 12:29:45 +00:00
if ( parent > 0 ) {
2019-04-17 13:23:28 +00:00
page_usage_msg ( pgno , " %s [%s], child %d of page %d, %s " ,
zType , zName , idx , parent , zEntry ) ;
2019-04-17 12:29:45 +00:00
} else if ( parent = = 0 ) {
2019-04-17 13:23:28 +00:00
page_usage_msg ( pgno , " root %s [%s], %s " , zType , zName , zEntry ) ;
2019-04-17 12:29:45 +00:00
} else {
2019-04-17 13:23:28 +00:00
page_usage_msg ( pgno , " orphaned %s, %s " , zType , zEntry ) ;
2012-04-03 14:59:50 +00:00
}
if ( a [ hdr ] = = 2 | | a [ hdr ] = = 5 ) {
int cellstart = hdr + 12 ;
2020-07-22 11:42:50 +00:00
u32 child ;
2012-04-03 14:59:50 +00:00
for ( i = 0 ; i < nCell ; i + + ) {
2021-04-26 23:57:02 +00:00
u32 cellidx ;
2020-07-22 11:42:50 +00:00
u32 ofst ;
2012-04-03 14:59:50 +00:00
2021-04-26 23:57:02 +00:00
cellidx = cellstart + i * 2 ;
if ( cellidx + 1 > = g . pagesize ) {
2021-04-27 00:05:03 +00:00
printf ( " ERROR: page %d too many cells (%d) \n " , pgno , nCell ) ;
2021-04-26 23:57:02 +00:00
break ;
}
ofst = a [ cellidx ] * 256 + a [ cellidx + 1 ] ;
if ( ofst < cellidx + 2 | | ofst + 4 > = g . pagesize ) {
2021-04-27 00:05:03 +00:00
printf ( " ERROR: page %d cell %d out of bounds \n " , pgno , i ) ;
2021-04-26 23:57:02 +00:00
continue ;
}
2012-04-03 14:59:50 +00:00
child = decodeInt32 ( a + ofst ) ;
page_usage_btree ( child , pgno , i , zName ) ;
}
child = decodeInt32 ( a + cellstart - 4 ) ;
page_usage_btree ( child , pgno , i , zName ) ;
}
if ( a [ hdr ] = = 2 | | a [ hdr ] = = 10 | | a [ hdr ] = = 13 ) {
int cellstart = hdr + 8 + 4 * ( a [ hdr ] < = 5 ) ;
for ( i = 0 ; i < nCell ; i + + ) {
int ofst ;
ofst = cellstart + i * 2 ;
ofst = a [ ofst ] * 256 + a [ ofst + 1 ] ;
page_usage_cell ( a [ hdr ] , a + ofst , pgno , i ) ;
}
}
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2012-04-03 14:59:50 +00:00
}
/*
* * Determine page usage by the freelist
*/
2020-07-22 11:42:50 +00:00
static void page_usage_freelist ( u32 pgno ) {
2012-04-03 14:59:50 +00:00
unsigned char * a ;
int cnt = 0 ;
int i ;
int n ;
int iNext ;
int parent = 1 ;
2021-12-31 18:26:50 +00:00
while ( pgno > 0 & & pgno < = g . mxPage & & ( u32 ) ( cnt + + ) < g . mxPage ) {
2012-04-03 14:59:50 +00:00
page_usage_msg ( pgno , " freelist trunk #%d child of %d " , cnt , parent ) ;
2015-08-03 17:03:31 +00:00
a = fileRead ( ( pgno - 1 ) * g . pagesize , g . pagesize ) ;
2012-04-03 14:59:50 +00:00
iNext = decodeInt32 ( a ) ;
n = decodeInt32 ( a + 4 ) ;
2023-09-14 14:05:51 +00:00
if ( n > ( g . pagesize - 8 ) / 4 ) {
printf ( " ERROR: page %d too many freelist entries (%d) \n " , pgno , n ) ;
n = ( g . pagesize - 8 ) / 4 ;
}
2012-04-03 14:59:50 +00:00
for ( i = 0 ; i < n ; i + + ) {
int child = decodeInt32 ( a + ( i * 4 + 8 ) ) ;
page_usage_msg ( child , " freelist leaf, child %d of trunk page %d " ,
i , pgno ) ;
}
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2012-04-03 14:59:50 +00:00
parent = pgno ;
pgno = iNext ;
}
}
2013-02-19 22:26:51 +00:00
/*
* * Determine pages used as PTRMAP pages
*/
2020-07-22 11:42:50 +00:00
static void page_usage_ptrmap ( u8 * a ) {
2020-01-14 13:24:14 +00:00
if ( decodeInt32 ( a + 52 ) ) {
2015-08-03 17:03:31 +00:00
int usable = g . pagesize - a [ 20 ] ;
2020-07-22 11:42:50 +00:00
u64 pgno = 2 ;
2013-02-19 22:26:51 +00:00
int perPage = usable / 5 ;
2015-08-03 17:03:31 +00:00
while ( pgno < = g . mxPage ) {
2020-07-22 11:42:50 +00:00
page_usage_msg ( ( u32 ) pgno , " PTRMAP page covering %llu..%llu " ,
2013-02-19 22:26:51 +00:00
pgno + 1 , pgno + perPage ) ;
pgno + = perPage + 1 ;
}
}
}
2012-04-03 14:59:50 +00:00
/*
* * Try to figure out how every page in the database file is being used .
*/
2015-08-03 17:03:31 +00:00
static void page_usage_report ( const char * zPrg , const char * zDbName ) {
2020-07-22 11:42:50 +00:00
u32 i , j ;
2012-04-03 14:59:50 +00:00
int rc ;
sqlite3 * db ;
sqlite3_stmt * pStmt ;
unsigned char * a ;
2013-02-19 18:45:11 +00:00
char zQuery [ 200 ] ;
2012-04-03 14:59:50 +00:00
/* Avoid the pathological case */
2015-08-03 17:03:31 +00:00
if ( g . mxPage < 1 ) {
2012-04-03 14:59:50 +00:00
printf ( " empty database \n " ) ;
return ;
}
/* Open the database file */
2015-08-03 17:03:31 +00:00
db = openDatabase ( zPrg , zDbName ) ;
2012-04-03 14:59:50 +00:00
2015-08-03 17:03:31 +00:00
/* Set up global variables zPageUse[] and g.mxPage to record page
2012-04-03 14:59:50 +00:00
* * usages */
2020-07-22 11:42:50 +00:00
zPageUse = sqlite3_malloc64 ( sizeof ( zPageUse [ 0 ] ) * ( g . mxPage + 1 ) ) ;
2012-04-03 14:59:50 +00:00
if ( zPageUse = = 0 ) out_of_memory ( ) ;
2015-08-03 17:03:31 +00:00
memset ( zPageUse , 0 , sizeof ( zPageUse [ 0 ] ) * ( g . mxPage + 1 ) ) ;
2012-04-03 14:59:50 +00:00
/* Discover the usage of each page */
2015-08-03 17:03:31 +00:00
a = fileRead ( 0 , 100 ) ;
2012-04-03 14:59:50 +00:00
page_usage_freelist ( decodeInt32 ( a + 32 ) ) ;
2013-02-19 22:26:51 +00:00
page_usage_ptrmap ( a ) ;
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2020-06-19 15:24:12 +00:00
page_usage_btree ( 1 , 0 , 0 , " sqlite_schema " ) ;
2013-02-19 18:45:11 +00:00
sqlite3_exec ( db , " PRAGMA writable_schema=ON " , 0 , 0 , 0 ) ;
for ( j = 0 ; j < 2 ; j + + ) {
sqlite3_snprintf ( sizeof ( zQuery ) , zQuery ,
" SELECT type, name, rootpage FROM SQLITE_MASTER WHERE rootpage "
" ORDER BY rowid %s " , j ? " DESC " : " " ) ;
rc = sqlite3_prepare_v2 ( db , zQuery , - 1 , & pStmt , 0 ) ;
if ( rc = = SQLITE_OK ) {
while ( sqlite3_step ( pStmt ) = = SQLITE_ROW ) {
2020-07-22 11:42:50 +00:00
u32 pgno = ( u32 ) sqlite3_column_int64 ( pStmt , 2 ) ;
2014-01-28 20:36:22 +00:00
page_usage_btree ( pgno , 0 , 0 , ( const char * ) sqlite3_column_text ( pStmt , 1 ) ) ;
2013-02-19 18:45:11 +00:00
}
} else {
printf ( " ERROR: cannot query database: %s \n " , sqlite3_errmsg ( db ) ) ;
2012-04-03 14:59:50 +00:00
}
2013-02-19 18:45:11 +00:00
rc = sqlite3_finalize ( pStmt ) ;
if ( rc = = SQLITE_OK ) break ;
2012-04-03 14:59:50 +00:00
}
sqlite3_close ( db ) ;
/* Print the report and free memory used */
2015-08-03 17:03:31 +00:00
for ( i = 1 ; i < = g . mxPage ; i + + ) {
2019-04-17 12:29:45 +00:00
if ( zPageUse [ i ] = = 0 ) page_usage_btree ( i , - 1 , 0 , 0 ) ;
2020-07-22 11:42:50 +00:00
printf ( " %5u: %s \n " , i , zPageUse [ i ] ? zPageUse [ i ] : " ??? " ) ;
2021-06-08 17:56:16 +00:00
}
for ( i = 1 ; i < = g . mxPage ; i + + ) {
2012-04-03 14:59:50 +00:00
sqlite3_free ( zPageUse [ i ] ) ;
}
sqlite3_free ( zPageUse ) ;
zPageUse = 0 ;
}
2013-02-19 22:26:51 +00:00
/*
* * Try to figure out how every page in the database file is being used .
*/
static void ptrmap_coverage_report ( const char * zDbName ) {
2020-07-22 11:42:50 +00:00
u64 pgno ;
2013-02-19 22:26:51 +00:00
unsigned char * aHdr ;
unsigned char * a ;
int usable ;
int perPage ;
2014-07-18 21:16:37 +00:00
int i ;
2013-02-19 22:26:51 +00:00
/* Avoid the pathological case */
2015-08-03 17:03:31 +00:00
if ( g . mxPage < 1 ) {
2013-02-19 22:26:51 +00:00
printf ( " empty database \n " ) ;
return ;
}
/* Make sure PTRMAPs are used in this database */
2015-08-03 17:03:31 +00:00
aHdr = fileRead ( 0 , 100 ) ;
2013-02-19 22:26:51 +00:00
if ( aHdr [ 55 ] = = 0 ) {
printf ( " database does not use PTRMAP pages \n " ) ;
return ;
}
2015-08-03 17:03:31 +00:00
usable = g . pagesize - aHdr [ 20 ] ;
2013-02-19 22:26:51 +00:00
perPage = usable / 5 ;
2015-08-03 17:03:31 +00:00
sqlite3_free ( aHdr ) ;
2020-06-19 15:24:12 +00:00
printf ( " %5d: root of sqlite_schema \n " , 1 ) ;
2015-08-03 17:03:31 +00:00
for ( pgno = 2 ; pgno < = g . mxPage ; pgno + = perPage + 1 ) {
2020-07-22 11:42:50 +00:00
printf ( " %5llu: PTRMAP page covering %llu..%llu \n " , pgno ,
2013-02-19 22:26:51 +00:00
pgno + 1 , pgno + perPage ) ;
2015-08-03 17:03:31 +00:00
a = fileRead ( ( pgno - 1 ) * g . pagesize , usable ) ;
for ( i = 0 ; i + 5 < = usable & & pgno + 1 + i / 5 < = g . mxPage ; i + = 5 ) {
2013-02-19 22:26:51 +00:00
const char * zType = " ??? " ;
2020-07-22 11:42:50 +00:00
u32 iFrom = decodeInt32 ( & a [ i + 1 ] ) ;
2013-02-19 22:26:51 +00:00
switch ( a [ i ] ) {
case 1 : zType = " b-tree root page " ; break ;
case 2 : zType = " freelist page " ; break ;
case 3 : zType = " first page of overflow " ; break ;
case 4 : zType = " later page of overflow " ; break ;
case 5 : zType = " b-tree non-root page " ; break ;
}
2020-07-22 11:42:50 +00:00
printf ( " %5llu: %s, parent=%u \n " , pgno + 1 + i / 5 , zType , iFrom ) ;
2013-02-19 22:26:51 +00:00
}
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2013-02-19 22:26:51 +00:00
}
}
2023-02-03 12:03:56 +00:00
/*
* * Check the range validity for a page number . Print an error and
* * exit if the page is out of range .
*/
static void checkPageValidity ( int iPage ) {
if ( iPage < 1 | | iPage > g . mxPage ) {
fprintf ( stderr , " Invalid page number %d: valid range is 1..%d \n " ,
iPage , g . mxPage ) ;
exit ( 1 ) ;
}
}
2010-08-23 15:26:49 +00:00
/*
* * Print a usage comment
*/
static void usage ( const char * argv0 ) {
2015-08-03 17:03:31 +00:00
fprintf ( stderr , " Usage %s ?--uri? FILENAME ?args...? \n \n " , argv0 ) ;
2010-08-23 15:26:49 +00:00
fprintf ( stderr ,
2015-08-03 17:03:31 +00:00
" switches: \n "
2015-08-04 15:23:49 +00:00
" --raw Read db file directly, bypassing SQLite VFS \n "
2010-08-23 15:26:49 +00:00
" args: \n "
" dbheader Show database header \n "
2012-04-03 14:59:50 +00:00
" pgidx Index of how each page is used \n "
2013-02-19 22:26:51 +00:00
" ptrmap Show all PTRMAP page content \n "
2010-08-23 15:26:49 +00:00
" NNN..MMM Show hex of pages NNN through MMM \n "
" NNN..end Show hex of pages NNN through end of file \n "
" NNNb Decode btree page NNN \n "
2011-01-06 01:26:38 +00:00
" NNNbc Decode btree page NNN and show content \n "
" NNNbm Decode btree page NNN and show a layout map \n "
2014-06-19 23:38:53 +00:00
" NNNbdCCC Decode cell CCC on btree page NNN \n "
2010-08-23 15:26:49 +00:00
" NNNt Decode freelist trunk page NNN \n "
2011-04-13 16:52:41 +00:00
" NNNtd Show leaf freelist pages on the decode \n "
2014-08-20 10:42:16 +00:00
" NNNtr Recursively decode freelist starting at NNN \n "
2010-08-23 15:26:49 +00:00
) ;
}
2002-07-06 16:32:14 +00:00
int main ( int argc , char * * argv ) {
2015-08-03 17:03:31 +00:00
sqlite3_int64 szFile ;
unsigned char * zPgSz ;
const char * zPrg = argv [ 0 ] ; /* Name of this executable */
char * * azArg = argv ;
int nArg = argc ;
/* Check for the "--uri" or "-uri" switch. */
if ( nArg > 1 ) {
2015-08-04 15:23:49 +00:00
if ( sqlite3_stricmp ( " -raw " , azArg [ 1 ] ) = = 0
| | sqlite3_stricmp ( " --raw " , azArg [ 1 ] ) = = 0
2015-08-03 17:03:31 +00:00
) {
2015-08-04 15:23:49 +00:00
g . bRaw = 1 ;
2015-08-03 17:03:31 +00:00
azArg + + ;
nArg - - ;
}
2002-07-06 16:32:14 +00:00
}
2015-08-03 17:03:31 +00:00
if ( nArg < 2 ) {
usage ( zPrg ) ;
2002-07-06 16:32:14 +00:00
exit ( 1 ) ;
}
2015-08-03 17:03:31 +00:00
2015-08-04 15:23:49 +00:00
fileOpen ( zPrg , azArg [ 1 ] ) ;
2015-08-03 17:03:31 +00:00
szFile = fileGetsize ( ) ;
zPgSz = fileRead ( 16 , 2 ) ;
g . pagesize = zPgSz [ 0 ] * 256 + zPgSz [ 1 ] * 65536 ;
if ( g . pagesize = = 0 ) g . pagesize = 1024 ;
sqlite3_free ( zPgSz ) ;
printf ( " Pagesize: %d \n " , g . pagesize ) ;
2020-07-22 11:42:50 +00:00
g . mxPage = ( u32 ) ( ( szFile + g . pagesize - 1 ) / g . pagesize ) ;
2015-08-03 17:03:31 +00:00
2020-07-22 11:42:50 +00:00
printf ( " Available pages: 1..%u \n " , g . mxPage ) ;
2015-08-03 17:03:31 +00:00
if ( nArg = = 2 ) {
2020-07-22 11:42:50 +00:00
u32 i ;
2015-08-03 17:03:31 +00:00
for ( i = 1 ; i < = g . mxPage ; i + + ) print_page ( i ) ;
2002-07-06 16:32:14 +00:00
} else {
int i ;
2015-08-03 17:03:31 +00:00
for ( i = 2 ; i < nArg ; i + + ) {
2020-07-22 11:42:50 +00:00
u32 iStart , iEnd ;
2002-07-06 16:32:14 +00:00
char * zLeft ;
2015-08-03 17:03:31 +00:00
if ( strcmp ( azArg [ i ] , " dbheader " ) = = 0 ) {
2010-04-26 15:44:07 +00:00
print_db_header ( ) ;
continue ;
}
2015-08-03 17:03:31 +00:00
if ( strcmp ( azArg [ i ] , " pgidx " ) = = 0 ) {
page_usage_report ( zPrg , azArg [ 1 ] ) ;
2012-04-03 14:59:50 +00:00
continue ;
}
2015-08-03 17:03:31 +00:00
if ( strcmp ( azArg [ i ] , " ptrmap " ) = = 0 ) {
ptrmap_coverage_report ( azArg [ 1 ] ) ;
2013-02-19 22:26:51 +00:00
continue ;
}
2015-08-03 17:03:31 +00:00
if ( strcmp ( azArg [ i ] , " help " ) = = 0 ) {
2015-08-04 15:23:49 +00:00
usage ( zPrg ) ;
2013-02-19 22:26:51 +00:00
continue ;
}
2015-10-29 13:48:15 +00:00
if ( ! ISDIGIT ( azArg [ i ] [ 0 ] ) ) {
2015-08-04 15:23:49 +00:00
fprintf ( stderr , " %s: unknown option: [%s] \n " , zPrg , azArg [ i ] ) ;
2010-04-26 15:44:07 +00:00
continue ;
}
2020-07-22 11:42:50 +00:00
iStart = strtoul ( azArg [ i ] , & zLeft , 0 ) ;
2023-02-03 12:03:56 +00:00
checkPageValidity ( iStart ) ;
2002-07-06 16:32:14 +00:00
if ( zLeft & & strcmp ( zLeft , " ..end " ) = = 0 ) {
2015-08-03 17:03:31 +00:00
iEnd = g . mxPage ;
2002-07-06 16:32:14 +00:00
} else if ( zLeft & & zLeft [ 0 ] = = ' . ' & & zLeft [ 1 ] = = ' . ' ) {
iEnd = strtol ( & zLeft [ 2 ] , 0 , 0 ) ;
2023-02-03 12:03:56 +00:00
checkPageValidity ( iEnd ) ;
2010-04-26 16:47:12 +00:00
} else if ( zLeft & & zLeft [ 0 ] = = ' b ' ) {
int ofst , nByte , hdrSize ;
unsigned char * a ;
if ( iStart = = 1 ) {
ofst = hdrSize = 100 ;
2015-08-03 17:03:31 +00:00
nByte = g . pagesize - 100 ;
2010-04-26 16:47:12 +00:00
} else {
hdrSize = 0 ;
2015-08-03 17:03:31 +00:00
ofst = ( iStart - 1 ) * g . pagesize ;
nByte = g . pagesize ;
2010-04-26 16:47:12 +00:00
}
2015-08-03 17:03:31 +00:00
a = fileRead ( ofst , nByte ) ;
2011-01-05 21:20:52 +00:00
decode_btree_page ( a , iStart , hdrSize , & zLeft [ 1 ] ) ;
2015-08-03 17:03:31 +00:00
sqlite3_free ( a ) ;
2010-04-26 16:47:12 +00:00
continue ;
2010-08-23 15:26:49 +00:00
} else if ( zLeft & & zLeft [ 0 ] = = ' t ' ) {
int detail = 0 ;
int recursive = 0 ;
2015-12-16 22:06:52 +00:00
int j ;
for ( j = 1 ; zLeft [ j ] ; j + + ) {
if ( zLeft [ j ] = = ' r ' ) recursive = 1 ;
if ( zLeft [ j ] = = ' d ' ) detail = 1 ;
2010-08-23 15:26:49 +00:00
}
2015-08-03 17:03:31 +00:00
decode_trunk_page ( iStart , detail , recursive ) ;
2010-08-23 15:26:49 +00:00
continue ;
2002-07-06 16:32:14 +00:00
} else {
iEnd = iStart ;
}
2015-08-03 17:03:31 +00:00
if ( iStart < 1 | | iEnd < iStart | | iEnd > g . mxPage ) {
2002-07-06 16:32:14 +00:00
fprintf ( stderr ,
" Page argument should be LOWER?..UPPER?. Range 1 to %d \n " ,
2015-08-03 17:03:31 +00:00
g . mxPage ) ;
2002-07-06 16:32:14 +00:00
exit ( 1 ) ;
}
while ( iStart < = iEnd ) {
print_page ( iStart ) ;
iStart + + ;
}
}
}
2015-08-03 17:03:31 +00:00
fileClose ( ) ;
2014-01-28 20:36:22 +00:00
return 0 ;
2002-07-06 16:32:14 +00:00
}