OLD | NEW |
(Empty) | |
| 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 } |
OLD | NEW |