Index: third_party/sqlite/sqlite-src-3100200/src/dbstat.c |
diff --git a/third_party/sqlite/sqlite-src-3080704/src/test_stat.c b/third_party/sqlite/sqlite-src-3100200/src/dbstat.c |
similarity index 73% |
copy from third_party/sqlite/sqlite-src-3080704/src/test_stat.c |
copy to third_party/sqlite/sqlite-src-3100200/src/dbstat.c |
index 615df3d80fe155847516cf00b5a5c0f0e129ea25..ae55d6b803800f9ae5c76401e3ccc9b330fe7105 100644 |
--- a/third_party/sqlite/sqlite-src-3080704/src/test_stat.c |
+++ b/third_party/sqlite/sqlite-src-3100200/src/dbstat.c |
@@ -16,13 +16,14 @@ |
** information from an SQLite database in order to implement the |
** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script |
** for an example implementation. |
+** |
+** Additional information is available on the "dbstat.html" page of the |
+** official SQLite documentation. |
*/ |
-#ifndef SQLITE_AMALGAMATION |
-# include "sqliteInt.h" |
-#endif |
- |
-#ifndef SQLITE_OMIT_VIRTUALTABLE |
+#include "sqliteInt.h" /* Requires access to internal data structures */ |
+#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \ |
+ && !defined(SQLITE_OMIT_VIRTUALTABLE) |
/* |
** Page paths: |
@@ -66,7 +67,8 @@ |
" unused INTEGER, /* Bytes of unused space on this page */" \ |
" mx_payload INTEGER, /* Largest payload size of all cells */" \ |
" pgoffset INTEGER, /* Offset of page in file */" \ |
- " pgsize INTEGER /* Size of the page */" \ |
+ " pgsize INTEGER, /* Size of the page */" \ |
+ " schema TEXT HIDDEN /* Database schema being analyzed */" \ |
");" |
@@ -104,6 +106,7 @@ struct StatCursor { |
sqlite3_vtab_cursor base; |
sqlite3_stmt *pStmt; /* Iterates through set of root pages */ |
int isEof; /* After pStmt has returned SQLITE_DONE */ |
+ int iDb; /* Schema used for this query */ |
StatPage aPage[32]; |
int iPage; /* Current entry in aPage[] */ |
@@ -124,6 +127,7 @@ struct StatCursor { |
struct StatTable { |
sqlite3_vtab base; |
sqlite3 *db; |
+ int iDb; /* Index of database to analyze */ |
}; |
#ifndef get2byte |
@@ -140,15 +144,34 @@ static int statConnect( |
sqlite3_vtab **ppVtab, |
char **pzErr |
){ |
- StatTable *pTab; |
+ StatTable *pTab = 0; |
+ int rc = SQLITE_OK; |
+ int iDb; |
+ |
+ if( argc>=4 ){ |
+ iDb = sqlite3FindDbName(db, argv[3]); |
+ if( iDb<0 ){ |
+ *pzErr = sqlite3_mprintf("no such database: %s", argv[3]); |
+ return SQLITE_ERROR; |
+ } |
+ }else{ |
+ iDb = 0; |
+ } |
+ rc = sqlite3_declare_vtab(db, VTAB_SCHEMA); |
+ if( rc==SQLITE_OK ){ |
+ pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable)); |
+ if( pTab==0 ) rc = SQLITE_NOMEM; |
+ } |
- pTab = (StatTable *)sqlite3_malloc(sizeof(StatTable)); |
- memset(pTab, 0, sizeof(StatTable)); |
- pTab->db = db; |
+ assert( rc==SQLITE_OK || pTab==0 ); |
+ if( rc==SQLITE_OK ){ |
+ memset(pTab, 0, sizeof(StatTable)); |
+ pTab->db = db; |
+ pTab->iDb = iDb; |
+ } |
- sqlite3_declare_vtab(db, VTAB_SCHEMA); |
- *ppVtab = &pTab->base; |
- return SQLITE_OK; |
+ *ppVtab = (sqlite3_vtab*)pTab; |
+ return rc; |
} |
/* |
@@ -161,9 +184,32 @@ static int statDisconnect(sqlite3_vtab *pVtab){ |
/* |
** There is no "best-index". This virtual table always does a linear |
-** scan of the binary VFS log file. |
+** scan. However, a schema=? constraint should cause this table to |
+** operate on a different database schema, so check for it. |
+** |
+** idxNum is normally 0, but will be 1 if a schema=? constraint exists. |
*/ |
static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
+ int i; |
+ |
+ pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */ |
+ |
+ /* Look for a valid schema=? constraint. If found, change the idxNum to |
+ ** 1 and request the value of that constraint be sent to xFilter. And |
+ ** lower the cost estimate to encourage the constrained version to be |
+ ** used. |
+ */ |
+ for(i=0; i<pIdxInfo->nConstraint; i++){ |
+ if( pIdxInfo->aConstraint[i].usable==0 ) continue; |
+ if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; |
+ if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue; |
+ pIdxInfo->idxNum = 1; |
+ pIdxInfo->estimatedCost = 1.0; |
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1; |
+ pIdxInfo->aConstraintUsage[i].omit = 1; |
+ break; |
+ } |
+ |
/* Records are always returned in ascending order of (name, path). |
** If this will satisfy the client, set the orderByConsumed flag so that |
@@ -183,7 +229,6 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
pIdxInfo->orderByConsumed = 1; |
} |
- pIdxInfo->estimatedCost = 10.0; |
return SQLITE_OK; |
} |
@@ -193,22 +238,14 @@ static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ |
static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ |
StatTable *pTab = (StatTable *)pVTab; |
StatCursor *pCsr; |
- int rc; |
- |
- pCsr = (StatCursor *)sqlite3_malloc(sizeof(StatCursor)); |
- memset(pCsr, 0, sizeof(StatCursor)); |
- pCsr->base.pVtab = pVTab; |
- rc = sqlite3_prepare_v2(pTab->db, |
- "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" |
- " UNION ALL " |
- "SELECT name, rootpage, type FROM sqlite_master WHERE rootpage!=0" |
- " ORDER BY name", -1, |
- &pCsr->pStmt, 0 |
- ); |
- if( rc!=SQLITE_OK ){ |
- sqlite3_free(pCsr); |
- return rc; |
+ pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor)); |
+ if( pCsr==0 ){ |
+ return SQLITE_NOMEM; |
+ }else{ |
+ memset(pCsr, 0, sizeof(StatCursor)); |
+ pCsr->base.pVtab = pVTab; |
+ pCsr->iDb = pTab->iDb; |
} |
*ppCursor = (sqlite3_vtab_cursor *)pCsr; |
@@ -217,11 +254,13 @@ static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ |
static void statClearPage(StatPage *p){ |
int i; |
- for(i=0; i<p->nCell; i++){ |
- sqlite3_free(p->aCell[i].aOvfl); |
+ if( p->aCell ){ |
+ for(i=0; i<p->nCell; i++){ |
+ sqlite3_free(p->aCell[i].aOvfl); |
+ } |
+ sqlite3_free(p->aCell); |
} |
sqlite3PagerUnref(p->pPg); |
- sqlite3_free(p->aCell); |
sqlite3_free(p->zPath); |
memset(p, 0, sizeof(StatPage)); |
} |
@@ -235,6 +274,7 @@ static void statResetCsr(StatCursor *pCsr){ |
pCsr->iPage = 0; |
sqlite3_free(pCsr->zPath); |
pCsr->zPath = 0; |
+ pCsr->isEof = 0; |
} |
/* |
@@ -301,9 +341,13 @@ static int statDecodePage(Btree *pBt, StatPage *p){ |
if( p->nCell ){ |
int i; /* Used to iterate through cells */ |
- int nUsable = szPage - sqlite3BtreeGetReserve(pBt); |
+ int nUsable; /* Usable bytes per page */ |
- p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell)); |
+ sqlite3BtreeEnter(pBt); |
+ nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt); |
+ sqlite3BtreeLeave(pBt); |
+ p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell)); |
+ if( p->aCell==0 ) return SQLITE_NOMEM; |
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell)); |
for(i=0; i<p->nCell; i++){ |
@@ -335,13 +379,14 @@ static int statDecodePage(Btree *pBt, StatPage *p){ |
int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4); |
pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4); |
pCell->nOvfl = nOvfl; |
- pCell->aOvfl = sqlite3_malloc(sizeof(u32)*nOvfl); |
+ pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl); |
+ if( pCell->aOvfl==0 ) return SQLITE_NOMEM; |
pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]); |
for(j=1; j<nOvfl; j++){ |
int rc; |
u32 iPrev = pCell->aOvfl[j-1]; |
DbPage *pPg = 0; |
- rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg); |
+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg, 0); |
if( rc!=SQLITE_OK ){ |
assert( pPg==0 ); |
return rc; |
@@ -363,7 +408,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){ |
*/ |
static void statSizeAndOffset(StatCursor *pCsr){ |
StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab; |
- Btree *pBt = pTab->db->aDb[0].pBt; |
+ Btree *pBt = pTab->db->aDb[pTab->iDb].pBt; |
Pager *pPager = sqlite3BtreePager(pBt); |
sqlite3_file *fd; |
sqlite3_int64 x[2]; |
@@ -377,7 +422,7 @@ static void statSizeAndOffset(StatCursor *pCsr){ |
*/ |
fd = sqlite3PagerFile(pPager); |
x[0] = pCsr->iPageno; |
- if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ |
+ if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){ |
pCsr->iOffset = x[0]; |
pCsr->szPage = (int)x[1]; |
} |
@@ -389,9 +434,10 @@ static void statSizeAndOffset(StatCursor *pCsr){ |
static int statNext(sqlite3_vtab_cursor *pCursor){ |
int rc; |
int nPayload; |
+ char *z; |
StatCursor *pCsr = (StatCursor *)pCursor; |
StatTable *pTab = (StatTable *)pCursor->pVtab; |
- Btree *pBt = pTab->db->aDb[0].pBt; |
+ Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt; |
Pager *pPager = sqlite3BtreePager(pBt); |
sqlite3_free(pCsr->zPath); |
@@ -408,11 +454,12 @@ statNextRestart: |
pCsr->isEof = 1; |
return sqlite3_reset(pCsr->pStmt); |
} |
- rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg); |
+ rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0); |
pCsr->aPage[0].iPgno = iRoot; |
pCsr->aPage[0].iCell = 0; |
- pCsr->aPage[0].zPath = sqlite3_mprintf("/"); |
+ pCsr->aPage[0].zPath = z = sqlite3_mprintf("/"); |
pCsr->iPage = 0; |
+ if( z==0 ) rc = SQLITE_NOMEM; |
}else{ |
pCsr->isEof = 1; |
return sqlite3_reset(pCsr->pStmt); |
@@ -425,13 +472,17 @@ statNextRestart: |
while( p->iCell<p->nCell ){ |
StatCell *pCell = &p->aCell[p->iCell]; |
if( pCell->iOvfl<pCell->nOvfl ){ |
- int nUsable = sqlite3BtreeGetPageSize(pBt)-sqlite3BtreeGetReserve(pBt); |
+ int nUsable; |
+ sqlite3BtreeEnter(pBt); |
+ nUsable = sqlite3BtreeGetPageSize(pBt) - |
+ sqlite3BtreeGetReserveNoMutex(pBt); |
+ sqlite3BtreeLeave(pBt); |
pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); |
pCsr->iPageno = pCell->aOvfl[pCell->iOvfl]; |
pCsr->zPagetype = "overflow"; |
pCsr->nCell = 0; |
pCsr->nMxPayload = 0; |
- pCsr->zPath = sqlite3_mprintf( |
+ pCsr->zPath = z = sqlite3_mprintf( |
"%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl |
); |
if( pCell->iOvfl<pCell->nOvfl-1 ){ |
@@ -443,7 +494,7 @@ statNextRestart: |
} |
pCell->iOvfl++; |
statSizeAndOffset(pCsr); |
- return SQLITE_OK; |
+ return z==0 ? SQLITE_NOMEM : SQLITE_OK; |
} |
if( p->iRightChildPg ) break; |
p->iCell++; |
@@ -463,10 +514,11 @@ statNextRestart: |
}else{ |
p[1].iPgno = p->aCell[p->iCell].iChildPg; |
} |
- rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg); |
+ rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0); |
p[1].iCell = 0; |
- p[1].zPath = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); |
+ p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell); |
p->iCell++; |
+ if( z==0 ) rc = SQLITE_NOMEM; |
} |
@@ -479,31 +531,34 @@ statNextRestart: |
pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0); |
pCsr->iPageno = p->iPgno; |
- statDecodePage(pBt, p); |
- statSizeAndOffset(pCsr); |
- |
- switch( p->flags ){ |
- case 0x05: /* table internal */ |
- case 0x02: /* index internal */ |
- pCsr->zPagetype = "internal"; |
- break; |
- case 0x0D: /* table leaf */ |
- case 0x0A: /* index leaf */ |
- pCsr->zPagetype = "leaf"; |
- break; |
- default: |
- pCsr->zPagetype = "corrupted"; |
- break; |
- } |
- pCsr->nCell = p->nCell; |
- pCsr->nUnused = p->nUnused; |
- pCsr->nMxPayload = p->nMxPayload; |
- pCsr->zPath = sqlite3_mprintf("%s", p->zPath); |
- nPayload = 0; |
- for(i=0; i<p->nCell; i++){ |
- nPayload += p->aCell[i].nLocal; |
+ rc = statDecodePage(pBt, p); |
+ if( rc==SQLITE_OK ){ |
+ statSizeAndOffset(pCsr); |
+ |
+ switch( p->flags ){ |
+ case 0x05: /* table internal */ |
+ case 0x02: /* index internal */ |
+ pCsr->zPagetype = "internal"; |
+ break; |
+ case 0x0D: /* table leaf */ |
+ case 0x0A: /* index leaf */ |
+ pCsr->zPagetype = "leaf"; |
+ break; |
+ default: |
+ pCsr->zPagetype = "corrupted"; |
+ break; |
+ } |
+ pCsr->nCell = p->nCell; |
+ pCsr->nUnused = p->nUnused; |
+ pCsr->nMxPayload = p->nMxPayload; |
+ pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath); |
+ if( z==0 ) rc = SQLITE_NOMEM; |
+ nPayload = 0; |
+ for(i=0; i<p->nCell; i++){ |
+ nPayload += p->aCell[i].nLocal; |
+ } |
+ pCsr->nPayload = nPayload; |
} |
- pCsr->nPayload = nPayload; |
} |
return rc; |
@@ -520,9 +575,43 @@ static int statFilter( |
int argc, sqlite3_value **argv |
){ |
StatCursor *pCsr = (StatCursor *)pCursor; |
- |
+ StatTable *pTab = (StatTable*)(pCursor->pVtab); |
+ char *zSql; |
+ int rc = SQLITE_OK; |
+ char *zMaster; |
+ |
+ if( idxNum==1 ){ |
+ const char *zDbase = (const char*)sqlite3_value_text(argv[0]); |
+ pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase); |
+ if( pCsr->iDb<0 ){ |
+ sqlite3_free(pCursor->pVtab->zErrMsg); |
+ pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase); |
+ return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM; |
+ } |
+ }else{ |
+ pCsr->iDb = pTab->iDb; |
+ } |
statResetCsr(pCsr); |
- return statNext(pCursor); |
+ sqlite3_finalize(pCsr->pStmt); |
+ pCsr->pStmt = 0; |
+ zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master"; |
+ zSql = sqlite3_mprintf( |
+ "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" |
+ " UNION ALL " |
+ "SELECT name, rootpage, type" |
+ " FROM \"%w\".%s WHERE rootpage!=0" |
+ " ORDER BY name", pTab->db->aDb[pCsr->iDb].zName, zMaster); |
+ if( zSql==0 ){ |
+ return SQLITE_NOMEM; |
+ }else{ |
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); |
+ sqlite3_free(zSql); |
+ } |
+ |
+ if( rc==SQLITE_OK ){ |
+ rc = statNext(pCursor); |
+ } |
+ return rc; |
} |
static int statColumn( |
@@ -533,7 +622,7 @@ static int statColumn( |
StatCursor *pCsr = (StatCursor *)pCursor; |
switch( i ){ |
case 0: /* name */ |
- sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_STATIC); |
+ sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_TRANSIENT); |
break; |
case 1: /* path */ |
sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT); |
@@ -562,6 +651,12 @@ static int statColumn( |
case 9: /* pgsize */ |
sqlite3_result_int(ctx, pCsr->szPage); |
break; |
+ default: { /* schema */ |
+ sqlite3 *db = sqlite3_context_db_handle(ctx); |
+ int iDb = pCsr->iDb; |
+ sqlite3_result_text(ctx, db->aDb[iDb].zName, -1, SQLITE_STATIC); |
+ break; |
+ } |
} |
return SQLITE_OK; |
} |
@@ -572,7 +667,10 @@ static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ |
return SQLITE_OK; |
} |
-int sqlite3_dbstat_register(sqlite3 *db){ |
+/* |
+** Invoke this routine to register the "dbstat" virtual table module |
+*/ |
+int sqlite3DbstatRegister(sqlite3 *db){ |
static sqlite3_module dbstat_module = { |
0, /* iVersion */ |
statConnect, /* xCreate */ |
@@ -595,46 +693,8 @@ int sqlite3_dbstat_register(sqlite3 *db){ |
0, /* xFindMethod */ |
0, /* xRename */ |
}; |
- sqlite3_create_module(db, "dbstat", &dbstat_module, 0); |
- return SQLITE_OK; |
-} |
- |
-#endif |
- |
-#if defined(SQLITE_TEST) || TCLSH==2 |
-#include <tcl.h> |
- |
-static int test_dbstat( |
- void *clientData, |
- Tcl_Interp *interp, |
- int objc, |
- Tcl_Obj *CONST objv[] |
-){ |
-#ifdef SQLITE_OMIT_VIRTUALTABLE |
- Tcl_AppendResult(interp, "dbstat not available because of " |
- "SQLITE_OMIT_VIRTUALTABLE", (void*)0); |
- return TCL_ERROR; |
-#else |
- struct SqliteDb { sqlite3 *db; }; |
- char *zDb; |
- Tcl_CmdInfo cmdInfo; |
- |
- if( objc!=2 ){ |
- Tcl_WrongNumArgs(interp, 1, objv, "DB"); |
- return TCL_ERROR; |
- } |
- |
- zDb = Tcl_GetString(objv[1]); |
- if( Tcl_GetCommandInfo(interp, zDb, &cmdInfo) ){ |
- sqlite3* db = ((struct SqliteDb*)cmdInfo.objClientData)->db; |
- sqlite3_dbstat_register(db); |
- } |
- return TCL_OK; |
-#endif |
-} |
- |
-int SqlitetestStat_Init(Tcl_Interp *interp){ |
- Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0); |
- return TCL_OK; |
+ return sqlite3_create_module(db, "dbstat", &dbstat_module, 0); |
} |
-#endif /* if defined(SQLITE_TEST) || TCLSH==2 */ |
+#elif defined(SQLITE_ENABLE_DBSTAT_VTAB) |
+int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; } |
+#endif /* SQLITE_ENABLE_DBSTAT_VTAB */ |