OLD | NEW |
| (Empty) |
1 /* | |
2 ** This program searches an SQLite database file for the lengths and | |
3 ** offsets for all TEXT or BLOB entries for a particular column of a | |
4 ** particular table. The rowid, size and offset for the column are | |
5 ** written to standard output. There are three arguments, which are the | |
6 ** name of the database file, the table, and the column. | |
7 */ | |
8 #include "sqlite3.h" | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <stdarg.h> | |
12 #include <string.h> | |
13 | |
14 typedef unsigned char u8; | |
15 typedef struct GState GState; | |
16 | |
17 #define ArraySize(X) (sizeof(X)/sizeof(X[0])) | |
18 | |
19 /* | |
20 ** Global state information for this program. | |
21 */ | |
22 struct GState { | |
23 char *zErr; /* Error message text */ | |
24 FILE *f; /* Open database file */ | |
25 int szPg; /* Page size for the database file */ | |
26 int iRoot; /* Root page of the table */ | |
27 int iCol; /* Column number for the column */ | |
28 int pgno; /* Current page number */ | |
29 u8 *aPage; /* Current page content */ | |
30 u8 *aStack[20]; /* Page stack */ | |
31 int aPgno[20]; /* Page number stack */ | |
32 int nStack; /* Depth of stack */ | |
33 int bTrace; /* True for tracing output */ | |
34 }; | |
35 | |
36 /* | |
37 ** Write an error. | |
38 */ | |
39 static void ofstError(GState *p, const char *zFormat, ...){ | |
40 va_list ap; | |
41 sqlite3_free(p->zErr); | |
42 va_start(ap, zFormat); | |
43 p->zErr = sqlite3_vmprintf(zFormat, ap); | |
44 va_end(ap); | |
45 } | |
46 | |
47 /* | |
48 ** Write a trace message | |
49 */ | |
50 static void ofstTrace(GState *p, const char *zFormat, ...){ | |
51 va_list ap; | |
52 if( p->bTrace ){ | |
53 va_start(ap, zFormat); | |
54 vprintf(zFormat, ap); | |
55 va_end(ap); | |
56 } | |
57 } | |
58 | |
59 /* | |
60 ** Find the root page of the table and the column number of the column. | |
61 */ | |
62 static void ofstRootAndColumn( | |
63 GState *p, /* Global state */ | |
64 const char *zFile, /* Name of the database file */ | |
65 const char *zTable, /* Name of the table */ | |
66 const char *zColumn /* Name of the column */ | |
67 ){ | |
68 sqlite3 *db = 0; | |
69 sqlite3_stmt *pStmt = 0; | |
70 char *zSql = 0; | |
71 int rc; | |
72 if( p->zErr ) return; | |
73 rc = sqlite3_open(zFile, &db); | |
74 if( rc ){ | |
75 ofstError(p, "cannot open database file \"%s\"", zFile); | |
76 goto rootAndColumn_exit; | |
77 } | |
78 zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master WHERE name=%Q", | |
79 zTable); | |
80 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
81 if( rc ) ofstError(p, "%s: [%s]", sqlite3_errmsg(db), zSql); | |
82 sqlite3_free(zSql); | |
83 if( p->zErr ) goto rootAndColumn_exit; | |
84 if( sqlite3_step(pStmt)!=SQLITE_ROW ){ | |
85 ofstError(p, "cannot find table [%s]\n", zTable); | |
86 sqlite3_finalize(pStmt); | |
87 goto rootAndColumn_exit; | |
88 } | |
89 p->iRoot = sqlite3_column_int(pStmt , 0); | |
90 sqlite3_finalize(pStmt); | |
91 | |
92 p->iCol = -1; | |
93 zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", zTable); | |
94 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
95 if( rc ) ofstError(p, "%s: [%s}", sqlite3_errmsg(db), zSql); | |
96 sqlite3_free(zSql); | |
97 if( p->zErr ) goto rootAndColumn_exit; | |
98 while( sqlite3_step(pStmt)==SQLITE_ROW ){ | |
99 const char *zCol = sqlite3_column_text(pStmt, 1); | |
100 if( strlen(zCol)==strlen(zColumn) | |
101 && sqlite3_strnicmp(zCol, zColumn, strlen(zCol))==0 | |
102 ){ | |
103 p->iCol = sqlite3_column_int(pStmt, 0); | |
104 break; | |
105 } | |
106 } | |
107 sqlite3_finalize(pStmt); | |
108 if( p->iCol<0 ){ | |
109 ofstError(p, "no such column: %s.%s", zTable, zColumn); | |
110 goto rootAndColumn_exit; | |
111 } | |
112 | |
113 zSql = sqlite3_mprintf("PRAGMA page_size"); | |
114 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
115 if( rc ) ofstError(p, "%s: [%s]", sqlite3_errmsg(db), zSql); | |
116 sqlite3_free(zSql); | |
117 if( p->zErr ) goto rootAndColumn_exit; | |
118 if( sqlite3_step(pStmt)!=SQLITE_ROW ){ | |
119 ofstError(p, "cannot find page size"); | |
120 }else{ | |
121 p->szPg = sqlite3_column_int(pStmt, 0); | |
122 } | |
123 sqlite3_finalize(pStmt); | |
124 | |
125 rootAndColumn_exit: | |
126 sqlite3_close(db); | |
127 return; | |
128 } | |
129 | |
130 /* | |
131 ** Pop a page from the stack | |
132 */ | |
133 static void ofstPopPage(GState *p){ | |
134 if( p->nStack<=0 ) return; | |
135 p->nStack--; | |
136 sqlite3_free(p->aStack[p->nStack]); | |
137 p->pgno = p->aPgno[p->nStack-1]; | |
138 p->aPage = p->aStack[p->nStack-1]; | |
139 } | |
140 | |
141 | |
142 /* | |
143 ** Push a new page onto the stack. | |
144 */ | |
145 static void ofstPushPage(GState *p, int pgno){ | |
146 u8 *pPage; | |
147 size_t got; | |
148 if( p->zErr ) return; | |
149 if( p->nStack >= ArraySize(p->aStack) ){ | |
150 ofstError(p, "page stack overflow"); | |
151 return; | |
152 } | |
153 p->aPgno[p->nStack] = pgno; | |
154 p->aStack[p->nStack] = pPage = sqlite3_malloc( p->szPg ); | |
155 if( pPage==0 ){ | |
156 fprintf(stderr, "out of memory\n"); | |
157 exit(1); | |
158 } | |
159 p->nStack++; | |
160 p->aPage = pPage; | |
161 p->pgno = pgno; | |
162 fseek(p->f, (pgno-1)*p->szPg, SEEK_SET); | |
163 got = fread(pPage, 1, p->szPg, p->f); | |
164 if( got!=p->szPg ){ | |
165 ofstError(p, "unable to read page %d", pgno); | |
166 ofstPopPage(p); | |
167 } | |
168 } | |
169 | |
170 /* Read a two-byte integer at the given offset into the current page */ | |
171 static int ofst2byte(GState *p, int ofst){ | |
172 int x = p->aPage[ofst]; | |
173 return (x<<8) + p->aPage[ofst+1]; | |
174 } | |
175 | |
176 /* Read a four-byte integer at the given offset into the current page */ | |
177 static int ofst4byte(GState *p, int ofst){ | |
178 int x = p->aPage[ofst]; | |
179 x = (x<<8) + p->aPage[ofst+1]; | |
180 x = (x<<8) + p->aPage[ofst+2]; | |
181 x = (x<<8) + p->aPage[ofst+3]; | |
182 return x; | |
183 } | |
184 | |
185 /* Read a variable-length integer. Update the offset */ | |
186 static sqlite3_int64 ofstVarint(GState *p, int *pOfst){ | |
187 sqlite3_int64 x = 0; | |
188 u8 *a = &p->aPage[*pOfst]; | |
189 int n = 0; | |
190 while( n<8 && (a[0] & 0x80)!=0 ){ | |
191 x = (x<<7) + (a[0] & 0x7f); | |
192 n++; | |
193 a++; | |
194 } | |
195 if( n==8 ){ | |
196 x = (x<<8) + a[0]; | |
197 }else{ | |
198 x = (x<<7) + a[0]; | |
199 } | |
200 *pOfst += (n+1); | |
201 return x; | |
202 } | |
203 | |
204 /* Return the absolute offset into a file for the given offset | |
205 ** into the current page */ | |
206 static int ofstInFile(GState *p, int ofst){ | |
207 return p->szPg*(p->pgno-1) + ofst; | |
208 } | |
209 | |
210 /* Return the size (in bytes) of the data corresponding to the | |
211 ** given serial code */ | |
212 static int ofstSerialSize(int scode){ | |
213 if( scode<5 ) return scode; | |
214 if( scode==5 ) return 6; | |
215 if( scode<8 ) return 8; | |
216 if( scode<12 ) return 0; | |
217 return (scode-12)/2; | |
218 } | |
219 | |
220 /* Forward reference */ | |
221 static void ofstWalkPage(GState*, int); | |
222 | |
223 /* Walk an interior btree page */ | |
224 static void ofstWalkInteriorPage(GState *p){ | |
225 int nCell; | |
226 int i; | |
227 int ofst; | |
228 int iChild; | |
229 | |
230 nCell = ofst2byte(p, 3); | |
231 for(i=0; i<nCell; i++){ | |
232 ofst = ofst2byte(p, 12+i*2); | |
233 iChild = ofst4byte(p, ofst); | |
234 ofstWalkPage(p, iChild); | |
235 if( p->zErr ) return; | |
236 } | |
237 ofstWalkPage(p, ofst4byte(p, 8)); | |
238 } | |
239 | |
240 /* Walk a leaf btree page */ | |
241 static void ofstWalkLeafPage(GState *p){ | |
242 int nCell; | |
243 int i; | |
244 int ofst; | |
245 int nPayload; | |
246 sqlite3_int64 rowid; | |
247 int nHdr; | |
248 int j; | |
249 int scode; | |
250 int sz; | |
251 int dataOfst; | |
252 char zMsg[200]; | |
253 | |
254 nCell = ofst2byte(p, 3); | |
255 for(i=0; i<nCell; i++){ | |
256 ofst = ofst2byte(p, 8+i*2); | |
257 nPayload = ofstVarint(p, &ofst); | |
258 rowid = ofstVarint(p, &ofst); | |
259 if( nPayload > p->szPg-35 ){ | |
260 sqlite3_snprintf(sizeof(zMsg), zMsg, | |
261 "# overflow rowid %lld", rowid); | |
262 printf("%s\n", zMsg); | |
263 continue; | |
264 } | |
265 dataOfst = ofst; | |
266 nHdr = ofstVarint(p, &ofst); | |
267 dataOfst += nHdr; | |
268 for(j=0; j<p->iCol; j++){ | |
269 scode = ofstVarint(p, &ofst); | |
270 dataOfst += ofstSerialSize(scode); | |
271 } | |
272 scode = ofstVarint(p, &ofst); | |
273 sz = ofstSerialSize(scode); | |
274 sqlite3_snprintf(sizeof(zMsg), zMsg, | |
275 "rowid %12lld size %5d offset %8d", | |
276 rowid, sz, ofstInFile(p, dataOfst)); | |
277 printf("%s\n", zMsg); | |
278 } | |
279 } | |
280 | |
281 /* | |
282 ** Output results from a single page. | |
283 */ | |
284 static void ofstWalkPage(GState *p, int pgno){ | |
285 if( p->zErr ) return; | |
286 ofstPushPage(p, pgno); | |
287 if( p->zErr ) return; | |
288 if( p->aPage[0]==5 ){ | |
289 ofstWalkInteriorPage(p); | |
290 }else if( p->aPage[0]==13 ){ | |
291 ofstWalkLeafPage(p); | |
292 }else{ | |
293 ofstError(p, "page %d has a faulty type byte: %d", pgno, p->aPage[0]); | |
294 } | |
295 ofstPopPage(p); | |
296 } | |
297 | |
298 int main(int argc, char **argv){ | |
299 GState g; | |
300 memset(&g, 0, sizeof(g)); | |
301 if( argc>2 && strcmp(argv[1],"--trace")==0 ){ | |
302 g.bTrace = 1; | |
303 argc--; | |
304 argv++; | |
305 } | |
306 if( argc!=4 ){ | |
307 fprintf(stderr, "Usage: %s DATABASE TABLE COLUMN\n", *argv); | |
308 exit(1); | |
309 } | |
310 ofstRootAndColumn(&g, argv[1], argv[2], argv[3]); | |
311 if( g.zErr ){ | |
312 fprintf(stderr, "%s\n", g.zErr); | |
313 exit(1); | |
314 } | |
315 ofstTrace(&g, "# szPg = %d\n", g.szPg); | |
316 ofstTrace(&g, "# iRoot = %d\n", g.iRoot); | |
317 ofstTrace(&g, "# iCol = %d\n", g.iCol); | |
318 g.f = fopen(argv[1], "rb"); | |
319 if( g.f==0 ){ | |
320 fprintf(stderr, "cannot open \"%s\"\n", argv[1]); | |
321 exit(1); | |
322 } | |
323 ofstWalkPage(&g, g.iRoot); | |
324 if( g.zErr ){ | |
325 fprintf(stderr, "%s\n", g.zErr); | |
326 exit(1); | |
327 } | |
328 return 0; | |
329 } | |
OLD | NEW |