| 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 |