Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(975)

Unified Diff: third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch

Issue 1742763003: [sqlite] Pull out recent recover.c changes into new patch. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@zzsql_patch_icu
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch
diff --git a/third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch b/third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch
index 35a29b9c28ca7fb963260e92140d88ee0059d4bf..7d925c4291d5ca347e0f07fc8d4031f71c1438f9 100644
--- a/third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch
+++ b/third_party/sqlite/patches/0005-Virtual-table-supporting-recovery-of-corrupted-datab.patch
@@ -1,7 +1,7 @@
-From 29b8ad9263dbc700c002e9044c7c712a566a26d7 Mon Sep 17 00:00:00 2001
+From 31dd2fd1cdeba87223f6a46cc19bcea9eb8b2f38 Mon Sep 17 00:00:00 2001
From: Scott Hess <shess@chromium.org>
Date: Sat, 20 Jul 2013 11:42:21 -0700
-Subject: [PATCH 05/10] Virtual table supporting recovery of corrupted
+Subject: [PATCH 05/13] Virtual table supporting recovery of corrupted
databases.
"recover" implements a virtual table which uses the SQLite pager layer
@@ -19,14 +19,14 @@ third_party/sqlite/src/src/{recover,recover-alt}.c .
third_party/sqlite/src/Makefile.linux-gcc | 4 +
third_party/sqlite/src/main.mk | 5 +-
third_party/sqlite/src/src/main.c | 8 +
- third_party/sqlite/src/src/recover.c | 2167 ++++++++++++++++++++++++++++
+ third_party/sqlite/src/src/recover.c | 2281 ++++++++++++++++++++++++++++
third_party/sqlite/src/src/sqlite.h.in | 16 +
- third_party/sqlite/src/test/recover.test | 147 ++
+ third_party/sqlite/src/test/recover.test | 164 ++
third_party/sqlite/src/test/recover0.test | 532 +++++++
third_party/sqlite/src/test/recover1.test | 429 ++++++
third_party/sqlite/src/test/recover2.test | 157 ++
third_party/sqlite/src/tool/mksqlite3c.tcl | 2 +
- 11 files changed, 3467 insertions(+), 1 deletion(-)
+ 11 files changed, 3598 insertions(+), 1 deletion(-)
create mode 100644 third_party/sqlite/src/src/recover.c
create mode 100644 third_party/sqlite/src/test/recover.test
create mode 100644 third_party/sqlite/src/test/recover0.test
@@ -111,10 +111,10 @@ index 3be7c77..301808c 100644
rc = sqlite3IcuInit(db);
diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/recover.c
new file mode 100644
-index 0000000..8e3929d
+index 0000000..5ff6f78
--- /dev/null
+++ b/third_party/sqlite/src/src/recover.c
-@@ -0,0 +1,2167 @@
+@@ -0,0 +1,2281 @@
+/*
+** 2012 Jan 11
+**
@@ -354,6 +354,15 @@ index 0000000..8e3929d
+static const unsigned char kTableLeafPage = 0x0D;
+static const unsigned char kTableInteriorPage = 0x05;
+
++/* From section 1.2. */
++static const unsigned kiHeaderPageSizeOffset = 16;
++static const unsigned kiHeaderReservedSizeOffset = 20;
++static const unsigned kiHeaderEncodingOffset = 56;
++/* TODO(shess) |static const unsigned| fails creating the header in GetPager()
++** because |knHeaderSize| isn't |constexpr|. But this isn't C++, either.
++*/
++enum { knHeaderSize = 100};
++
+/* From section 1.5. */
+static const unsigned kiPageTypeOffset = 0;
+static const unsigned kiPageFreeBlockOffset = 1;
@@ -487,9 +496,104 @@ index 0000000..8e3929d
+ return ascii_strncasecmp(s1, s2, strlen(s1)+1);
+}
+
++/* Provide access to the pages of a SQLite database in a way similar to SQLite's
++** Pager.
++*/
++typedef struct RecoverPager RecoverPager;
++struct RecoverPager {
++ 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);
++}
++
++/* 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 ){
++ return SQLITE_NOMEM;
++ }
++
++ memset(pPager, 0, sizeof(*pPager));
++ 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 {
++ Pgno pgno; /* Page number for this page */
++ void *pData; /* Page data for pgno */
++ RecoverPager *pPager; /* The pager this page is part of */
++};
++
++static void pageDestroy(RecoverPage *pPage){
++ sqlite3_free(pPage->pData);
++ memset(pPage, 0xA5, sizeof(*pPage));
++ sqlite3_free(pPage);
++}
++
++static int pageCreate(RecoverPager *pPager, u32 pgno, RecoverPage **ppPage){
++ RecoverPage *pPage = sqlite3_malloc(sizeof(RecoverPage));
++ if( !pPage ){
++ return SQLITE_NOMEM;
++ }
++
++ memset(pPage, 0, sizeof(*pPage));
++ pPage->pPager = pPager;
++ 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) {
++ sqlite3_int64 iOfst;
++ sqlite3_file *pFile = pPager->pSqliteFile;
++ RecoverPage *pPage;
++ int rc = pageCreate(pPager, iPage, &pPage);
++ if( rc!=SQLITE_OK ){
++ return rc;
++ }
++
++ /* 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(DbPage *pPage, unsigned iOffset){
-+ assert( iOffset<=pPage->nPageSize );
++static const unsigned char *PageData(RecoverPage *pPage, unsigned iOffset){
++ assert( iOffset<=pPage->pPager->nPageSize );
+ return (unsigned char *)pPage->pData + iOffset;
+}
+
@@ -498,10 +602,9 @@ index 0000000..8e3929d
+ * the offsets in the page's header information are relative to the
+ * beginning of the page, NOT the end of the page header.
+ */
-+static const unsigned char *PageHeader(DbPage *pPage){
++static const unsigned char *PageHeader(RecoverPage *pPage){
+ if( pPage->pgno==1 ){
-+ const unsigned nDatabaseHeader = 100;
-+ return PageData(pPage, nDatabaseHeader);
++ return PageData(pPage, knHeaderSize);
+ }else{
+ return PageData(pPage, 0);
+ }
@@ -509,22 +612,74 @@ index 0000000..8e3929d
+
+/* Helper to fetch the pager and page size for the named database. */
+static int GetPager(sqlite3 *db, const char *zName,
-+ Pager **pPager, unsigned *pnPageSize){
-+ Btree *pBt = NULL;
-+ int i;
-+ for( i=0; i<db->nDb; ++i ){
-+ if( ascii_strcasecmp(db->aDb[i].zName, zName)==0 ){
-+ pBt = db->aDb[i].pBt;
-+ break;
++ RecoverPager **ppPager, unsigned *pnPageSize,
++ int *piEncoding){
++ int rc, iEncoding;
++ unsigned nPageSize, nReservedSize;
++ unsigned char header[knHeaderSize];
++ sqlite3_file *pFile = NULL;
++ RecoverPager *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;
++ }
++
++ /* Read the Initial header information. In case of SQLITE_IOERR_SHORT_READ,
++ ** the header is incomplete, which means no data could be recovered anyhow.
++ */
++ rc = pFile->pMethods->xRead(pFile, header, sizeof(header), 0);
++ if( rc != SQLITE_OK ){
++ pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE);
++ if( rc==SQLITE_IOERR_SHORT_READ ){
++ return SQLITE_CORRUPT;
+ }
++ return rc;
+ }
-+ if( !pBt ){
-+ return SQLITE_ERROR;
++
++ /* Page size must be a power of two between 512 and 32768 inclusive. */
++ nPageSize = decodeUnsigned16(header + kiHeaderPageSizeOffset);
++ if( (nPageSize&(nPageSize-1)) || nPageSize>32768 || nPageSize<512 ){
++ pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE);
++ return rc;
++ }
++
++ /* Space reserved a the end of the page for extensions. Usually 0. */
++ nReservedSize = header[kiHeaderReservedSizeOffset];
++
++ /* 1 for UTF-8, 2 for UTF-16le, 3 for UTF-16be. */
++ iEncoding = decodeUnsigned32(header + kiHeaderEncodingOffset);
++ if( iEncoding==3 ){
++ *piEncoding = SQLITE_UTF16BE;
++ } else if( iEncoding==2 ){
++ *piEncoding = SQLITE_UTF16LE;
++ } else if( iEncoding==1 ){
++ *piEncoding = SQLITE_UTF8;
++ } else {
++ /* This case should not be possible. */
++ *piEncoding = SQLITE_UTF8;
++ }
++
++ rc = pagerCreate(pFile, nPageSize, &pPager);
++ if( rc!=SQLITE_OK ){
++ pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE);
++ return rc;
+ }
+
-+ *pPager = sqlite3BtreePager(pBt);
-+ *pnPageSize =
-+ sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetOptimalReserve(pBt);
++ *ppPager = pPager;
++ *pnPageSize = nPageSize - nReservedSize;
++ *piEncoding = iEncoding;
+ return SQLITE_OK;
+}
+
@@ -548,7 +703,7 @@ index 0000000..8e3929d
+ case 7 : return 8; /* 64-bit float. */
+ case 8 : return 0; /* Constant 0. */
+ case 9 : return 0; /* Constant 1. */
-+ case 10 : case 11 : assert( !"RESERVED TYPE"); return 0;
++ case 10 : case 11 : assert( "RESERVED TYPE"==NULL ); return 0;
+ }
+ return (u32)((iSerialType>>1) - 6);
+}
@@ -574,8 +729,8 @@ index 0000000..8e3929d
+ case 7 : return (mask&MASK_FLOAT)!=0;
+ case 8 : return (mask&MASK_INTEGER)!=0;
+ case 9 : return (mask&MASK_INTEGER)!=0;
-+ case 10 : assert( !"RESERVED TYPE"); return 0;
-+ case 11 : assert( !"RESERVED TYPE"); return 0;
++ case 10 : assert( "RESERVED TYPE"==NULL ); return 0;
++ case 11 : assert( "RESERVED TYPE"==NULL ); return 0;
+ }
+ return (mask&(SerialTypeIsBlob(iSerialType) ? MASK_BLOB : MASK_TEXT));
+}
@@ -651,57 +806,6 @@ index 0000000..8e3929d
+ return rc;
+}
+
-+static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){
-+ sqlite3_stmt *pStmt;
-+ int rc;
-+ char *zSql = sqlite3_mprintf("PRAGMA %s.encoding", zDb);
-+ if( !zSql ){
-+ return SQLITE_NOMEM;
-+ }
-+
-+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
-+ sqlite3_free(zSql);
-+ if( rc!=SQLITE_OK ){
-+ return rc;
-+ }
-+
-+ /* Require a result. */
-+ rc = sqlite3_step(pStmt);
-+ if( rc==SQLITE_DONE ){
-+ /* This case should not be possible. */
-+ rc = SQLITE_CORRUPT;
-+ }else if( rc==SQLITE_ROW ){
-+ if( sqlite3_column_type(pStmt, 0)==SQLITE_TEXT ){
-+ const char* z = (const char *)sqlite3_column_text(pStmt, 0);
-+ /* These strings match the literals in pragma.c. */
-+ if( !strcmp(z, "UTF-16le") ){
-+ *piEncoding = SQLITE_UTF16LE;
-+ }else if( !strcmp(z, "UTF-16be") ){
-+ *piEncoding = SQLITE_UTF16BE;
-+ }else if( !strcmp(z, "UTF-8") ){
-+ *piEncoding = SQLITE_UTF8;
-+ }else{
-+ /* This case should not be possible. */
-+ *piEncoding = SQLITE_UTF8;
-+ }
-+ }else{
-+ /* This case should not be possible. */
-+ *piEncoding = SQLITE_UTF8;
-+ }
-+
-+ /* Require only one result. */
-+ rc = sqlite3_step(pStmt);
-+ if( rc==SQLITE_DONE ){
-+ rc = SQLITE_OK;
-+ }else if( rc==SQLITE_ROW ){
-+ /* This case should not be possible. */
-+ rc = SQLITE_CORRUPT;
-+ }
-+ }
-+ sqlite3_finalize(pStmt);
-+ return rc;
-+}
-+
+/* Cursor for iterating interior nodes. Interior page cells contain a
+ * child page number and a rowid. The child page contains items left
+ * of the rowid (less than). The rightmost page of the subtree is
@@ -737,7 +841,7 @@ index 0000000..8e3929d
+typedef struct RecoverInteriorCursor RecoverInteriorCursor;
+struct RecoverInteriorCursor {
+ RecoverInteriorCursor *pParent; /* Parent node to this node. */
-+ DbPage *pPage; /* Reference to leaf page. */
++ RecoverPage *pPage; /* Reference to leaf page. */
+ unsigned nPageSize; /* Size of page. */
+ unsigned nChildren; /* Number of children on the page. */
+ unsigned iChild; /* Index of next child to return. */
@@ -750,7 +854,7 @@ index 0000000..8e3929d
+ pCursor = pCursor->pParent;
+
+ if( p->pPage ){
-+ sqlite3PagerUnref(p->pPage);
++ pageDestroy(p->pPage);
+ p->pPage = NULL;
+ }
+
@@ -761,13 +865,13 @@ index 0000000..8e3929d
+
+/* Internal helper. Reset storage in preparation for iterating pPage. */
+static void interiorCursorSetPage(RecoverInteriorCursor *pCursor,
-+ DbPage *pPage){
++ RecoverPage *pPage){
+ const unsigned knMinCellLength = 2 + 4 + 1;
+ unsigned nMaxChildren;
+ assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage );
+
+ if( pCursor->pPage ){
-+ sqlite3PagerUnref(pCursor->pPage);
++ pageDestroy(pCursor->pPage);
+ pCursor->pPage = NULL;
+ }
+ pCursor->pPage = pPage;
@@ -798,7 +902,7 @@ index 0000000..8e3929d
+}
+
+static int interiorCursorCreate(RecoverInteriorCursor *pParent,
-+ DbPage *pPage, int nPageSize,
++ RecoverPage *pPage, int nPageSize,
+ RecoverInteriorCursor **ppCursor){
+ RecoverInteriorCursor *pCursor =
+ sqlite3_malloc(sizeof(RecoverInteriorCursor));
@@ -883,7 +987,7 @@ index 0000000..8e3929d
+ * reverse the list during traversal.
+ */
+static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor,
-+ DbPage **ppPage){
++ RecoverPage **ppPage){
+ RecoverInteriorCursor *pCursor = *ppCursor;
+ while( 1 ){
+ int rc;
@@ -896,7 +1000,7 @@ index 0000000..8e3929d
+ if( interiorCursorPageInUse(pCursor, iPage) ){
+ fprintf(stderr, "Loop detected at %d\n", iPage);
+ }else{
-+ int rc = sqlite3PagerGet(pCursor->pPage->pPager, iPage, ppPage, 0);
++ int rc = pagerGetPage(pCursor->pPage->pPager, iPage, ppPage);
+ if( rc==SQLITE_OK ){
+ return SQLITE_ROW;
+ }
@@ -950,7 +1054,7 @@ index 0000000..8e3929d
+typedef struct RecoverOverflow RecoverOverflow;
+struct RecoverOverflow {
+ RecoverOverflow *pNextOverflow;
-+ DbPage *pPage;
++ RecoverPage *pPage;
+ unsigned nPageSize;
+};
+
@@ -960,7 +1064,7 @@ index 0000000..8e3929d
+ pOverflow = p->pNextOverflow;
+
+ if( p->pPage ){
-+ sqlite3PagerUnref(p->pPage);
++ pageDestroy(p->pPage);
+ p->pPage = NULL;
+ }
+
@@ -987,7 +1091,7 @@ index 0000000..8e3929d
+ * overflowGetSegment() will do the right thing regardless of whether
+ * those values are set to be in-page or not.
+ */
-+static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize,
++static int overflowMaybeCreate(RecoverPage *pPage, unsigned nPageSize,
+ unsigned iRecordOffset, unsigned nRecordBytes,
+ unsigned *pnLocalRecordBytes,
+ RecoverOverflow **ppOverflow){
@@ -1040,14 +1144,14 @@ index 0000000..8e3929d
+ while( iNextPage && nBytes<nRecordBytes ){
+ RecoverOverflow *pOverflow; /* New overflow page for the list. */
+
-+ rc = sqlite3PagerGet(pPage->pPager, iNextPage, &pPage, 0);
++ rc = pagerGetPage(pPage->pPager, iNextPage, &pPage);
+ if( rc!=SQLITE_OK ){
+ break;
+ }
+
+ pOverflow = sqlite3_malloc(sizeof(RecoverOverflow));
+ if( !pOverflow ){
-+ sqlite3PagerUnref(pPage);
++ pageDestroy(pPage);
+ rc = SQLITE_NOMEM;
+ break;
+ }
@@ -1111,7 +1215,7 @@ index 0000000..8e3929d
+ * and overflow pages consistently by adjusting the values
+ * appropriately.
+ */
-+static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset,
++static int overflowGetSegment(RecoverPage *pPage, unsigned iRecordOffset,
+ unsigned nLocalRecordBytes,
+ RecoverOverflow *pOverflow,
+ unsigned iRequestOffset, unsigned nRequestBytes,
@@ -1218,7 +1322,8 @@ index 0000000..8e3929d
+typedef struct RecoverLeafCursor RecoverLeafCursor;
+struct RecoverLeafCursor {
+ RecoverInteriorCursor *pParent; /* Parent node to this node. */
-+ DbPage *pPage; /* Reference to leaf page. */
++ RecoverPager *pPager; /* Page provider. */
++ RecoverPage *pPage; /* Current leaf page. */
+ unsigned nPageSize; /* Size of pPage. */
+ unsigned nCells; /* Number of cells in pPage. */
+ unsigned iCell; /* Current cell. */
@@ -1253,12 +1358,13 @@ index 0000000..8e3929d
+ * If SQLITE_OK is returned, the caller no longer owns pPage,
+ * otherwise the caller is responsible for discarding it.
+ */
-+static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){
++static int leafCursorLoadPage(RecoverLeafCursor *pCursor, RecoverPage *pPage){
+ const unsigned char *pPageHeader; /* Header of *pPage */
++ unsigned nCells; /* Number of cells in the page */
+
+ /* Release the current page. */
+ if( pCursor->pPage ){
-+ sqlite3PagerUnref(pCursor->pPage);
++ pageDestroy(pCursor->pPage);
+ pCursor->pPage = NULL;
+ pCursor->iCell = pCursor->nCells = 0;
+ }
@@ -1280,14 +1386,21 @@ index 0000000..8e3929d
+
+ /* Not a leaf page, skip it. */
+ if( pPageHeader[kiPageTypeOffset]!=kTableLeafPage ){
-+ sqlite3PagerUnref(pPage);
++ pageDestroy(pPage);
++ return SQLITE_OK;
++ }
++
++ /* Leaf contains no data, skip it. Empty tables, for instance. */
++ nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset);;
++ if( nCells<1 ){
++ pageDestroy(pPage);
+ return SQLITE_OK;
+ }
+
+ /* Take ownership of the page and start decoding. */
+ pCursor->pPage = pPage;
+ pCursor->iCell = 0;
-+ pCursor->nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset);
++ pCursor->nCells = nCells;
+ return SQLITE_OK;
+}
+
@@ -1302,7 +1415,7 @@ index 0000000..8e3929d
+
+ /* Repeatedly load the parent's next child page until a leaf is found. */
+ do {
-+ DbPage *pNextPage;
++ RecoverPage *pNextPage;
+ int rc = interiorCursorNextPage(&pCursor->pParent, &pNextPage);
+ if( rc!=SQLITE_ROW ){
+ assert( rc==SQLITE_DONE );
@@ -1311,7 +1424,7 @@ index 0000000..8e3929d
+
+ rc = leafCursorLoadPage(pCursor, pNextPage);
+ if( rc!=SQLITE_OK ){
-+ sqlite3PagerUnref(pNextPage);
++ pageDestroy(pNextPage);
+ return rc;
+ }
+ } while( !pCursor->pPage );
@@ -1341,10 +1454,15 @@ index 0000000..8e3929d
+ }
+
+ if( pCursor->pPage ){
-+ sqlite3PagerUnref(pCursor->pPage);
++ pageDestroy(pCursor->pPage);
+ pCursor->pPage = NULL;
+ }
+
++ if( pCursor->pPager ){
++ pagerDestroy(pCursor->pPager);
++ pCursor->pPager = NULL;
++ }
++
+ memset(pCursor, 0xA5, sizeof(*pCursor));
+ sqlite3_free(pCursor);
+}
@@ -1362,30 +1480,31 @@ index 0000000..8e3929d
+ * - pPage is a valid interior page who's leaves contain no valid cells.
+ * - pPage is not a valid leaf or interior page.
+ */
-+static int leafCursorCreate(Pager *pPager, unsigned nPageSize,
++static int leafCursorCreate(RecoverPager *pPager, unsigned nPageSize,
+ u32 iRootPage, RecoverLeafCursor **ppCursor){
-+ DbPage *pPage; /* Reference to page at iRootPage. */
++ RecoverPage *pPage; /* Reference to page at iRootPage. */
+ RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */
+ int rc;
+
+ /* Start out with the root page. */
-+ rc = sqlite3PagerGet(pPager, iRootPage, &pPage, 0);
++ rc = pagerGetPage(pPager, iRootPage, &pPage);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor));
+ if( !pCursor ){
-+ sqlite3PagerUnref(pPage);
++ pageDestroy(pPage);
+ return SQLITE_NOMEM;
+ }
+ memset(pCursor, 0, sizeof(*pCursor));
+
+ pCursor->nPageSize = nPageSize;
++ pCursor->pPager = pPager;
+
+ rc = leafCursorLoadPage(pCursor, pPage);
+ if( rc!=SQLITE_OK ){
-+ sqlite3PagerUnref(pPage);
++ pageDestroy(pPage);
+ leafCursorDestroy(pCursor);
+ return rc;
+ }
@@ -1728,7 +1847,7 @@ index 0000000..8e3929d
+ u32 iRootPage; /* Root page of the backing table. */
+ int iEncoding; /* UTF encoding for backing database. */
+ unsigned nPageSize; /* Size of pages in backing database. */
-+ Pager *pPager; /* Backing database pager. */
++ RecoverPager *pPager; /* Backing database pager. */
+ RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */
+ RecoverCursor *pCursor; /* Cursor to read rows from leaves. */
+ int rc;
@@ -1742,19 +1861,14 @@ index 0000000..8e3929d
+ return rc;
+ }
+
-+ iEncoding = 0;
-+ rc = getEncoding(pRecover->db, pRecover->zDb, &iEncoding);
-+ if( rc!=SQLITE_OK ){
-+ return rc;
-+ }
-+
-+ rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize);
++ rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize, &iEncoding);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor);
+ if( rc!=SQLITE_OK ){
++ pagerDestroy(pPager);
+ return rc;
+ }
+
@@ -2311,10 +2425,10 @@ index e5673fd..6829bcb 100644
** DO NOT EXTEND THE USE OF THIS.
diff --git a/third_party/sqlite/src/test/recover.test b/third_party/sqlite/src/test/recover.test
new file mode 100644
-index 0000000..b5aa182
+index 0000000..bfb7888
--- /dev/null
+++ b/third_party/sqlite/src/test/recover.test
-@@ -0,0 +1,147 @@
+@@ -0,0 +1,164 @@
+# 2012 January 11 {}
+#
+# The author disclaims copyright to this source code. In place of
@@ -2427,6 +2541,23 @@ index 0000000..b5aa182
+ execsql {SELECT t, n FROM leaf_recover ORDER BY rowid}
+} {{Leaf-node-generating line 0} 0 {Leaf-node-generating line 1} 1 {Leaf-node-generating line 2} 2 {Leaf-node-generating line 3} 3 {Leaf-node-generating line 4} 4 {Leaf-node-generating line 5} 5 {Leaf-node-generating line 6} 6 {Leaf-node-generating line 7} 7 {Leaf-node-generating line 8} 8 {Leaf-node-generating line 9} 9}
+
++# Empty table gives empty results.
++do_test recover-leaf-2.0 {
++ db close
++ sqlite3 db test.db
++ generate "empty" "Leaf-node-generating line " 0
++
++ db eval {
++ DROP TABLE IF EXISTS temp.leaf_recover;
++ CREATE VIRTUAL TABLE temp.leaf_recover USING recover(
++ empty,
++ t TEXT,
++ n INTEGER
++ );
++ }
++ execsql {SELECT t, n FROM leaf_recover ORDER BY rowid}
++} {}
++
+# Single level of interior node.
+do_test recover-interior-1.0 {
+ db close
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698