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