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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>