xref: /sqlite-3.40.0/tool/offsets.c (revision 067b92ba)
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