Index: third_party/sqlite/src/src/recover.c |
diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/recover.c |
index f7e108fc6496d59ea34b5596d60e374ff927091f..9e6c35c542dbeee82e2b51dd8aa6426479bca8bc 100644 |
--- a/third_party/sqlite/src/src/recover.c |
+++ b/third_party/sqlite/src/src/recover.c |
@@ -371,19 +371,22 @@ static int ascii_strcasecmp(const char *s1, const char *s2){ |
} |
/* Provide access to the pages of a SQLite database in a way similar to SQLite's |
-** Pager. Will be re-implemented in terms of sqlite3_file. |
+** Pager. |
*/ |
typedef struct RecoverPager RecoverPager; |
struct RecoverPager { |
- Pager *pSqlitePager; /* SQLite's pager. */ |
+ sqlite3_file *pSqliteFile; /* Reference to database's file handle */ |
+ u32 nPageSize; /* Size of pages in pSqliteFile */ |
}; |
static void pagerDestroy(RecoverPager *pPager){ |
+ pPager->pSqliteFile->pMethods->xUnlock(pPager->pSqliteFile, SQLITE_LOCK_NONE); |
memset(pPager, 0xA5, sizeof(*pPager)); |
sqlite3_free(pPager); |
} |
-static int pagerCreate(Pager *pSqlitePager, u32 nPageSize, |
+/* pSqliteFile should already have a SHARED lock. */ |
+static int pagerCreate(sqlite3_file *pSqliteFile, u32 nPageSize, |
RecoverPager **ppPager){ |
RecoverPager *pPager = sqlite3_malloc(sizeof(RecoverPager)); |
if( !pPager ){ |
@@ -391,28 +394,35 @@ static int pagerCreate(Pager *pSqlitePager, u32 nPageSize, |
} |
memset(pPager, 0, sizeof(*pPager)); |
- pPager->pSqlitePager = pSqlitePager; |
+ pPager->pSqliteFile = pSqliteFile; |
+ pPager->nPageSize = nPageSize; |
*ppPager = pPager; |
return SQLITE_OK; |
} |
/* Matches DbPage (aka PgHdr) from SQLite internals. */ |
+/* TODO(shess): SQLite by default allocates page metadata in a single allocation |
+** such that the page's data and metadata are contiguous, see pcache1AllocPage |
+** in pcache1.c. I believe this was intended to reduce malloc churn. It means |
+** that Chromium's automated tooling would be unlikely to see page-buffer |
+** overruns. I believe that this code is safe, but for now replicate SQLite's |
+** approach with kExcessSpace. |
+*/ |
+const int kExcessSpace = 128; |
typedef struct RecoverPage RecoverPage; |
struct RecoverPage { |
- DbPage *pSqlitePage; /* SQLite's page. */ |
Pgno pgno; /* Page number for this page */ |
- void *pData; /* Page data */ |
+ void *pData; /* Page data for pgno */ |
RecoverPager *pPager; /* The pager this page is part of */ |
}; |
static void pageDestroy(RecoverPage *pPage){ |
- sqlite3PagerUnref(pPage->pSqlitePage); |
+ sqlite3_free(pPage->pData); |
memset(pPage, 0xA5, sizeof(*pPage)); |
sqlite3_free(pPage); |
} |
-static int pageCreate(RecoverPager *pPager, DbPage *pSqlitePage, |
- RecoverPage **ppPage){ |
+static int pageCreate(RecoverPager *pPager, u32 pgno, RecoverPage **ppPage){ |
RecoverPage *pPage = sqlite3_malloc(sizeof(RecoverPage)); |
if( !pPage ){ |
return SQLITE_NOMEM; |
@@ -420,33 +430,44 @@ static int pageCreate(RecoverPager *pPager, DbPage *pSqlitePage, |
memset(pPage, 0, sizeof(*pPage)); |
pPage->pPager = pPager; |
- pPage->pSqlitePage = pSqlitePage; |
- pPage->pgno = pSqlitePage->pgno; |
- pPage->pData = pSqlitePage->pData; |
+ pPage->pgno = pgno; |
+ pPage->pData = sqlite3_malloc(pPager->nPageSize + kExcessSpace); |
+ if( pPage->pData==NULL ){ |
+ pageDestroy(pPage); |
+ return SQLITE_NOMEM; |
+ } |
+ memset((u8 *)pPage->pData + pPager->nPageSize, 0, kExcessSpace); |
*ppPage = pPage; |
return SQLITE_OK; |
} |
static int pagerGetPage(RecoverPager *pPager, u32 iPage, RecoverPage **ppPage) { |
- DbPage *pSqlitePage; |
- int rc = sqlite3PagerGet(pPager->pSqlitePager, iPage, &pSqlitePage, 0); |
+ sqlite3_int64 iOfst; |
+ sqlite3_file *pFile = pPager->pSqliteFile; |
+ RecoverPage *pPage; |
+ int rc = pageCreate(pPager, iPage, &pPage); |
if( rc!=SQLITE_OK ){ |
return rc; |
} |
- rc = pageCreate(pPager, pSqlitePage, ppPage); |
- if( rc!=SQLITE_OK ){ |
- sqlite3PagerUnref(pSqlitePage); |
+ /* xRead() can return SQLITE_IOERR_SHORT_READ, which should be treated as |
+ ** SQLITE_OK plus an EOF indicator. The excess space is zero-filled. |
+ */ |
+ iOfst = ((sqlite3_int64)iPage - 1) * pPager->nPageSize; |
+ rc = pFile->pMethods->xRead(pFile, pPage->pData, pPager->nPageSize, iOfst); |
+ if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ |
+ pageDestroy(pPage); |
return rc; |
} |
+ *ppPage = pPage; |
return SQLITE_OK; |
} |
/* For some reason I kept making mistakes with offset calculations. */ |
static const unsigned char *PageData(RecoverPage *pPage, unsigned iOffset){ |
- assert( iOffset<=pPage->nPageSize ); |
+ assert( iOffset<=pPage->pPager->nPageSize ); |
return (unsigned char *)pPage->pData + iOffset; |
} |
@@ -467,8 +488,10 @@ static const unsigned char *PageHeader(RecoverPage *pPage){ |
/* Helper to fetch the pager and page size for the named database. */ |
static int GetPager(sqlite3 *db, const char *zName, |
RecoverPager **ppPager, unsigned *pnPageSize){ |
- Btree *pBt = NULL; |
int i, rc; |
+ unsigned nPageSize, nReservedSize; |
+ sqlite3_file *pFile = NULL; |
+ Btree *pBt = NULL; |
RecoverPager *pPager; |
for( i=0; i<db->nDb; ++i ){ |
if( ascii_strcasecmp(db->aDb[i].zName, zName)==0 ){ |
@@ -480,14 +503,32 @@ static int GetPager(sqlite3 *db, const char *zName, |
return SQLITE_ERROR; |
} |
- rc = pagerCreate(sqlite3BtreePager(pBt), 0, &pPager); |
+ rc = sqlite3_file_control(db, zName, SQLITE_FCNTL_FILE_POINTER, &pFile); |
+ if( rc!=SQLITE_OK ) { |
+ return rc; |
+ } else if( pFile==NULL ){ |
+ /* The documentation for sqlite3PagerFile() indicates it can return NULL if |
+ ** the file has not yet been opened. That should not be possible here... |
+ */ |
+ return SQLITE_MISUSE; |
+ } |
+ |
+ /* Get a shared lock to make sure the on-disk version of the file is truth. */ |
+ rc = pFile->pMethods->xLock(pFile, SQLITE_LOCK_SHARED); |
+ if( rc != SQLITE_OK ){ |
+ return rc; |
+ } |
+ |
+ nPageSize = sqlite3BtreeGetPageSize(pBt); |
+ nReservedSize = sqlite3BtreeGetOptimalReserve(pBt); |
+ rc = pagerCreate(pFile, nPageSize, &pPager); |
if( rc!=SQLITE_OK ){ |
+ pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE); |
return rc; |
} |
*ppPager = pPager; |
- *pnPageSize = |
- sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetOptimalReserve(pBt); |
+ *pnPageSize = nPageSize - nReservedSize; |
return SQLITE_OK; |
} |