Index: third_party/sqlite/sqlite-src-3080704/tool/offsets.c |
diff --git a/third_party/sqlite/sqlite-src-3080704/tool/offsets.c b/third_party/sqlite/sqlite-src-3080704/tool/offsets.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8e098e71cb98be9fa218331f7a8a1d9c31e5218a |
--- /dev/null |
+++ b/third_party/sqlite/sqlite-src-3080704/tool/offsets.c |
@@ -0,0 +1,329 @@ |
+/* |
+** This program searches an SQLite database file for the lengths and |
+** offsets for all TEXT or BLOB entries for a particular column of a |
+** particular table. The rowid, size and offset for the column are |
+** written to standard output. There are three arguments, which are the |
+** name of the database file, the table, and the column. |
+*/ |
+#include "sqlite3.h" |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <stdarg.h> |
+#include <string.h> |
+ |
+typedef unsigned char u8; |
+typedef struct GState GState; |
+ |
+#define ArraySize(X) (sizeof(X)/sizeof(X[0])) |
+ |
+/* |
+** Global state information for this program. |
+*/ |
+struct GState { |
+ char *zErr; /* Error message text */ |
+ FILE *f; /* Open database file */ |
+ int szPg; /* Page size for the database file */ |
+ int iRoot; /* Root page of the table */ |
+ int iCol; /* Column number for the column */ |
+ int pgno; /* Current page number */ |
+ u8 *aPage; /* Current page content */ |
+ u8 *aStack[20]; /* Page stack */ |
+ int aPgno[20]; /* Page number stack */ |
+ int nStack; /* Depth of stack */ |
+ int bTrace; /* True for tracing output */ |
+}; |
+ |
+/* |
+** Write an error. |
+*/ |
+static void ofstError(GState *p, const char *zFormat, ...){ |
+ va_list ap; |
+ sqlite3_free(p->zErr); |
+ va_start(ap, zFormat); |
+ p->zErr = sqlite3_vmprintf(zFormat, ap); |
+ va_end(ap); |
+} |
+ |
+/* |
+** Write a trace message |
+*/ |
+static void ofstTrace(GState *p, const char *zFormat, ...){ |
+ va_list ap; |
+ if( p->bTrace ){ |
+ va_start(ap, zFormat); |
+ vprintf(zFormat, ap); |
+ va_end(ap); |
+ } |
+} |
+ |
+/* |
+** Find the root page of the table and the column number of the column. |
+*/ |
+static void ofstRootAndColumn( |
+ GState *p, /* Global state */ |
+ const char *zFile, /* Name of the database file */ |
+ const char *zTable, /* Name of the table */ |
+ const char *zColumn /* Name of the column */ |
+){ |
+ sqlite3 *db = 0; |
+ sqlite3_stmt *pStmt = 0; |
+ char *zSql = 0; |
+ int rc; |
+ if( p->zErr ) return; |
+ rc = sqlite3_open(zFile, &db); |
+ if( rc ){ |
+ ofstError(p, "cannot open database file \"%s\"", zFile); |
+ goto rootAndColumn_exit; |
+ } |
+ zSql = sqlite3_mprintf("SELECT rootpage FROM sqlite_master WHERE name=%Q", |
+ zTable); |
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
+ if( rc ) ofstError(p, "%s: [%s]", sqlite3_errmsg(db), zSql); |
+ sqlite3_free(zSql); |
+ if( p->zErr ) goto rootAndColumn_exit; |
+ if( sqlite3_step(pStmt)!=SQLITE_ROW ){ |
+ ofstError(p, "cannot find table [%s]\n", zTable); |
+ sqlite3_finalize(pStmt); |
+ goto rootAndColumn_exit; |
+ } |
+ p->iRoot = sqlite3_column_int(pStmt , 0); |
+ sqlite3_finalize(pStmt); |
+ |
+ p->iCol = -1; |
+ zSql = sqlite3_mprintf("PRAGMA table_info(%Q)", zTable); |
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
+ if( rc ) ofstError(p, "%s: [%s}", sqlite3_errmsg(db), zSql); |
+ sqlite3_free(zSql); |
+ if( p->zErr ) goto rootAndColumn_exit; |
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
+ const char *zCol = sqlite3_column_text(pStmt, 1); |
+ if( strlen(zCol)==strlen(zColumn) |
+ && sqlite3_strnicmp(zCol, zColumn, strlen(zCol))==0 |
+ ){ |
+ p->iCol = sqlite3_column_int(pStmt, 0); |
+ break; |
+ } |
+ } |
+ sqlite3_finalize(pStmt); |
+ if( p->iCol<0 ){ |
+ ofstError(p, "no such column: %s.%s", zTable, zColumn); |
+ goto rootAndColumn_exit; |
+ } |
+ |
+ zSql = sqlite3_mprintf("PRAGMA page_size"); |
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); |
+ if( rc ) ofstError(p, "%s: [%s]", sqlite3_errmsg(db), zSql); |
+ sqlite3_free(zSql); |
+ if( p->zErr ) goto rootAndColumn_exit; |
+ if( sqlite3_step(pStmt)!=SQLITE_ROW ){ |
+ ofstError(p, "cannot find page size"); |
+ }else{ |
+ p->szPg = sqlite3_column_int(pStmt, 0); |
+ } |
+ sqlite3_finalize(pStmt); |
+ |
+rootAndColumn_exit: |
+ sqlite3_close(db); |
+ return; |
+} |
+ |
+/* |
+** Pop a page from the stack |
+*/ |
+static void ofstPopPage(GState *p){ |
+ if( p->nStack<=0 ) return; |
+ p->nStack--; |
+ sqlite3_free(p->aStack[p->nStack]); |
+ p->pgno = p->aPgno[p->nStack-1]; |
+ p->aPage = p->aStack[p->nStack-1]; |
+} |
+ |
+ |
+/* |
+** Push a new page onto the stack. |
+*/ |
+static void ofstPushPage(GState *p, int pgno){ |
+ u8 *pPage; |
+ size_t got; |
+ if( p->zErr ) return; |
+ if( p->nStack >= ArraySize(p->aStack) ){ |
+ ofstError(p, "page stack overflow"); |
+ return; |
+ } |
+ p->aPgno[p->nStack] = pgno; |
+ p->aStack[p->nStack] = pPage = sqlite3_malloc( p->szPg ); |
+ if( pPage==0 ){ |
+ fprintf(stderr, "out of memory\n"); |
+ exit(1); |
+ } |
+ p->nStack++; |
+ p->aPage = pPage; |
+ p->pgno = pgno; |
+ fseek(p->f, (pgno-1)*p->szPg, SEEK_SET); |
+ got = fread(pPage, 1, p->szPg, p->f); |
+ if( got!=p->szPg ){ |
+ ofstError(p, "unable to read page %d", pgno); |
+ ofstPopPage(p); |
+ } |
+} |
+ |
+/* Read a two-byte integer at the given offset into the current page */ |
+static int ofst2byte(GState *p, int ofst){ |
+ int x = p->aPage[ofst]; |
+ return (x<<8) + p->aPage[ofst+1]; |
+} |
+ |
+/* Read a four-byte integer at the given offset into the current page */ |
+static int ofst4byte(GState *p, int ofst){ |
+ int x = p->aPage[ofst]; |
+ x = (x<<8) + p->aPage[ofst+1]; |
+ x = (x<<8) + p->aPage[ofst+2]; |
+ x = (x<<8) + p->aPage[ofst+3]; |
+ return x; |
+} |
+ |
+/* Read a variable-length integer. Update the offset */ |
+static sqlite3_int64 ofstVarint(GState *p, int *pOfst){ |
+ sqlite3_int64 x = 0; |
+ u8 *a = &p->aPage[*pOfst]; |
+ int n = 0; |
+ while( n<8 && (a[0] & 0x80)!=0 ){ |
+ x = (x<<7) + (a[0] & 0x7f); |
+ n++; |
+ a++; |
+ } |
+ if( n==8 ){ |
+ x = (x<<8) + a[0]; |
+ }else{ |
+ x = (x<<7) + a[0]; |
+ } |
+ *pOfst += (n+1); |
+ return x; |
+} |
+ |
+/* Return the absolute offset into a file for the given offset |
+** into the current page */ |
+static int ofstInFile(GState *p, int ofst){ |
+ return p->szPg*(p->pgno-1) + ofst; |
+} |
+ |
+/* Return the size (in bytes) of the data corresponding to the |
+** given serial code */ |
+static int ofstSerialSize(int scode){ |
+ if( scode<5 ) return scode; |
+ if( scode==5 ) return 6; |
+ if( scode<8 ) return 8; |
+ if( scode<12 ) return 0; |
+ return (scode-12)/2; |
+} |
+ |
+/* Forward reference */ |
+static void ofstWalkPage(GState*, int); |
+ |
+/* Walk an interior btree page */ |
+static void ofstWalkInteriorPage(GState *p){ |
+ int nCell; |
+ int i; |
+ int ofst; |
+ int iChild; |
+ |
+ nCell = ofst2byte(p, 3); |
+ for(i=0; i<nCell; i++){ |
+ ofst = ofst2byte(p, 12+i*2); |
+ iChild = ofst4byte(p, ofst); |
+ ofstWalkPage(p, iChild); |
+ if( p->zErr ) return; |
+ } |
+ ofstWalkPage(p, ofst4byte(p, 8)); |
+} |
+ |
+/* Walk a leaf btree page */ |
+static void ofstWalkLeafPage(GState *p){ |
+ int nCell; |
+ int i; |
+ int ofst; |
+ int nPayload; |
+ sqlite3_int64 rowid; |
+ int nHdr; |
+ int j; |
+ int scode; |
+ int sz; |
+ int dataOfst; |
+ char zMsg[200]; |
+ |
+ nCell = ofst2byte(p, 3); |
+ for(i=0; i<nCell; i++){ |
+ ofst = ofst2byte(p, 8+i*2); |
+ nPayload = ofstVarint(p, &ofst); |
+ rowid = ofstVarint(p, &ofst); |
+ if( nPayload > p->szPg-35 ){ |
+ sqlite3_snprintf(sizeof(zMsg), zMsg, |
+ "# overflow rowid %lld", rowid); |
+ printf("%s\n", zMsg); |
+ continue; |
+ } |
+ dataOfst = ofst; |
+ nHdr = ofstVarint(p, &ofst); |
+ dataOfst += nHdr; |
+ for(j=0; j<p->iCol; j++){ |
+ scode = ofstVarint(p, &ofst); |
+ dataOfst += ofstSerialSize(scode); |
+ } |
+ scode = ofstVarint(p, &ofst); |
+ sz = ofstSerialSize(scode); |
+ sqlite3_snprintf(sizeof(zMsg), zMsg, |
+ "rowid %12lld size %5d offset %8d", |
+ rowid, sz, ofstInFile(p, dataOfst)); |
+ printf("%s\n", zMsg); |
+ } |
+} |
+ |
+/* |
+** Output results from a single page. |
+*/ |
+static void ofstWalkPage(GState *p, int pgno){ |
+ if( p->zErr ) return; |
+ ofstPushPage(p, pgno); |
+ if( p->zErr ) return; |
+ if( p->aPage[0]==5 ){ |
+ ofstWalkInteriorPage(p); |
+ }else if( p->aPage[0]==13 ){ |
+ ofstWalkLeafPage(p); |
+ }else{ |
+ ofstError(p, "page %d has a faulty type byte: %d", pgno, p->aPage[0]); |
+ } |
+ ofstPopPage(p); |
+} |
+ |
+int main(int argc, char **argv){ |
+ GState g; |
+ memset(&g, 0, sizeof(g)); |
+ if( argc>2 && strcmp(argv[1],"--trace")==0 ){ |
+ g.bTrace = 1; |
+ argc--; |
+ argv++; |
+ } |
+ if( argc!=4 ){ |
+ fprintf(stderr, "Usage: %s DATABASE TABLE COLUMN\n", *argv); |
+ exit(1); |
+ } |
+ ofstRootAndColumn(&g, argv[1], argv[2], argv[3]); |
+ if( g.zErr ){ |
+ fprintf(stderr, "%s\n", g.zErr); |
+ exit(1); |
+ } |
+ ofstTrace(&g, "# szPg = %d\n", g.szPg); |
+ ofstTrace(&g, "# iRoot = %d\n", g.iRoot); |
+ ofstTrace(&g, "# iCol = %d\n", g.iCol); |
+ g.f = fopen(argv[1], "rb"); |
+ if( g.f==0 ){ |
+ fprintf(stderr, "cannot open \"%s\"\n", argv[1]); |
+ exit(1); |
+ } |
+ ofstWalkPage(&g, g.iRoot); |
+ if( g.zErr ){ |
+ fprintf(stderr, "%s\n", g.zErr); |
+ exit(1); |
+ } |
+ return 0; |
+} |