Annotation of embedaddon/sqlite3/tool/showdb.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** A utility for printing all or part of an SQLite database file.
! 3: */
! 4: #include <stdio.h>
! 5: #include <ctype.h>
! 6: #include <sys/types.h>
! 7: #include <sys/stat.h>
! 8: #include <fcntl.h>
! 9: #include <unistd.h>
! 10: #include <stdlib.h>
! 11: #include <string.h>
! 12:
! 13:
! 14: static int pagesize = 1024; /* Size of a database page */
! 15: static int db = -1; /* File descriptor for reading the DB */
! 16: static int mxPage = 0; /* Last page number */
! 17: static int perLine = 16; /* HEX elements to print per line */
! 18:
! 19: typedef long long int i64; /* Datatype for 64-bit integers */
! 20:
! 21:
! 22: /*
! 23: ** Convert the var-int format into i64. Return the number of bytes
! 24: ** in the var-int. Write the var-int value into *pVal.
! 25: */
! 26: static int decodeVarint(const unsigned char *z, i64 *pVal){
! 27: i64 v = 0;
! 28: int i;
! 29: for(i=0; i<8; i++){
! 30: v = (v<<7) + (z[i]&0x7f);
! 31: if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; }
! 32: }
! 33: v = (v<<8) + (z[i]&0xff);
! 34: *pVal = v;
! 35: return 9;
! 36: }
! 37:
! 38: /*
! 39: ** Extract a big-endian 32-bit integer
! 40: */
! 41: static unsigned int decodeInt32(const unsigned char *z){
! 42: return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
! 43: }
! 44:
! 45: /* Report an out-of-memory error and die.
! 46: */
! 47: static void out_of_memory(void){
! 48: fprintf(stderr,"Out of memory...\n");
! 49: exit(1);
! 50: }
! 51:
! 52: /*
! 53: ** Read content from the file.
! 54: **
! 55: ** Space to hold the content is obtained from malloc() and needs to be
! 56: ** freed by the caller.
! 57: */
! 58: static unsigned char *getContent(int ofst, int nByte){
! 59: unsigned char *aData;
! 60: aData = malloc(nByte+32);
! 61: if( aData==0 ) out_of_memory();
! 62: memset(aData, 0, nByte+32);
! 63: lseek(db, ofst, SEEK_SET);
! 64: read(db, aData, nByte);
! 65: return aData;
! 66: }
! 67:
! 68: /*
! 69: ** Print a range of bytes as hex and as ascii.
! 70: */
! 71: static unsigned char *print_byte_range(
! 72: int ofst, /* First byte in the range of bytes to print */
! 73: int nByte, /* Number of bytes to print */
! 74: int printOfst /* Add this amount to the index on the left column */
! 75: ){
! 76: unsigned char *aData;
! 77: int i, j;
! 78: const char *zOfstFmt;
! 79:
! 80: if( ((printOfst+nByte)&~0xfff)==0 ){
! 81: zOfstFmt = " %03x: ";
! 82: }else if( ((printOfst+nByte)&~0xffff)==0 ){
! 83: zOfstFmt = " %04x: ";
! 84: }else if( ((printOfst+nByte)&~0xfffff)==0 ){
! 85: zOfstFmt = " %05x: ";
! 86: }else if( ((printOfst+nByte)&~0xffffff)==0 ){
! 87: zOfstFmt = " %06x: ";
! 88: }else{
! 89: zOfstFmt = " %08x: ";
! 90: }
! 91:
! 92: aData = getContent(ofst, nByte);
! 93: for(i=0; i<nByte; i += perLine){
! 94: fprintf(stdout, zOfstFmt, i+printOfst);
! 95: for(j=0; j<perLine; j++){
! 96: if( i+j>nByte ){
! 97: fprintf(stdout, " ");
! 98: }else{
! 99: fprintf(stdout,"%02x ", aData[i+j]);
! 100: }
! 101: }
! 102: for(j=0; j<perLine; j++){
! 103: if( i+j>nByte ){
! 104: fprintf(stdout, " ");
! 105: }else{
! 106: fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
! 107: }
! 108: }
! 109: fprintf(stdout,"\n");
! 110: }
! 111: return aData;
! 112: }
! 113:
! 114: /*
! 115: ** Print an entire page of content as hex
! 116: */
! 117: static print_page(int iPg){
! 118: int iStart;
! 119: unsigned char *aData;
! 120: iStart = (iPg-1)*pagesize;
! 121: fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n",
! 122: iPg, iStart, iStart+pagesize-1);
! 123: aData = print_byte_range(iStart, pagesize, 0);
! 124: free(aData);
! 125: }
! 126:
! 127: /* Print a line of decode output showing a 4-byte integer.
! 128: */
! 129: static print_decode_line(
! 130: unsigned char *aData, /* Content being decoded */
! 131: int ofst, int nByte, /* Start and size of decode */
! 132: const char *zMsg /* Message to append */
! 133: ){
! 134: int i, j;
! 135: int val = aData[ofst];
! 136: char zBuf[100];
! 137: sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]);
! 138: i = strlen(zBuf);
! 139: for(j=1; j<4; j++){
! 140: if( j>=nByte ){
! 141: sprintf(&zBuf[i], " ");
! 142: }else{
! 143: sprintf(&zBuf[i], " %02x", aData[ofst+j]);
! 144: val = val*256 + aData[ofst+j];
! 145: }
! 146: i += strlen(&zBuf[i]);
! 147: }
! 148: sprintf(&zBuf[i], " %9d", val);
! 149: printf("%s %s\n", zBuf, zMsg);
! 150: }
! 151:
! 152: /*
! 153: ** Decode the database header.
! 154: */
! 155: static void print_db_header(void){
! 156: unsigned char *aData;
! 157: aData = print_byte_range(0, 100, 0);
! 158: printf("Decoded:\n");
! 159: print_decode_line(aData, 16, 2, "Database page size");
! 160: print_decode_line(aData, 18, 1, "File format write version");
! 161: print_decode_line(aData, 19, 1, "File format read version");
! 162: print_decode_line(aData, 20, 1, "Reserved space at end of page");
! 163: print_decode_line(aData, 24, 4, "File change counter");
! 164: print_decode_line(aData, 28, 4, "Size of database in pages");
! 165: print_decode_line(aData, 32, 4, "Page number of first freelist page");
! 166: print_decode_line(aData, 36, 4, "Number of freelist pages");
! 167: print_decode_line(aData, 40, 4, "Schema cookie");
! 168: print_decode_line(aData, 44, 4, "Schema format version");
! 169: print_decode_line(aData, 48, 4, "Default page cache size");
! 170: print_decode_line(aData, 52, 4, "Largest auto-vac root page");
! 171: print_decode_line(aData, 56, 4, "Text encoding");
! 172: print_decode_line(aData, 60, 4, "User version");
! 173: print_decode_line(aData, 64, 4, "Incremental-vacuum mode");
! 174: print_decode_line(aData, 68, 4, "meta[7]");
! 175: print_decode_line(aData, 72, 4, "meta[8]");
! 176: print_decode_line(aData, 76, 4, "meta[9]");
! 177: print_decode_line(aData, 80, 4, "meta[10]");
! 178: print_decode_line(aData, 84, 4, "meta[11]");
! 179: print_decode_line(aData, 88, 4, "meta[12]");
! 180: print_decode_line(aData, 92, 4, "Change counter for version number");
! 181: print_decode_line(aData, 96, 4, "SQLite version number");
! 182: }
! 183:
! 184: /*
! 185: ** Describe cell content.
! 186: */
! 187: static int describeContent(
! 188: unsigned char *a, /* Cell content */
! 189: int nLocal, /* Bytes in a[] */
! 190: char *zDesc /* Write description here */
! 191: ){
! 192: int nDesc = 0;
! 193: int n, i, j;
! 194: i64 x, v;
! 195: const unsigned char *pData;
! 196: const unsigned char *pLimit;
! 197: char sep = ' ';
! 198:
! 199: pLimit = &a[nLocal];
! 200: n = decodeVarint(a, &x);
! 201: pData = &a[x];
! 202: a += n;
! 203: i = x - n;
! 204: while( i>0 && pData<=pLimit ){
! 205: n = decodeVarint(a, &x);
! 206: a += n;
! 207: i -= n;
! 208: nLocal -= n;
! 209: zDesc[0] = sep;
! 210: sep = ',';
! 211: nDesc++;
! 212: zDesc++;
! 213: if( x==0 ){
! 214: sprintf(zDesc, "*"); /* NULL is a "*" */
! 215: }else if( x>=1 && x<=6 ){
! 216: v = (signed char)pData[0];
! 217: pData++;
! 218: switch( x ){
! 219: case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
! 220: case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2;
! 221: case 4: v = (v<<8) + pData[0]; pData++;
! 222: case 3: v = (v<<8) + pData[0]; pData++;
! 223: case 2: v = (v<<8) + pData[0]; pData++;
! 224: }
! 225: sprintf(zDesc, "%lld", v);
! 226: }else if( x==7 ){
! 227: sprintf(zDesc, "real");
! 228: pData += 8;
! 229: }else if( x==8 ){
! 230: sprintf(zDesc, "0");
! 231: }else if( x==9 ){
! 232: sprintf(zDesc, "1");
! 233: }else if( x>=12 ){
! 234: int size = (x-12)/2;
! 235: if( (x&1)==0 ){
! 236: sprintf(zDesc, "blob(%d)", size);
! 237: }else{
! 238: sprintf(zDesc, "txt(%d)", size);
! 239: }
! 240: pData += size;
! 241: }
! 242: j = strlen(zDesc);
! 243: zDesc += j;
! 244: nDesc += j;
! 245: }
! 246: return nDesc;
! 247: }
! 248:
! 249: /*
! 250: ** Compute the local payload size given the total payload size and
! 251: ** the page size.
! 252: */
! 253: static int localPayload(i64 nPayload, char cType){
! 254: int maxLocal;
! 255: int minLocal;
! 256: int surplus;
! 257: int nLocal;
! 258: if( cType==13 ){
! 259: /* Table leaf */
! 260: maxLocal = pagesize-35;
! 261: minLocal = (pagesize-12)*32/255-23;
! 262: }else{
! 263: maxLocal = (pagesize-12)*64/255-23;
! 264: minLocal = (pagesize-12)*32/255-23;
! 265: }
! 266: if( nPayload>maxLocal ){
! 267: surplus = minLocal + (nPayload-minLocal)%(pagesize-4);
! 268: if( surplus<=maxLocal ){
! 269: nLocal = surplus;
! 270: }else{
! 271: nLocal = minLocal;
! 272: }
! 273: }else{
! 274: nLocal = nPayload;
! 275: }
! 276: return nLocal;
! 277: }
! 278:
! 279:
! 280: /*
! 281: ** Create a description for a single cell.
! 282: **
! 283: ** The return value is the local cell size.
! 284: */
! 285: static int describeCell(
! 286: unsigned char cType, /* Page type */
! 287: unsigned char *a, /* Cell content */
! 288: int showCellContent, /* Show cell content if true */
! 289: char **pzDesc /* Store description here */
! 290: ){
! 291: int i;
! 292: int nDesc = 0;
! 293: int n = 0;
! 294: int leftChild;
! 295: i64 nPayload;
! 296: i64 rowid;
! 297: int nLocal;
! 298: static char zDesc[1000];
! 299: i = 0;
! 300: if( cType<=5 ){
! 301: leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3];
! 302: a += 4;
! 303: n += 4;
! 304: sprintf(zDesc, "lx: %d ", leftChild);
! 305: nDesc = strlen(zDesc);
! 306: }
! 307: if( cType!=5 ){
! 308: i = decodeVarint(a, &nPayload);
! 309: a += i;
! 310: n += i;
! 311: sprintf(&zDesc[nDesc], "n: %lld ", nPayload);
! 312: nDesc += strlen(&zDesc[nDesc]);
! 313: nLocal = localPayload(nPayload, cType);
! 314: }else{
! 315: nPayload = nLocal = 0;
! 316: }
! 317: if( cType==5 || cType==13 ){
! 318: i = decodeVarint(a, &rowid);
! 319: a += i;
! 320: n += i;
! 321: sprintf(&zDesc[nDesc], "r: %lld ", rowid);
! 322: nDesc += strlen(&zDesc[nDesc]);
! 323: }
! 324: if( nLocal<nPayload ){
! 325: int ovfl;
! 326: unsigned char *b = &a[nLocal];
! 327: ovfl = ((b[0]*256 + b[1])*256 + b[2])*256 + b[3];
! 328: sprintf(&zDesc[nDesc], "ov: %d ", ovfl);
! 329: nDesc += strlen(&zDesc[nDesc]);
! 330: n += 4;
! 331: }
! 332: if( showCellContent && cType!=5 ){
! 333: nDesc += describeContent(a, nLocal, &zDesc[nDesc-1]);
! 334: }
! 335: *pzDesc = zDesc;
! 336: return nLocal+n;
! 337: }
! 338:
! 339: /*
! 340: ** Decode a btree page
! 341: */
! 342: static void decode_btree_page(
! 343: unsigned char *a, /* Page content */
! 344: int pgno, /* Page number */
! 345: int hdrSize, /* Size of the page header. 0 or 100 */
! 346: char *zArgs /* Flags to control formatting */
! 347: ){
! 348: const char *zType = "unknown";
! 349: int nCell;
! 350: int i, j;
! 351: int iCellPtr;
! 352: int showCellContent = 0;
! 353: int showMap = 0;
! 354: char *zMap = 0;
! 355: switch( a[0] ){
! 356: case 2: zType = "index interior node"; break;
! 357: case 5: zType = "table interior node"; break;
! 358: case 10: zType = "index leaf"; break;
! 359: case 13: zType = "table leaf"; break;
! 360: }
! 361: while( zArgs[0] ){
! 362: switch( zArgs[0] ){
! 363: case 'c': showCellContent = 1; break;
! 364: case 'm': showMap = 1; break;
! 365: }
! 366: zArgs++;
! 367: }
! 368: printf("Decode of btree page %d:\n", pgno);
! 369: print_decode_line(a, 0, 1, zType);
! 370: print_decode_line(a, 1, 2, "Offset to first freeblock");
! 371: print_decode_line(a, 3, 2, "Number of cells on this page");
! 372: nCell = a[3]*256 + a[4];
! 373: print_decode_line(a, 5, 2, "Offset to cell content area");
! 374: print_decode_line(a, 7, 1, "Fragmented byte count");
! 375: if( a[0]==2 || a[0]==5 ){
! 376: print_decode_line(a, 8, 4, "Right child");
! 377: iCellPtr = 12;
! 378: }else{
! 379: iCellPtr = 8;
! 380: }
! 381: if( nCell>0 ){
! 382: printf(" key: lx=left-child n=payload-size r=rowid\n");
! 383: }
! 384: if( showMap ){
! 385: zMap = malloc(pagesize);
! 386: memset(zMap, '.', pagesize);
! 387: memset(zMap, '1', hdrSize);
! 388: memset(&zMap[hdrSize], 'H', iCellPtr);
! 389: memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell);
! 390: }
! 391: for(i=0; i<nCell; i++){
! 392: int cofst = iCellPtr + i*2;
! 393: char *zDesc;
! 394: int n;
! 395:
! 396: cofst = a[cofst]*256 + a[cofst+1];
! 397: n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc);
! 398: if( showMap ){
! 399: char zBuf[30];
! 400: memset(&zMap[cofst], '*', n);
! 401: zMap[cofst] = '[';
! 402: zMap[cofst+n-1] = ']';
! 403: sprintf(zBuf, "%d", i);
! 404: j = strlen(zBuf);
! 405: if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j);
! 406: }
! 407: printf(" %03x: cell[%d] %s\n", cofst, i, zDesc);
! 408: }
! 409: if( showMap ){
! 410: for(i=0; i<pagesize; i+=64){
! 411: printf(" %03x: %.64s\n", i, &zMap[i]);
! 412: }
! 413: free(zMap);
! 414: }
! 415: }
! 416:
! 417: /*
! 418: ** Decode a freelist trunk page.
! 419: */
! 420: static void decode_trunk_page(
! 421: int pgno, /* The page number */
! 422: int pagesize, /* Size of each page */
! 423: int detail, /* Show leaf pages if true */
! 424: int recursive /* Follow the trunk change if true */
! 425: ){
! 426: int n, i, k;
! 427: unsigned char *a;
! 428: while( pgno>0 ){
! 429: a = getContent((pgno-1)*pagesize, pagesize);
! 430: printf("Decode of freelist trunk page %d:\n", pgno);
! 431: print_decode_line(a, 0, 4, "Next freelist trunk page");
! 432: print_decode_line(a, 4, 4, "Number of entries on this page");
! 433: if( detail ){
! 434: n = (int)decodeInt32(&a[4]);
! 435: for(i=0; i<n; i++){
! 436: unsigned int x = decodeInt32(&a[8+4*i]);
! 437: char zIdx[10];
! 438: sprintf(zIdx, "[%d]", i);
! 439: printf(" %5s %7u", zIdx, x);
! 440: if( i%5==4 ) printf("\n");
! 441: }
! 442: if( i%5!=0 ) printf("\n");
! 443: }
! 444: if( !recursive ){
! 445: pgno = 0;
! 446: }else{
! 447: pgno = (int)decodeInt32(&a[0]);
! 448: }
! 449: free(a);
! 450: }
! 451: }
! 452:
! 453: /*
! 454: ** Print a usage comment
! 455: */
! 456: static void usage(const char *argv0){
! 457: fprintf(stderr, "Usage %s FILENAME ?args...?\n\n", argv0);
! 458: fprintf(stderr,
! 459: "args:\n"
! 460: " dbheader Show database header\n"
! 461: " NNN..MMM Show hex of pages NNN through MMM\n"
! 462: " NNN..end Show hex of pages NNN through end of file\n"
! 463: " NNNb Decode btree page NNN\n"
! 464: " NNNbc Decode btree page NNN and show content\n"
! 465: " NNNbm Decode btree page NNN and show a layout map\n"
! 466: " NNNt Decode freelist trunk page NNN\n"
! 467: " NNNtd Show leaf freelist pages on the decode\n"
! 468: " NNNtr Recurisvely decode freelist starting at NNN\n"
! 469: );
! 470: }
! 471:
! 472: int main(int argc, char **argv){
! 473: struct stat sbuf;
! 474: unsigned char zPgSz[2];
! 475: if( argc<2 ){
! 476: usage(argv[0]);
! 477: exit(1);
! 478: }
! 479: db = open(argv[1], O_RDONLY);
! 480: if( db<0 ){
! 481: fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]);
! 482: exit(1);
! 483: }
! 484: zPgSz[0] = 0;
! 485: zPgSz[1] = 0;
! 486: lseek(db, 16, SEEK_SET);
! 487: read(db, zPgSz, 2);
! 488: pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
! 489: if( pagesize==0 ) pagesize = 1024;
! 490: printf("Pagesize: %d\n", pagesize);
! 491: fstat(db, &sbuf);
! 492: mxPage = sbuf.st_size/pagesize;
! 493: printf("Available pages: 1..%d\n", mxPage);
! 494: if( argc==2 ){
! 495: int i;
! 496: for(i=1; i<=mxPage; i++) print_page(i);
! 497: }else{
! 498: int i;
! 499: for(i=2; i<argc; i++){
! 500: int iStart, iEnd;
! 501: char *zLeft;
! 502: if( strcmp(argv[i], "dbheader")==0 ){
! 503: print_db_header();
! 504: continue;
! 505: }
! 506: if( !isdigit(argv[i][0]) ){
! 507: fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]);
! 508: continue;
! 509: }
! 510: iStart = strtol(argv[i], &zLeft, 0);
! 511: if( zLeft && strcmp(zLeft,"..end")==0 ){
! 512: iEnd = mxPage;
! 513: }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){
! 514: iEnd = strtol(&zLeft[2], 0, 0);
! 515: }else if( zLeft && zLeft[0]=='b' ){
! 516: int ofst, nByte, hdrSize;
! 517: unsigned char *a;
! 518: if( iStart==1 ){
! 519: ofst = hdrSize = 100;
! 520: nByte = pagesize-100;
! 521: }else{
! 522: hdrSize = 0;
! 523: ofst = (iStart-1)*pagesize;
! 524: nByte = pagesize;
! 525: }
! 526: a = getContent(ofst, nByte);
! 527: decode_btree_page(a, iStart, hdrSize, &zLeft[1]);
! 528: free(a);
! 529: continue;
! 530: }else if( zLeft && zLeft[0]=='t' ){
! 531: unsigned char *a;
! 532: int detail = 0;
! 533: int recursive = 0;
! 534: int i;
! 535: for(i=1; zLeft[i]; i++){
! 536: if( zLeft[i]=='r' ) recursive = 1;
! 537: if( zLeft[i]=='d' ) detail = 1;
! 538: }
! 539: decode_trunk_page(iStart, pagesize, detail, recursive);
! 540: continue;
! 541: }else{
! 542: iEnd = iStart;
! 543: }
! 544: if( iStart<1 || iEnd<iStart || iEnd>mxPage ){
! 545: fprintf(stderr,
! 546: "Page argument should be LOWER?..UPPER?. Range 1 to %d\n",
! 547: mxPage);
! 548: exit(1);
! 549: }
! 550: while( iStart<=iEnd ){
! 551: print_page(iStart);
! 552: iStart++;
! 553: }
! 554: }
! 555: }
! 556: close(db);
! 557: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>