Avoid superfluous SHM unlock call in the Win32 VFS.
[sqlite.git] / tool / offsets.c
blob8e098e71cb98be9fa218331f7a8a1d9c31e5218a
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>
14 typedef unsigned char u8;
15 typedef struct GState GState;
17 #define ArraySize(X) (sizeof(X)/sizeof(X[0]))
20 ** Global state information for this program.
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 */
37 ** Write an error.
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);
48 ** Write a trace message
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);
60 ** Find the root page of the table and the column number of the column.
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 */
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;
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;
89 p->iRoot = sqlite3_column_int(pStmt , 0);
90 sqlite3_finalize(pStmt);
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
103 p->iCol = sqlite3_column_int(pStmt, 0);
104 break;
107 sqlite3_finalize(pStmt);
108 if( p->iCol<0 ){
109 ofstError(p, "no such column: %s.%s", zTable, zColumn);
110 goto rootAndColumn_exit;
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);
123 sqlite3_finalize(pStmt);
125 rootAndColumn_exit:
126 sqlite3_close(db);
127 return;
131 ** Pop a page from the stack
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];
143 ** Push a new page onto the stack.
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;
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);
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);
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];
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;
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++;
195 if( n==8 ){
196 x = (x<<8) + a[0];
197 }else{
198 x = (x<<7) + a[0];
200 *pOfst += (n+1);
201 return x;
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;
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;
220 /* Forward reference */
221 static void ofstWalkPage(GState*, int);
223 /* Walk an interior btree page */
224 static void ofstWalkInteriorPage(GState *p){
225 int nCell;
226 int i;
227 int ofst;
228 int iChild;
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;
237 ofstWalkPage(p, ofst4byte(p, 8));
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];
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;
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);
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);
282 ** Output results from a single page.
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]);
295 ofstPopPage(p);
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++;
306 if( argc!=4 ){
307 fprintf(stderr, "Usage: %s DATABASE TABLE COLUMN\n", *argv);
308 exit(1);
310 ofstRootAndColumn(&g, argv[1], argv[2], argv[3]);
311 if( g.zErr ){
312 fprintf(stderr, "%s\n", g.zErr);
313 exit(1);
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);
323 ofstWalkPage(&g, g.iRoot);
324 if( g.zErr ){
325 fprintf(stderr, "%s\n", g.zErr);
326 exit(1);
328 return 0;