static struct GlobalData {
i64 pagesize; /* Size of a database page */
+ i64 usablesize; /* pagesize-nRes */
int dbfd; /* File descriptor for reading the DB */
u32 mxPage; /* Last page number */
+ u32 nRes; /* Amount of reserve space */
int perLine; /* HEX elements to print per line */
int bRaw; /* True to access db file via OS APIs */
int bCSV; /* CSV output for "pgidx" */
+ int bTmstmp; /* Interpret tmstmpvfs tags on "pgidx" */
sqlite3_file *pFd; /* File descriptor for non-raw mode */
sqlite3 *pDb; /* Database handle that owns pFd */
-} g = {1024, -1, 0, 16, 0, 0, 0};
+ char **zPageUse; /* Use for each page */
+ struct TmstmpTag {
+ unsigned char a[16]; /* tmstmpvfs tag for each page */
+ } *aPageTag;
+} g = {4096, 4096, -1, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0};
/*
** Convert the var-int format into i64. Return the number of bytes
static unsigned char *fileRead(sqlite3_int64 ofst, int nByte){
unsigned char *aData;
int got;
+ int rc;
aData = sqlite3_malloc64(32+(i64)nByte);
if( aData==0 ) out_of_memory();
memset(aData, 0, nByte+32);
if( g.bRaw==0 ){
- int rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst);
+ rc = g.pFd->pMethods->xRead(g.pFd, (void*)aData, nByte, ofst);
if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
fprintf(stderr, "error in xRead() - %d\n", rc);
exit(1);
}else{
lseek(g.dbfd, (long)ofst, SEEK_SET);
got = read(g.dbfd, aData, nByte);
- if( got>0 && got<nByte ) memset(aData+got, 0, nByte-got);
+ if( got==nByte ){
+ rc = SQLITE_OK;
+ }else if( got>0 && got<nByte ){
+ memset(aData+got, 0, nByte-got);
+ rc = SQLITE_IOERR_SHORT_READ;
+ }else{
+ memset(aData,0,nByte);
+ rc = SQLITE_IOERR;
+ }
+ }
+ if( g.aPageTag && nByte==(int)g.pagesize ){
+ unsigned int pgno = (unsigned int)(ofst/g.pagesize);
+ if( pgno>=0 && pgno<=g.mxPage ){
+ memcpy(g.aPageTag[pgno].a, &aData[nByte-16], 16);
+ }
}
return aData;
}
i64 nLocal;
if( cType==13 ){
/* Table leaf */
- maxLocal = g.pagesize-35;
- minLocal = (g.pagesize-12)*32/255-23;
+ maxLocal = g.usablesize-35;
+ minLocal = (g.usablesize-12)*32/255-23;
}else{
- maxLocal = (g.pagesize-12)*64/255-23;
- minLocal = (g.pagesize-12)*32/255-23;
+ maxLocal = (g.usablesize-12)*64/255-23;
+ minLocal = (g.usablesize-12)*32/255-23;
}
if( nPayload>maxLocal ){
- surplus = minLocal + (nPayload-minLocal)%(g.pagesize-4);
+ surplus = minLocal + (nPayload-minLocal)%(g.usablesize-4);
if( surplus<=maxLocal ){
nLocal = surplus;
}else{
print_decode_line(a, 4, 4, "Number of entries on this page");
if( detail ){
n = decodeInt32(&a[4]);
- for(i=0; i<n && i<g.pagesize/4; i++){
+ for(i=0; i<n && i<g.usablesize/4; i++){
u32 x = decodeInt32(&a[8+4*i]);
char zIdx[13];
sprintf(zIdx, "[%d]", i);
}
}
-/*
-** A short text comment on the use of each page.
-*/
-static char **zPageUse;
-
/*
** Add a comment on the use of a page.
*/
sqlite3_free(zMsg);
return;
}
- if( zPageUse[pgno]!=0 ){
+ if( g.zPageUse[pgno]!=0 ){
printf("ERROR: page %d used multiple times:\n", pgno);
- printf("ERROR: previous: %s\n", zPageUse[pgno]);
+ printf("ERROR: previous: %s\n", g.zPageUse[pgno]);
printf("ERROR: current: %s\n", zMsg);
- sqlite3_free(zPageUse[pgno]);
+ sqlite3_free(g.zPageUse[pgno]);
}
- zPageUse[pgno] = zMsg;
+ g.zPageUse[pgno] = zMsg;
}
/*
u32 ofst;
cellidx = cellstart + i*2;
- if( cellidx+1 >= g.pagesize ){
+ if( cellidx+1 >= g.usablesize ){
printf("ERROR: page %d too many cells (%d)\n", pgno, nCell);
break;
}
ofst = a[cellidx]*256 + a[cellidx+1];
- if( ofst<cellidx+2 || ofst+4>=g.pagesize ){
+ if( ofst<cellidx+2 || ofst+4>=g.usablesize ){
printf("ERROR: page %d cell %d out of bounds\n", pgno, i);
continue;
}
a = fileRead((pgno-1)*g.pagesize, g.pagesize);
iNext = decodeInt32(a);
n = decodeInt32(a+4);
- if( n>(g.pagesize - 8)/4 ){
+ if( n>(g.usablesize - 8)/4 ){
printf("ERROR: page %d too many freelist entries (%d)\n", pgno, n);
- n = (g.pagesize - 8)/4;
+ n = (g.usablesize - 8)/4;
}
for(i=0; i<n; i++){
int child = decodeInt32(a + (i*4+8));
/* Open the database file */
db = openDatabase(zPrg, zDbName);
- /* Set up global variables zPageUse[] and g.mxPage to record page
+ /* Set up global variables g.zPageUse[] and g.mxPage to record page
** usages */
- zPageUse = sqlite3_malloc64( sizeof(zPageUse[0])*(g.mxPage+1) );
- if( zPageUse==0 ) out_of_memory();
- memset(zPageUse, 0, sizeof(zPageUse[0])*(g.mxPage+1));
+ g.zPageUse = sqlite3_malloc64( sizeof(g.zPageUse[0])*(g.mxPage+1) );
+ if( g.zPageUse==0 ) out_of_memory();
+ memset(g.zPageUse, 0, sizeof(g.zPageUse[0])*(g.mxPage+1));
/* Discover the usage of each page */
a = fileRead(0, 100);
+ if( g.bTmstmp && a[20]==16 ){
+ g.aPageTag = sqlite3_malloc64( sizeof(struct TmstmpTag)*(g.mxPage+1) );
+ if( g.aPageTag==0 ) out_of_memory();
+ memset(g.aPageTag, 0, sizeof(struct TmstmpTag)*(g.mxPage+1) );
+ }else{
+ g.bTmstmp = 0;
+ g.aPageTag = 0;
+ }
page_usage_freelist(decodeInt32(a+32));
page_usage_ptrmap(a);
sqlite3_free(a);
/* Print the report and free memory used */
if( g.bCSV ){
- printf("pgno,txt,parent,child,ovfl\r\n");
+ if( g.bTmstmp ){
+ printf("pgno,tm,frame,flg,salt,parent,child,ovfl,txt\r\n");
+ }else{
+ printf("pgno,parent,child,ovfl,txt\r\n");
+ }
}
for(i=1; i<=g.mxPage; i++){
- if( zPageUse[i]==0 ){
- zPageUse[i] = sqlite3_mprintf("???");
- if( zPageUse[i]==0 ) continue;
+ if( g.zPageUse[i]==0 ){
+ g.zPageUse[i] = sqlite3_mprintf("???");
+ if( g.zPageUse[i]==0 ) continue;
}
if( !g.bCSV ){
- printf("%5u: %s\n", i, zPageUse[i]);
+ printf("%5u: %s\n", i, g.zPageUse[i]);
}else{
- const char *z = zPageUse[i];
+ const char *z = g.zPageUse[i];
const char *s;
- printf("%u,\"%s\",", i, zPageUse[i]);
+ printf("%u,", i);
+ if( g.bTmstmp ){
+ const unsigned char *a = g.aPageTag[i].a;
+ sqlite3_uint64 tm = 0;
+ unsigned int x;
+ int k;
+ for(k=2; k<=7; k++) tm = (tm<<8)+a[k];
+ printf("%llu.%03u,", tm/1000, (unsigned int)(tm%1000));
+ for(x=0, k=8; k<=11; k++) x = (x<<8)+a[k];
+ printf("%u,", x);
+ printf("%u,", a[12]);
+ for(x=0, k=13; k<=15; k++) x = (x<<8)+a[k];
+ printf("%u,", x);
+ }
if( (s = strstr(z, " of page "))!=0 ){
printf("%d,", atoi(s+9));
}else if( (s = strstr(z, " of trunk page "))!=0 ){
printf("-1,");
}
if( strncmp(z,"overflow ", 9)==0 ){
- printf("%d\r\n", atoi(z+9));
+ printf("%d,", atoi(z+9));
}else{
- printf("-1\r\n");
+ printf("-1,");
}
+ printf("\"%s\"\r\n", z);
}
}
for(i=1; i<=g.mxPage; i++){
- sqlite3_free(zPageUse[i]);
+ sqlite3_free(g.zPageUse[i]);
}
- sqlite3_free(zPageUse);
- zPageUse = 0;
+ sqlite3_free(g.zPageUse);
+ g.zPageUse = 0;
}
/*
fprintf(stderr, "Usage %s ?--uri? FILENAME ?args...?\n\n", argv0);
fprintf(stderr,
"switches:\n"
- " --raw Read db file directly, bypassing SQLite VFS\n"
" --csv CSV output for \"pgidx\"\n"
+ " --raw Read db file directly, bypassing SQLite VFS\n"
+ " --tmstmp Interpret tmstmpvfs tags\n"
"args:\n"
" dbheader Show database header\n"
" pgidx Index of how each page is used\n"
azArg++;
nArg--;
}else
+ if( strcmp("-tmstmp", z)==0 ){
+ g.bTmstmp = 1;
+ azArg++;
+ nArg--;
+ }else
{
usage(zPrg);
exit(1);
fileOpen(zPrg, azArg[1]);
szFile = fileGetsize();
- zPgSz = fileRead(16, 2);
- g.pagesize = zPgSz[0]*256 + zPgSz[1]*65536;
- if( g.pagesize==0 ) g.pagesize = 1024;
+ zPgSz = fileRead(0, 24);
+ g.pagesize = zPgSz[16]*256 + zPgSz[17]*65536;
+ if( g.pagesize==0 ) g.pagesize = 4096;
+ g.nRes = zPgSz[20];
+ g.usablesize = g.pagesize - g.nRes;
sqlite3_free(zPgSz);
-
- printf("Pagesize: %d\n", (int)g.pagesize);
g.mxPage = (u32)((szFile+g.pagesize-1)/g.pagesize);
if( !g.bCSV ){
+ printf("Pagesize: %d\n", (int)g.pagesize);
+ if( g.nRes ) printf("Useable-size: %d\n", (int)g.usablesize);
printf("Available pages: 1..%u\n", g.mxPage);
}
if( nArg==2 ){