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