OLD | NEW |
| (Empty) |
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 | |
10 #if !defined(_MSC_VER) | |
11 #include <unistd.h> | |
12 #else | |
13 #include <io.h> | |
14 #endif | |
15 | |
16 #include <stdlib.h> | |
17 #include <string.h> | |
18 #include "sqlite3.h" | |
19 | |
20 | |
21 static int pagesize = 1024; /* Size of a database page */ | |
22 static int db = -1; /* File descriptor for reading the DB */ | |
23 static int mxPage = 0; /* Last page number */ | |
24 static int perLine = 16; /* HEX elements to print per line */ | |
25 | |
26 typedef long long int i64; /* Datatype for 64-bit integers */ | |
27 | |
28 | |
29 /* | |
30 ** Convert the var-int format into i64. Return the number of bytes | |
31 ** in the var-int. Write the var-int value into *pVal. | |
32 */ | |
33 static int decodeVarint(const unsigned char *z, i64 *pVal){ | |
34 i64 v = 0; | |
35 int i; | |
36 for(i=0; i<8; i++){ | |
37 v = (v<<7) + (z[i]&0x7f); | |
38 if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } | |
39 } | |
40 v = (v<<8) + (z[i]&0xff); | |
41 *pVal = v; | |
42 return 9; | |
43 } | |
44 | |
45 /* | |
46 ** Extract a big-endian 32-bit integer | |
47 */ | |
48 static unsigned int decodeInt32(const unsigned char *z){ | |
49 return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3]; | |
50 } | |
51 | |
52 /* Report an out-of-memory error and die. | |
53 */ | |
54 static void out_of_memory(void){ | |
55 fprintf(stderr,"Out of memory...\n"); | |
56 exit(1); | |
57 } | |
58 | |
59 /* | |
60 ** Read content from the file. | |
61 ** | |
62 ** Space to hold the content is obtained from malloc() and needs to be | |
63 ** freed by the caller. | |
64 */ | |
65 static unsigned char *getContent(int ofst, int nByte){ | |
66 unsigned char *aData; | |
67 aData = malloc(nByte+32); | |
68 if( aData==0 ) out_of_memory(); | |
69 memset(aData, 0, nByte+32); | |
70 lseek(db, ofst, SEEK_SET); | |
71 if( read(db, aData, nByte)<nByte ) memset(aData, 0, nByte); | |
72 return aData; | |
73 } | |
74 | |
75 /* | |
76 ** Print a range of bytes as hex and as ascii. | |
77 */ | |
78 static unsigned char *print_byte_range( | |
79 int ofst, /* First byte in the range of bytes to print */ | |
80 int nByte, /* Number of bytes to print */ | |
81 int printOfst /* Add this amount to the index on the left column */ | |
82 ){ | |
83 unsigned char *aData; | |
84 int i, j; | |
85 const char *zOfstFmt; | |
86 | |
87 if( ((printOfst+nByte)&~0xfff)==0 ){ | |
88 zOfstFmt = " %03x: "; | |
89 }else if( ((printOfst+nByte)&~0xffff)==0 ){ | |
90 zOfstFmt = " %04x: "; | |
91 }else if( ((printOfst+nByte)&~0xfffff)==0 ){ | |
92 zOfstFmt = " %05x: "; | |
93 }else if( ((printOfst+nByte)&~0xffffff)==0 ){ | |
94 zOfstFmt = " %06x: "; | |
95 }else{ | |
96 zOfstFmt = " %08x: "; | |
97 } | |
98 | |
99 aData = getContent(ofst, nByte); | |
100 for(i=0; i<nByte; i += perLine){ | |
101 fprintf(stdout, zOfstFmt, i+printOfst); | |
102 for(j=0; j<perLine; j++){ | |
103 if( i+j>nByte ){ | |
104 fprintf(stdout, " "); | |
105 }else{ | |
106 fprintf(stdout,"%02x ", aData[i+j]); | |
107 } | |
108 } | |
109 for(j=0; j<perLine; j++){ | |
110 if( i+j>nByte ){ | |
111 fprintf(stdout, " "); | |
112 }else{ | |
113 fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); | |
114 } | |
115 } | |
116 fprintf(stdout,"\n"); | |
117 } | |
118 return aData; | |
119 } | |
120 | |
121 /* | |
122 ** Print an entire page of content as hex | |
123 */ | |
124 static void print_page(int iPg){ | |
125 int iStart; | |
126 unsigned char *aData; | |
127 iStart = (iPg-1)*pagesize; | |
128 fprintf(stdout, "Page %d: (offsets 0x%x..0x%x)\n", | |
129 iPg, iStart, iStart+pagesize-1); | |
130 aData = print_byte_range(iStart, pagesize, 0); | |
131 free(aData); | |
132 } | |
133 | |
134 | |
135 /* Print a line of decode output showing a 4-byte integer. | |
136 */ | |
137 static void print_decode_line( | |
138 unsigned char *aData, /* Content being decoded */ | |
139 int ofst, int nByte, /* Start and size of decode */ | |
140 const char *zMsg /* Message to append */ | |
141 ){ | |
142 int i, j; | |
143 int val = aData[ofst]; | |
144 char zBuf[100]; | |
145 sprintf(zBuf, " %03x: %02x", ofst, aData[ofst]); | |
146 i = (int)strlen(zBuf); | |
147 for(j=1; j<4; j++){ | |
148 if( j>=nByte ){ | |
149 sprintf(&zBuf[i], " "); | |
150 }else{ | |
151 sprintf(&zBuf[i], " %02x", aData[ofst+j]); | |
152 val = val*256 + aData[ofst+j]; | |
153 } | |
154 i += (int)strlen(&zBuf[i]); | |
155 } | |
156 sprintf(&zBuf[i], " %9d", val); | |
157 printf("%s %s\n", zBuf, zMsg); | |
158 } | |
159 | |
160 /* | |
161 ** Decode the database header. | |
162 */ | |
163 static void print_db_header(void){ | |
164 unsigned char *aData; | |
165 aData = print_byte_range(0, 100, 0); | |
166 printf("Decoded:\n"); | |
167 print_decode_line(aData, 16, 2, "Database page size"); | |
168 print_decode_line(aData, 18, 1, "File format write version"); | |
169 print_decode_line(aData, 19, 1, "File format read version"); | |
170 print_decode_line(aData, 20, 1, "Reserved space at end of page"); | |
171 print_decode_line(aData, 24, 4, "File change counter"); | |
172 print_decode_line(aData, 28, 4, "Size of database in pages"); | |
173 print_decode_line(aData, 32, 4, "Page number of first freelist page"); | |
174 print_decode_line(aData, 36, 4, "Number of freelist pages"); | |
175 print_decode_line(aData, 40, 4, "Schema cookie"); | |
176 print_decode_line(aData, 44, 4, "Schema format version"); | |
177 print_decode_line(aData, 48, 4, "Default page cache size"); | |
178 print_decode_line(aData, 52, 4, "Largest auto-vac root page"); | |
179 print_decode_line(aData, 56, 4, "Text encoding"); | |
180 print_decode_line(aData, 60, 4, "User version"); | |
181 print_decode_line(aData, 64, 4, "Incremental-vacuum mode"); | |
182 print_decode_line(aData, 68, 4, "Application ID"); | |
183 print_decode_line(aData, 72, 4, "meta[8]"); | |
184 print_decode_line(aData, 76, 4, "meta[9]"); | |
185 print_decode_line(aData, 80, 4, "meta[10]"); | |
186 print_decode_line(aData, 84, 4, "meta[11]"); | |
187 print_decode_line(aData, 88, 4, "meta[12]"); | |
188 print_decode_line(aData, 92, 4, "Change counter for version number"); | |
189 print_decode_line(aData, 96, 4, "SQLite version number"); | |
190 } | |
191 | |
192 /* | |
193 ** Describe cell content. | |
194 */ | |
195 static i64 describeContent( | |
196 unsigned char *a, /* Cell content */ | |
197 i64 nLocal, /* Bytes in a[] */ | |
198 char *zDesc /* Write description here */ | |
199 ){ | |
200 i64 nDesc = 0; | |
201 int n, j; | |
202 i64 i, x, v; | |
203 const unsigned char *pData; | |
204 const unsigned char *pLimit; | |
205 char sep = ' '; | |
206 | |
207 pLimit = &a[nLocal]; | |
208 n = decodeVarint(a, &x); | |
209 pData = &a[x]; | |
210 a += n; | |
211 i = x - n; | |
212 while( i>0 && pData<=pLimit ){ | |
213 n = decodeVarint(a, &x); | |
214 a += n; | |
215 i -= n; | |
216 nLocal -= n; | |
217 zDesc[0] = sep; | |
218 sep = ','; | |
219 nDesc++; | |
220 zDesc++; | |
221 if( x==0 ){ | |
222 sprintf(zDesc, "*"); /* NULL is a "*" */ | |
223 }else if( x>=1 && x<=6 ){ | |
224 v = (signed char)pData[0]; | |
225 pData++; | |
226 switch( x ){ | |
227 case 6: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; | |
228 case 5: v = (v<<16) + (pData[0]<<8) + pData[1]; pData += 2; | |
229 case 4: v = (v<<8) + pData[0]; pData++; | |
230 case 3: v = (v<<8) + pData[0]; pData++; | |
231 case 2: v = (v<<8) + pData[0]; pData++; | |
232 } | |
233 sprintf(zDesc, "%lld", v); | |
234 }else if( x==7 ){ | |
235 sprintf(zDesc, "real"); | |
236 pData += 8; | |
237 }else if( x==8 ){ | |
238 sprintf(zDesc, "0"); | |
239 }else if( x==9 ){ | |
240 sprintf(zDesc, "1"); | |
241 }else if( x>=12 ){ | |
242 i64 size = (x-12)/2; | |
243 if( (x&1)==0 ){ | |
244 sprintf(zDesc, "blob(%lld)", size); | |
245 }else{ | |
246 sprintf(zDesc, "txt(%lld)", size); | |
247 } | |
248 pData += size; | |
249 } | |
250 j = (int)strlen(zDesc); | |
251 zDesc += j; | |
252 nDesc += j; | |
253 } | |
254 return nDesc; | |
255 } | |
256 | |
257 /* | |
258 ** Compute the local payload size given the total payload size and | |
259 ** the page size. | |
260 */ | |
261 static i64 localPayload(i64 nPayload, char cType){ | |
262 i64 maxLocal; | |
263 i64 minLocal; | |
264 i64 surplus; | |
265 i64 nLocal; | |
266 if( cType==13 ){ | |
267 /* Table leaf */ | |
268 maxLocal = pagesize-35; | |
269 minLocal = (pagesize-12)*32/255-23; | |
270 }else{ | |
271 maxLocal = (pagesize-12)*64/255-23; | |
272 minLocal = (pagesize-12)*32/255-23; | |
273 } | |
274 if( nPayload>maxLocal ){ | |
275 surplus = minLocal + (nPayload-minLocal)%(pagesize-4); | |
276 if( surplus<=maxLocal ){ | |
277 nLocal = surplus; | |
278 }else{ | |
279 nLocal = minLocal; | |
280 } | |
281 }else{ | |
282 nLocal = nPayload; | |
283 } | |
284 return nLocal; | |
285 } | |
286 | |
287 | |
288 /* | |
289 ** Create a description for a single cell. | |
290 ** | |
291 ** The return value is the local cell size. | |
292 */ | |
293 static i64 describeCell( | |
294 unsigned char cType, /* Page type */ | |
295 unsigned char *a, /* Cell content */ | |
296 int showCellContent, /* Show cell content if true */ | |
297 char **pzDesc /* Store description here */ | |
298 ){ | |
299 int i; | |
300 i64 nDesc = 0; | |
301 int n = 0; | |
302 int leftChild; | |
303 i64 nPayload; | |
304 i64 rowid; | |
305 i64 nLocal; | |
306 static char zDesc[1000]; | |
307 i = 0; | |
308 if( cType<=5 ){ | |
309 leftChild = ((a[0]*256 + a[1])*256 + a[2])*256 + a[3]; | |
310 a += 4; | |
311 n += 4; | |
312 sprintf(zDesc, "lx: %d ", leftChild); | |
313 nDesc = strlen(zDesc); | |
314 } | |
315 if( cType!=5 ){ | |
316 i = decodeVarint(a, &nPayload); | |
317 a += i; | |
318 n += i; | |
319 sprintf(&zDesc[nDesc], "n: %lld ", nPayload); | |
320 nDesc += strlen(&zDesc[nDesc]); | |
321 nLocal = localPayload(nPayload, cType); | |
322 }else{ | |
323 nPayload = nLocal = 0; | |
324 } | |
325 if( cType==5 || cType==13 ){ | |
326 i = decodeVarint(a, &rowid); | |
327 a += i; | |
328 n += i; | |
329 sprintf(&zDesc[nDesc], "r: %lld ", rowid); | |
330 nDesc += strlen(&zDesc[nDesc]); | |
331 } | |
332 if( nLocal<nPayload ){ | |
333 int ovfl; | |
334 unsigned char *b = &a[nLocal]; | |
335 ovfl = ((b[0]*256 + b[1])*256 + b[2])*256 + b[3]; | |
336 sprintf(&zDesc[nDesc], "ov: %d ", ovfl); | |
337 nDesc += strlen(&zDesc[nDesc]); | |
338 n += 4; | |
339 } | |
340 if( showCellContent && cType!=5 ){ | |
341 nDesc += describeContent(a, nLocal, &zDesc[nDesc-1]); | |
342 } | |
343 *pzDesc = zDesc; | |
344 return nLocal+n; | |
345 } | |
346 | |
347 /* Print an offset followed by nByte bytes. Add extra white-space | |
348 ** at the end so that subsequent text is aligned. | |
349 */ | |
350 static void printBytes( | |
351 unsigned char *aData, /* Content being decoded */ | |
352 unsigned char *aStart, /* Start of content to be printed */ | |
353 int nByte /* Number of bytes to print */ | |
354 ){ | |
355 int j; | |
356 printf(" %03x: ", (int)(aStart-aData)); | |
357 for(j=0; j<9; j++){ | |
358 if( j>=nByte ){ | |
359 printf(" "); | |
360 }else{ | |
361 printf("%02x ", aStart[j]); | |
362 } | |
363 } | |
364 } | |
365 | |
366 | |
367 /* | |
368 ** Write a full decode on stdout for the cell at a[ofst]. | |
369 ** Assume the page contains a header of size szPgHdr bytes. | |
370 */ | |
371 static void decodeCell( | |
372 unsigned char *a, /* Page content (without the page-1 header) */ | |
373 unsigned pgno, /* Page number */ | |
374 int iCell, /* Cell index */ | |
375 int szPgHdr, /* Size of the page header. 0 or 100 */ | |
376 int ofst /* Cell begins at a[ofst] */ | |
377 ){ | |
378 int i, j; | |
379 int leftChild; | |
380 i64 k; | |
381 i64 nPayload; | |
382 i64 rowid; | |
383 i64 nHdr; | |
384 i64 iType; | |
385 i64 nLocal; | |
386 unsigned char *x = a + ofst; | |
387 unsigned char *end; | |
388 unsigned char cType = a[0]; | |
389 int nCol = 0; | |
390 int szCol[2000]; | |
391 int ofstCol[2000]; | |
392 int typeCol[2000]; | |
393 | |
394 printf("Cell[%d]:\n", iCell); | |
395 if( cType<=5 ){ | |
396 leftChild = ((x[0]*256 + x[1])*256 + x[2])*256 + x[3]; | |
397 printBytes(a, x, 4); | |
398 printf("left child page:: %d\n", leftChild); | |
399 x += 4; | |
400 } | |
401 if( cType!=5 ){ | |
402 i = decodeVarint(x, &nPayload); | |
403 printBytes(a, x, i); | |
404 nLocal = localPayload(nPayload, cType); | |
405 if( nLocal==nPayload ){ | |
406 printf("payload-size: %lld\n", nPayload); | |
407 }else{ | |
408 printf("payload-size: %lld (%lld local, %lld overflow)\n", | |
409 nPayload, nLocal, nPayload-nLocal); | |
410 } | |
411 x += i; | |
412 }else{ | |
413 nPayload = nLocal = 0; | |
414 } | |
415 end = x + nLocal; | |
416 if( cType==5 || cType==13 ){ | |
417 i = decodeVarint(x, &rowid); | |
418 printBytes(a, x, i); | |
419 printf("rowid: %lld\n", rowid); | |
420 x += i; | |
421 } | |
422 if( nLocal>0 ){ | |
423 i = decodeVarint(x, &nHdr); | |
424 printBytes(a, x, i); | |
425 printf("record-header-size: %d\n", (int)nHdr); | |
426 j = i; | |
427 nCol = 0; | |
428 k = nHdr; | |
429 while( x+j<end && j<nHdr ){ | |
430 const char *zTypeName; | |
431 int sz = 0; | |
432 char zNm[30]; | |
433 i = decodeVarint(x+j, &iType); | |
434 printBytes(a, x+j, i); | |
435 printf("typecode[%d]: %d - ", nCol, (int)iType); | |
436 switch( iType ){ | |
437 case 0: zTypeName = "NULL"; sz = 0; break; | |
438 case 1: zTypeName = "int8"; sz = 1; break; | |
439 case 2: zTypeName = "int16"; sz = 2; break; | |
440 case 3: zTypeName = "int24"; sz = 3; break; | |
441 case 4: zTypeName = "int32"; sz = 4; break; | |
442 case 5: zTypeName = "int48"; sz = 6; break; | |
443 case 6: zTypeName = "int64"; sz = 8; break; | |
444 case 7: zTypeName = "double"; sz = 8; break; | |
445 case 8: zTypeName = "zero"; sz = 0; break; | |
446 case 9: zTypeName = "one"; sz = 0; break; | |
447 case 10: | |
448 case 11: zTypeName = "error"; sz = 0; break; | |
449 default: { | |
450 sz = (int)(iType-12)/2; | |
451 sprintf(zNm, (iType&1)==0 ? "blob(%d)" : "text(%d)", sz); | |
452 zTypeName = zNm; | |
453 break; | |
454 } | |
455 } | |
456 printf("%s\n", zTypeName); | |
457 szCol[nCol] = sz; | |
458 ofstCol[nCol] = (int)k; | |
459 typeCol[nCol] = (int)iType; | |
460 k += sz; | |
461 nCol++; | |
462 j += i; | |
463 } | |
464 for(i=0; i<nCol && ofstCol[i]+szCol[i]<=nLocal; i++){ | |
465 int s = ofstCol[i]; | |
466 i64 v; | |
467 const unsigned char *pData; | |
468 if( szCol[i]==0 ) continue; | |
469 printBytes(a, x+s, szCol[i]); | |
470 printf("data[%d]: ", i); | |
471 pData = x+s; | |
472 if( typeCol[i]<=7 ){ | |
473 v = (signed char)pData[0]; | |
474 for(k=1; k<szCol[i]; k++){ | |
475 v = (v<<8) + pData[k]; | |
476 } | |
477 if( typeCol[i]==7 ){ | |
478 double r; | |
479 memcpy(&r, &v, sizeof(r)); | |
480 printf("%#g\n", r); | |
481 }else{ | |
482 printf("%lld\n", v); | |
483 } | |
484 }else{ | |
485 int ii, jj; | |
486 char zConst[32]; | |
487 if( (typeCol[i]&1)==0 ){ | |
488 zConst[0] = 'x'; | |
489 zConst[1] = '\''; | |
490 for(ii=2, jj=0; jj<szCol[i] && ii<24; jj++, ii+=2){ | |
491 sprintf(zConst+ii, "%02x", pData[jj]); | |
492 } | |
493 }else{ | |
494 zConst[0] = '\''; | |
495 for(ii=1, jj=0; jj<szCol[i] && ii<24; jj++, ii++){ | |
496 zConst[ii] = isprint(pData[jj]) ? pData[jj] : '.'; | |
497 } | |
498 zConst[ii] = 0; | |
499 } | |
500 if( jj<szCol[i] ){ | |
501 memcpy(zConst+ii, "...'", 5); | |
502 }else{ | |
503 memcpy(zConst+ii, "'", 2); | |
504 } | |
505 printf("%s\n", zConst); | |
506 } | |
507 j = ofstCol[i] + szCol[i]; | |
508 } | |
509 } | |
510 if( j<nLocal ){ | |
511 printBytes(a, x+j, 0); | |
512 printf("... %lld bytes of content ...\n", nLocal-j); | |
513 } | |
514 if( nLocal<nPayload ){ | |
515 printBytes(a, x+nLocal, 4); | |
516 printf("overflow-page: %d\n", decodeInt32(x+nLocal)); | |
517 } | |
518 } | |
519 | |
520 | |
521 /* | |
522 ** Decode a btree page | |
523 */ | |
524 static void decode_btree_page( | |
525 unsigned char *a, /* Page content */ | |
526 int pgno, /* Page number */ | |
527 int hdrSize, /* Size of the page header. 0 or 100 */ | |
528 char *zArgs /* Flags to control formatting */ | |
529 ){ | |
530 const char *zType = "unknown"; | |
531 int nCell; | |
532 int i, j; | |
533 int iCellPtr; | |
534 int showCellContent = 0; | |
535 int showMap = 0; | |
536 int cellToDecode = -2; | |
537 char *zMap = 0; | |
538 switch( a[0] ){ | |
539 case 2: zType = "index interior node"; break; | |
540 case 5: zType = "table interior node"; break; | |
541 case 10: zType = "index leaf"; break; | |
542 case 13: zType = "table leaf"; break; | |
543 } | |
544 while( zArgs[0] ){ | |
545 switch( zArgs[0] ){ | |
546 case 'c': showCellContent = 1; break; | |
547 case 'm': showMap = 1; break; | |
548 case 'd': { | |
549 if( !isdigit(zArgs[1]) ){ | |
550 cellToDecode = -1; | |
551 }else{ | |
552 cellToDecode = 0; | |
553 while( isdigit(zArgs[1]) ){ | |
554 zArgs++; | |
555 cellToDecode = cellToDecode*10 + zArgs[0] - '0'; | |
556 } | |
557 } | |
558 break; | |
559 } | |
560 } | |
561 zArgs++; | |
562 } | |
563 nCell = a[3]*256 + a[4]; | |
564 iCellPtr = (a[0]==2 || a[0]==5) ? 12 : 8; | |
565 if( cellToDecode>=nCell ){ | |
566 printf("Page %d has only %d cells\n", pgno, nCell); | |
567 return; | |
568 } | |
569 printf("Header on btree page %d:\n", pgno); | |
570 print_decode_line(a, 0, 1, zType); | |
571 print_decode_line(a, 1, 2, "Offset to first freeblock"); | |
572 print_decode_line(a, 3, 2, "Number of cells on this page"); | |
573 print_decode_line(a, 5, 2, "Offset to cell content area"); | |
574 print_decode_line(a, 7, 1, "Fragmented byte count"); | |
575 if( a[0]==2 || a[0]==5 ){ | |
576 print_decode_line(a, 8, 4, "Right child"); | |
577 } | |
578 if( cellToDecode==(-2) && nCell>0 ){ | |
579 printf(" key: lx=left-child n=payload-size r=rowid\n"); | |
580 } | |
581 if( showMap ){ | |
582 zMap = malloc(pagesize); | |
583 memset(zMap, '.', pagesize); | |
584 memset(zMap, '1', hdrSize); | |
585 memset(&zMap[hdrSize], 'H', iCellPtr); | |
586 memset(&zMap[hdrSize+iCellPtr], 'P', 2*nCell); | |
587 } | |
588 for(i=0; i<nCell; i++){ | |
589 int cofst = iCellPtr + i*2; | |
590 char *zDesc; | |
591 i64 n; | |
592 | |
593 cofst = a[cofst]*256 + a[cofst+1]; | |
594 n = describeCell(a[0], &a[cofst-hdrSize], showCellContent, &zDesc); | |
595 if( showMap ){ | |
596 char zBuf[30]; | |
597 memset(&zMap[cofst], '*', (size_t)n); | |
598 zMap[cofst] = '['; | |
599 zMap[cofst+n-1] = ']'; | |
600 sprintf(zBuf, "%d", i); | |
601 j = (int)strlen(zBuf); | |
602 if( j<=n-2 ) memcpy(&zMap[cofst+1], zBuf, j); | |
603 } | |
604 if( cellToDecode==(-2) ){ | |
605 printf(" %03x: cell[%d] %s\n", cofst, i, zDesc); | |
606 }else if( cellToDecode==(-1) || cellToDecode==i ){ | |
607 decodeCell(a, pgno, i, hdrSize, cofst-hdrSize); | |
608 } | |
609 } | |
610 if( showMap ){ | |
611 printf("Page map: (H=header P=cell-index 1=page-1-header .=free-space)\n"); | |
612 for(i=0; i<pagesize; i+=64){ | |
613 printf(" %03x: %.64s\n", i, &zMap[i]); | |
614 } | |
615 free(zMap); | |
616 } | |
617 } | |
618 | |
619 /* | |
620 ** Decode a freelist trunk page. | |
621 */ | |
622 static void decode_trunk_page( | |
623 int pgno, /* The page number */ | |
624 int pagesize, /* Size of each page */ | |
625 int detail, /* Show leaf pages if true */ | |
626 int recursive /* Follow the trunk change if true */ | |
627 ){ | |
628 int n, i; | |
629 unsigned char *a; | |
630 while( pgno>0 ){ | |
631 a = getContent((pgno-1)*pagesize, pagesize); | |
632 printf("Decode of freelist trunk page %d:\n", pgno); | |
633 print_decode_line(a, 0, 4, "Next freelist trunk page"); | |
634 print_decode_line(a, 4, 4, "Number of entries on this page"); | |
635 if( detail ){ | |
636 n = (int)decodeInt32(&a[4]); | |
637 for(i=0; i<n; i++){ | |
638 unsigned int x = decodeInt32(&a[8+4*i]); | |
639 char zIdx[10]; | |
640 sprintf(zIdx, "[%d]", i); | |
641 printf(" %5s %7u", zIdx, x); | |
642 if( i%5==4 ) printf("\n"); | |
643 } | |
644 if( i%5!=0 ) printf("\n"); | |
645 } | |
646 if( !recursive ){ | |
647 pgno = 0; | |
648 }else{ | |
649 pgno = (int)decodeInt32(&a[0]); | |
650 } | |
651 free(a); | |
652 } | |
653 } | |
654 | |
655 /* | |
656 ** A short text comment on the use of each page. | |
657 */ | |
658 static char **zPageUse; | |
659 | |
660 /* | |
661 ** Add a comment on the use of a page. | |
662 */ | |
663 static void page_usage_msg(int pgno, const char *zFormat, ...){ | |
664 va_list ap; | |
665 char *zMsg; | |
666 | |
667 va_start(ap, zFormat); | |
668 zMsg = sqlite3_vmprintf(zFormat, ap); | |
669 va_end(ap); | |
670 if( pgno<=0 || pgno>mxPage ){ | |
671 printf("ERROR: page %d out of range 1..%d: %s\n", | |
672 pgno, mxPage, zMsg); | |
673 sqlite3_free(zMsg); | |
674 return; | |
675 } | |
676 if( zPageUse[pgno]!=0 ){ | |
677 printf("ERROR: page %d used multiple times:\n", pgno); | |
678 printf("ERROR: previous: %s\n", zPageUse[pgno]); | |
679 printf("ERROR: current: %s\n", zMsg); | |
680 sqlite3_free(zPageUse[pgno]); | |
681 } | |
682 zPageUse[pgno] = zMsg; | |
683 } | |
684 | |
685 /* | |
686 ** Find overflow pages of a cell and describe their usage. | |
687 */ | |
688 static void page_usage_cell( | |
689 unsigned char cType, /* Page type */ | |
690 unsigned char *a, /* Cell content */ | |
691 int pgno, /* page containing the cell */ | |
692 int cellno /* Index of the cell on the page */ | |
693 ){ | |
694 int i; | |
695 int n = 0; | |
696 i64 nPayload; | |
697 i64 rowid; | |
698 i64 nLocal; | |
699 i = 0; | |
700 if( cType<=5 ){ | |
701 a += 4; | |
702 n += 4; | |
703 } | |
704 if( cType!=5 ){ | |
705 i = decodeVarint(a, &nPayload); | |
706 a += i; | |
707 n += i; | |
708 nLocal = localPayload(nPayload, cType); | |
709 }else{ | |
710 nPayload = nLocal = 0; | |
711 } | |
712 if( cType==5 || cType==13 ){ | |
713 i = decodeVarint(a, &rowid); | |
714 a += i; | |
715 n += i; | |
716 } | |
717 if( nLocal<nPayload ){ | |
718 int ovfl = decodeInt32(a+nLocal); | |
719 int cnt = 0; | |
720 while( ovfl && (cnt++)<mxPage ){ | |
721 page_usage_msg(ovfl, "overflow %d from cell %d of page %d", | |
722 cnt, cellno, pgno); | |
723 a = getContent((ovfl-1)*pagesize, 4); | |
724 ovfl = decodeInt32(a); | |
725 free(a); | |
726 } | |
727 } | |
728 } | |
729 | |
730 | |
731 /* | |
732 ** Describe the usages of a b-tree page | |
733 */ | |
734 static void page_usage_btree( | |
735 int pgno, /* Page to describe */ | |
736 int parent, /* Parent of this page. 0 for root pages */ | |
737 int idx, /* Which child of the parent */ | |
738 const char *zName /* Name of the table */ | |
739 ){ | |
740 unsigned char *a; | |
741 const char *zType = "corrupt node"; | |
742 int nCell; | |
743 int i; | |
744 int hdr = pgno==1 ? 100 : 0; | |
745 | |
746 if( pgno<=0 || pgno>mxPage ) return; | |
747 a = getContent((pgno-1)*pagesize, pagesize); | |
748 switch( a[hdr] ){ | |
749 case 2: zType = "interior node of index"; break; | |
750 case 5: zType = "interior node of table"; break; | |
751 case 10: zType = "leaf of index"; break; | |
752 case 13: zType = "leaf of table"; break; | |
753 } | |
754 if( parent ){ | |
755 page_usage_msg(pgno, "%s [%s], child %d of page %d", | |
756 zType, zName, idx, parent); | |
757 }else{ | |
758 page_usage_msg(pgno, "root %s [%s]", zType, zName); | |
759 } | |
760 nCell = a[hdr+3]*256 + a[hdr+4]; | |
761 if( a[hdr]==2 || a[hdr]==5 ){ | |
762 int cellstart = hdr+12; | |
763 unsigned int child; | |
764 for(i=0; i<nCell; i++){ | |
765 int ofst; | |
766 | |
767 ofst = cellstart + i*2; | |
768 ofst = a[ofst]*256 + a[ofst+1]; | |
769 child = decodeInt32(a+ofst); | |
770 page_usage_btree(child, pgno, i, zName); | |
771 } | |
772 child = decodeInt32(a+cellstart-4); | |
773 page_usage_btree(child, pgno, i, zName); | |
774 } | |
775 if( a[hdr]==2 || a[hdr]==10 || a[hdr]==13 ){ | |
776 int cellstart = hdr + 8 + 4*(a[hdr]<=5); | |
777 for(i=0; i<nCell; i++){ | |
778 int ofst; | |
779 ofst = cellstart + i*2; | |
780 ofst = a[ofst]*256 + a[ofst+1]; | |
781 page_usage_cell(a[hdr], a+ofst, pgno, i); | |
782 } | |
783 } | |
784 free(a); | |
785 } | |
786 | |
787 /* | |
788 ** Determine page usage by the freelist | |
789 */ | |
790 static void page_usage_freelist(int pgno){ | |
791 unsigned char *a; | |
792 int cnt = 0; | |
793 int i; | |
794 int n; | |
795 int iNext; | |
796 int parent = 1; | |
797 | |
798 while( pgno>0 && pgno<=mxPage && (cnt++)<mxPage ){ | |
799 page_usage_msg(pgno, "freelist trunk #%d child of %d", cnt, parent); | |
800 a = getContent((pgno-1)*pagesize, pagesize); | |
801 iNext = decodeInt32(a); | |
802 n = decodeInt32(a+4); | |
803 for(i=0; i<n; i++){ | |
804 int child = decodeInt32(a + (i*4+8)); | |
805 page_usage_msg(child, "freelist leaf, child %d of trunk page %d", | |
806 i, pgno); | |
807 } | |
808 free(a); | |
809 parent = pgno; | |
810 pgno = iNext; | |
811 } | |
812 } | |
813 | |
814 /* | |
815 ** Determine pages used as PTRMAP pages | |
816 */ | |
817 static void page_usage_ptrmap(unsigned char *a){ | |
818 if( a[55] ){ | |
819 int usable = pagesize - a[20]; | |
820 int pgno = 2; | |
821 int perPage = usable/5; | |
822 while( pgno<=mxPage ){ | |
823 page_usage_msg(pgno, "PTRMAP page covering %d..%d", | |
824 pgno+1, pgno+perPage); | |
825 pgno += perPage + 1; | |
826 } | |
827 } | |
828 } | |
829 | |
830 /* | |
831 ** Try to figure out how every page in the database file is being used. | |
832 */ | |
833 static void page_usage_report(const char *zDbName){ | |
834 int i, j; | |
835 int rc; | |
836 sqlite3 *db; | |
837 sqlite3_stmt *pStmt; | |
838 unsigned char *a; | |
839 char zQuery[200]; | |
840 | |
841 /* Avoid the pathological case */ | |
842 if( mxPage<1 ){ | |
843 printf("empty database\n"); | |
844 return; | |
845 } | |
846 | |
847 /* Open the database file */ | |
848 rc = sqlite3_open(zDbName, &db); | |
849 if( rc ){ | |
850 printf("cannot open database: %s\n", sqlite3_errmsg(db)); | |
851 sqlite3_close(db); | |
852 return; | |
853 } | |
854 | |
855 /* Set up global variables zPageUse[] and mxPage to record page | |
856 ** usages */ | |
857 zPageUse = sqlite3_malloc( sizeof(zPageUse[0])*(mxPage+1) ); | |
858 if( zPageUse==0 ) out_of_memory(); | |
859 memset(zPageUse, 0, sizeof(zPageUse[0])*(mxPage+1)); | |
860 | |
861 /* Discover the usage of each page */ | |
862 a = getContent(0, 100); | |
863 page_usage_freelist(decodeInt32(a+32)); | |
864 page_usage_ptrmap(a); | |
865 free(a); | |
866 page_usage_btree(1, 0, 0, "sqlite_master"); | |
867 sqlite3_exec(db, "PRAGMA writable_schema=ON", 0, 0, 0); | |
868 for(j=0; j<2; j++){ | |
869 sqlite3_snprintf(sizeof(zQuery), zQuery, | |
870 "SELECT type, name, rootpage FROM SQLITE_MASTER WHERE rootpage" | |
871 " ORDER BY rowid %s", j?"DESC":""); | |
872 rc = sqlite3_prepare_v2(db, zQuery, -1, &pStmt, 0); | |
873 if( rc==SQLITE_OK ){ | |
874 while( sqlite3_step(pStmt)==SQLITE_ROW ){ | |
875 int pgno = sqlite3_column_int(pStmt, 2); | |
876 page_usage_btree(pgno, 0, 0, (const char*)sqlite3_column_text(pStmt,1)); | |
877 } | |
878 }else{ | |
879 printf("ERROR: cannot query database: %s\n", sqlite3_errmsg(db)); | |
880 } | |
881 rc = sqlite3_finalize(pStmt); | |
882 if( rc==SQLITE_OK ) break; | |
883 } | |
884 sqlite3_close(db); | |
885 | |
886 /* Print the report and free memory used */ | |
887 for(i=1; i<=mxPage; i++){ | |
888 printf("%5d: %s\n", i, zPageUse[i] ? zPageUse[i] : "???"); | |
889 sqlite3_free(zPageUse[i]); | |
890 } | |
891 sqlite3_free(zPageUse); | |
892 zPageUse = 0; | |
893 } | |
894 | |
895 /* | |
896 ** Try to figure out how every page in the database file is being used. | |
897 */ | |
898 static void ptrmap_coverage_report(const char *zDbName){ | |
899 int pgno; | |
900 unsigned char *aHdr; | |
901 unsigned char *a; | |
902 int usable; | |
903 int perPage; | |
904 int i; | |
905 | |
906 /* Avoid the pathological case */ | |
907 if( mxPage<1 ){ | |
908 printf("empty database\n"); | |
909 return; | |
910 } | |
911 | |
912 /* Make sure PTRMAPs are used in this database */ | |
913 aHdr = getContent(0, 100); | |
914 if( aHdr[55]==0 ){ | |
915 printf("database does not use PTRMAP pages\n"); | |
916 return; | |
917 } | |
918 usable = pagesize - aHdr[20]; | |
919 perPage = usable/5; | |
920 free(aHdr); | |
921 printf("%5d: root of sqlite_master\n", 1); | |
922 for(pgno=2; pgno<=mxPage; pgno += perPage+1){ | |
923 printf("%5d: PTRMAP page covering %d..%d\n", pgno, | |
924 pgno+1, pgno+perPage); | |
925 a = getContent((pgno-1)*pagesize, usable); | |
926 for(i=0; i+5<=usable && pgno+1+i/5<=mxPage; i+=5){ | |
927 const char *zType = "???"; | |
928 unsigned int iFrom = decodeInt32(&a[i+1]); | |
929 switch( a[i] ){ | |
930 case 1: zType = "b-tree root page"; break; | |
931 case 2: zType = "freelist page"; break; | |
932 case 3: zType = "first page of overflow"; break; | |
933 case 4: zType = "later page of overflow"; break; | |
934 case 5: zType = "b-tree non-root page"; break; | |
935 } | |
936 printf("%5d: %s, parent=%u\n", pgno+1+i/5, zType, iFrom); | |
937 } | |
938 free(a); | |
939 } | |
940 } | |
941 | |
942 /* | |
943 ** Print a usage comment | |
944 */ | |
945 static void usage(const char *argv0){ | |
946 fprintf(stderr, "Usage %s FILENAME ?args...?\n\n", argv0); | |
947 fprintf(stderr, | |
948 "args:\n" | |
949 " dbheader Show database header\n" | |
950 " pgidx Index of how each page is used\n" | |
951 " ptrmap Show all PTRMAP page content\n" | |
952 " NNN..MMM Show hex of pages NNN through MMM\n" | |
953 " NNN..end Show hex of pages NNN through end of file\n" | |
954 " NNNb Decode btree page NNN\n" | |
955 " NNNbc Decode btree page NNN and show content\n" | |
956 " NNNbm Decode btree page NNN and show a layout map\n" | |
957 " NNNbdCCC Decode cell CCC on btree page NNN\n" | |
958 " NNNt Decode freelist trunk page NNN\n" | |
959 " NNNtd Show leaf freelist pages on the decode\n" | |
960 " NNNtr Recursively decode freelist starting at NNN\n" | |
961 ); | |
962 } | |
963 | |
964 int main(int argc, char **argv){ | |
965 struct stat sbuf; | |
966 unsigned char zPgSz[2]; | |
967 if( argc<2 ){ | |
968 usage(argv[0]); | |
969 exit(1); | |
970 } | |
971 db = open(argv[1], O_RDONLY); | |
972 if( db<0 ){ | |
973 fprintf(stderr,"%s: can't open %s\n", argv[0], argv[1]); | |
974 exit(1); | |
975 } | |
976 zPgSz[0] = 0; | |
977 zPgSz[1] = 0; | |
978 lseek(db, 16, SEEK_SET); | |
979 if( read(db, zPgSz, 2)<2 ) memset(zPgSz, 0, 2); | |
980 pagesize = zPgSz[0]*256 + zPgSz[1]*65536; | |
981 if( pagesize==0 ) pagesize = 1024; | |
982 printf("Pagesize: %d\n", pagesize); | |
983 fstat(db, &sbuf); | |
984 mxPage = sbuf.st_size/pagesize; | |
985 printf("Available pages: 1..%d\n", mxPage); | |
986 if( argc==2 ){ | |
987 int i; | |
988 for(i=1; i<=mxPage; i++) print_page(i); | |
989 }else{ | |
990 int i; | |
991 for(i=2; i<argc; i++){ | |
992 int iStart, iEnd; | |
993 char *zLeft; | |
994 if( strcmp(argv[i], "dbheader")==0 ){ | |
995 print_db_header(); | |
996 continue; | |
997 } | |
998 if( strcmp(argv[i], "pgidx")==0 ){ | |
999 page_usage_report(argv[1]); | |
1000 continue; | |
1001 } | |
1002 if( strcmp(argv[i], "ptrmap")==0 ){ | |
1003 ptrmap_coverage_report(argv[1]); | |
1004 continue; | |
1005 } | |
1006 if( strcmp(argv[i], "help")==0 ){ | |
1007 usage(argv[0]); | |
1008 continue; | |
1009 } | |
1010 if( !isdigit(argv[i][0]) ){ | |
1011 fprintf(stderr, "%s: unknown option: [%s]\n", argv[0], argv[i]); | |
1012 continue; | |
1013 } | |
1014 iStart = strtol(argv[i], &zLeft, 0); | |
1015 if( zLeft && strcmp(zLeft,"..end")==0 ){ | |
1016 iEnd = mxPage; | |
1017 }else if( zLeft && zLeft[0]=='.' && zLeft[1]=='.' ){ | |
1018 iEnd = strtol(&zLeft[2], 0, 0); | |
1019 }else if( zLeft && zLeft[0]=='b' ){ | |
1020 int ofst, nByte, hdrSize; | |
1021 unsigned char *a; | |
1022 if( iStart==1 ){ | |
1023 ofst = hdrSize = 100; | |
1024 nByte = pagesize-100; | |
1025 }else{ | |
1026 hdrSize = 0; | |
1027 ofst = (iStart-1)*pagesize; | |
1028 nByte = pagesize; | |
1029 } | |
1030 a = getContent(ofst, nByte); | |
1031 decode_btree_page(a, iStart, hdrSize, &zLeft[1]); | |
1032 free(a); | |
1033 continue; | |
1034 }else if( zLeft && zLeft[0]=='t' ){ | |
1035 int detail = 0; | |
1036 int recursive = 0; | |
1037 int i; | |
1038 for(i=1; zLeft[i]; i++){ | |
1039 if( zLeft[i]=='r' ) recursive = 1; | |
1040 if( zLeft[i]=='d' ) detail = 1; | |
1041 } | |
1042 decode_trunk_page(iStart, pagesize, detail, recursive); | |
1043 continue; | |
1044 }else{ | |
1045 iEnd = iStart; | |
1046 } | |
1047 if( iStart<1 || iEnd<iStart || iEnd>mxPage ){ | |
1048 fprintf(stderr, | |
1049 "Page argument should be LOWER?..UPPER?. Range 1 to %d\n", | |
1050 mxPage); | |
1051 exit(1); | |
1052 } | |
1053 while( iStart<=iEnd ){ | |
1054 print_page(iStart); | |
1055 iStart++; | |
1056 } | |
1057 } | |
1058 } | |
1059 close(db); | |
1060 return 0; | |
1061 } | |
OLD | NEW |