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_schema 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 } 330