1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-16 23:02:26 +03:00

Have the dbstat virtual table take a copy of each page buffer that it traverses instead of just a reference to the page-cache object. This avoids problems if an error causes transaction rollback while a dbstat cursor is open. dbsqlfuzz crash-417224040fee04f0f0e62b70265c518893b08769.

FossilOrigin-Name: 6ab25f8bd52d6412a9600143de364f6d8ad8e2c835315fafca6f54d5f38a49dc
This commit is contained in:
dan
2021-09-27 17:11:20 +00:00
parent 5a69d19eea
commit 6bcaba70ac
3 changed files with 50 additions and 17 deletions

View File

@@ -92,9 +92,8 @@ struct StatCell {
/* Size information for a single btree page */
struct StatPage {
u32 iPgno; /* Page number */
DbPage *pPg; /* Page content */
u8 *aPg; /* Page buffer from sqlite3_malloc() */
int iCell; /* Current cell */
char *zPath; /* Path to this page */
/* Variables populated by statDecodePage(): */
@@ -306,10 +305,11 @@ static void statClearCells(StatPage *p){
}
static void statClearPage(StatPage *p){
u8 *aPg = p->aPg;
statClearCells(p);
sqlite3PagerUnref(p->pPg);
sqlite3_free(p->zPath);
memset(p, 0, sizeof(StatPage));
p->aPg = aPg;
}
static void statResetCsr(StatCursor *pCsr){
@@ -320,6 +320,8 @@ static void statResetCsr(StatCursor *pCsr){
** this happens. dbsqlfuzz 9ed3e4e3816219d3509d711636c38542bf3f40b1. */
for(i=0; i<ArraySize(pCsr->aPage); i++){
statClearPage(&pCsr->aPage[i]);
sqlite3_free(pCsr->aPage[i].aPg);
pCsr->aPage[i].aPg = 0;
}
sqlite3_reset(pCsr->pStmt);
pCsr->iPage = 0;
@@ -386,7 +388,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
int isLeaf;
int szPage;
u8 *aData = sqlite3PagerGetData(p->pPg);
u8 *aData = p->aPg;
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
p->flags = aHdr[0];
@@ -516,6 +518,37 @@ static void statSizeAndOffset(StatCursor *pCsr){
}
}
/*
** Load a copy of the page data for page iPg into the buffer belonging
** to page object pPg. Allocate the buffer if necessary. Return SQLITE_OK
** if successful, or an SQLite error code otherwise.
*/
static int statGetPage(
Btree *pBt, /* Load page from this b-tree */
u32 iPg, /* Page number to load */
StatPage *pPg /* Load page into this object */
){
int pgsz = sqlite3BtreeGetPageSize(pBt);
DbPage *pDbPage = 0;
int rc;
if( pPg->aPg==0 ){
pPg->aPg = (u8*)sqlite3_malloc(pgsz);
if( pPg->aPg==0 ){
return SQLITE_NOMEM_BKPT;
}
}
rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPg, &pDbPage, 0);
if( rc==SQLITE_OK ){
const u8 *a = sqlite3PagerGetData(pDbPage);
memcpy(pPg->aPg, a, pgsz);
sqlite3PagerUnref(pDbPage);
}
return rc;
}
/*
** Move a DBSTAT cursor to the next entry. Normally, the next
** entry will be the next page, but in aggregated mode (pCsr->isAgg!=0),
@@ -534,7 +567,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
pCsr->zPath = 0;
statNextRestart:
if( pCsr->aPage[0].pPg==0 ){
if( pCsr->iPage<0 ){
/* Start measuring space on the next btree */
statResetCounts(pCsr);
rc = sqlite3_step(pCsr->pStmt);
@@ -546,7 +579,7 @@ statNextRestart:
pCsr->isEof = 1;
return sqlite3_reset(pCsr->pStmt);
}
rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
rc = statGetPage(pBt, iRoot, &pCsr->aPage[0]);
pCsr->aPage[0].iPgno = iRoot;
pCsr->aPage[0].iCell = 0;
if( !pCsr->isAgg ){
@@ -597,9 +630,8 @@ statNextRestart:
if( !p->iRightChildPg || p->iCell>p->nCell ){
statClearPage(p);
if( pCsr->iPage>0 ){
pCsr->iPage--;
}else if( pCsr->isAgg ){
pCsr->iPage--;
if( pCsr->isAgg && pCsr->iPage<0 ){
/* label-statNext-done: When computing aggregate space usage over
** an entire btree, this is the exit point from this function */
return SQLITE_OK;
@@ -618,7 +650,7 @@ statNextRestart:
}else{
p[1].iPgno = p->aCell[p->iCell].iChildPg;
}
rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
rc = statGetPage(pBt, p[1].iPgno, &p[1]);
pCsr->nPage++;
p[1].iCell = 0;
if( !pCsr->isAgg ){
@@ -748,6 +780,7 @@ static int statFilter(
}
if( rc==SQLITE_OK ){
pCsr->iPage = -1;
rc = statNext(pCursor);
}
return rc;