| OLD | NEW |
| 1 From 29b8ad9263dbc700c002e9044c7c712a566a26d7 Mon Sep 17 00:00:00 2001 | 1 From 31dd2fd1cdeba87223f6a46cc19bcea9eb8b2f38 Mon Sep 17 00:00:00 2001 |
| 2 From: Scott Hess <shess@chromium.org> | 2 From: Scott Hess <shess@chromium.org> |
| 3 Date: Sat, 20 Jul 2013 11:42:21 -0700 | 3 Date: Sat, 20 Jul 2013 11:42:21 -0700 |
| 4 Subject: [PATCH 05/10] Virtual table supporting recovery of corrupted | 4 Subject: [PATCH 05/13] Virtual table supporting recovery of corrupted |
| 5 databases. | 5 databases. |
| 6 | 6 |
| 7 "recover" implements a virtual table which uses the SQLite pager layer | 7 "recover" implements a virtual table which uses the SQLite pager layer |
| 8 to read table pages and pull out the data which is structurally sound | 8 to read table pages and pull out the data which is structurally sound |
| 9 (at least at the storage layer). | 9 (at least at the storage layer). |
| 10 | 10 |
| 11 BUG=109482 | 11 BUG=109482 |
| 12 | 12 |
| 13 Since this implements a new feature for SQLite, the review URLs aren't | 13 Since this implements a new feature for SQLite, the review URLs aren't |
| 14 listed. This patch and the top of recover.c should be considered | 14 listed. This patch and the top of recover.c should be considered |
| 15 authoritative. The history is mostly under | 15 authoritative. The history is mostly under |
| 16 third_party/sqlite/src/src/{recover,recover-alt}.c . | 16 third_party/sqlite/src/src/{recover,recover-alt}.c . |
| 17 --- | 17 --- |
| 18 third_party/sqlite/src/Makefile.in | 1 + | 18 third_party/sqlite/src/Makefile.in | 1 + |
| 19 third_party/sqlite/src/Makefile.linux-gcc | 4 + | 19 third_party/sqlite/src/Makefile.linux-gcc | 4 + |
| 20 third_party/sqlite/src/main.mk | 5 +- | 20 third_party/sqlite/src/main.mk | 5 +- |
| 21 third_party/sqlite/src/src/main.c | 8 + | 21 third_party/sqlite/src/src/main.c | 8 + |
| 22 third_party/sqlite/src/src/recover.c | 2167 ++++++++++++++++++++++++++++ | 22 third_party/sqlite/src/src/recover.c | 2281 ++++++++++++++++++++++++++++ |
| 23 third_party/sqlite/src/src/sqlite.h.in | 16 + | 23 third_party/sqlite/src/src/sqlite.h.in | 16 + |
| 24 third_party/sqlite/src/test/recover.test | 147 ++ | 24 third_party/sqlite/src/test/recover.test | 164 ++ |
| 25 third_party/sqlite/src/test/recover0.test | 532 +++++++ | 25 third_party/sqlite/src/test/recover0.test | 532 +++++++ |
| 26 third_party/sqlite/src/test/recover1.test | 429 ++++++ | 26 third_party/sqlite/src/test/recover1.test | 429 ++++++ |
| 27 third_party/sqlite/src/test/recover2.test | 157 ++ | 27 third_party/sqlite/src/test/recover2.test | 157 ++ |
| 28 third_party/sqlite/src/tool/mksqlite3c.tcl | 2 + | 28 third_party/sqlite/src/tool/mksqlite3c.tcl | 2 + |
| 29 11 files changed, 3467 insertions(+), 1 deletion(-) | 29 11 files changed, 3598 insertions(+), 1 deletion(-) |
| 30 create mode 100644 third_party/sqlite/src/src/recover.c | 30 create mode 100644 third_party/sqlite/src/src/recover.c |
| 31 create mode 100644 third_party/sqlite/src/test/recover.test | 31 create mode 100644 third_party/sqlite/src/test/recover.test |
| 32 create mode 100644 third_party/sqlite/src/test/recover0.test | 32 create mode 100644 third_party/sqlite/src/test/recover0.test |
| 33 create mode 100644 third_party/sqlite/src/test/recover1.test | 33 create mode 100644 third_party/sqlite/src/test/recover1.test |
| 34 create mode 100644 third_party/sqlite/src/test/recover2.test | 34 create mode 100644 third_party/sqlite/src/test/recover2.test |
| 35 | 35 |
| 36 diff --git a/third_party/sqlite/src/Makefile.in b/third_party/sqlite/src/Makefil
e.in | 36 diff --git a/third_party/sqlite/src/Makefile.in b/third_party/sqlite/src/Makefil
e.in |
| 37 index 1fe49d6..8b965c7 100644 | 37 index 1fe49d6..8b965c7 100644 |
| 38 --- a/third_party/sqlite/src/Makefile.in | 38 --- a/third_party/sqlite/src/Makefile.in |
| 39 +++ b/third_party/sqlite/src/Makefile.in | 39 +++ b/third_party/sqlite/src/Makefile.in |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 + if( !db->mallocFailed && rc==SQLITE_OK ){ | 104 + if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 105 + rc = recoverVtableInit(db); | 105 + rc = recoverVtableInit(db); |
| 106 + } | 106 + } |
| 107 +#endif | 107 +#endif |
| 108 + | 108 + |
| 109 #ifdef SQLITE_ENABLE_ICU | 109 #ifdef SQLITE_ENABLE_ICU |
| 110 if( !db->mallocFailed && rc==SQLITE_OK ){ | 110 if( !db->mallocFailed && rc==SQLITE_OK ){ |
| 111 rc = sqlite3IcuInit(db); | 111 rc = sqlite3IcuInit(db); |
| 112 diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/r
ecover.c | 112 diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/r
ecover.c |
| 113 new file mode 100644 | 113 new file mode 100644 |
| 114 index 0000000..8e3929d | 114 index 0000000..5ff6f78 |
| 115 --- /dev/null | 115 --- /dev/null |
| 116 +++ b/third_party/sqlite/src/src/recover.c | 116 +++ b/third_party/sqlite/src/src/recover.c |
| 117 @@ -0,0 +1,2167 @@ | 117 @@ -0,0 +1,2281 @@ |
| 118 +/* | 118 +/* |
| 119 +** 2012 Jan 11 | 119 +** 2012 Jan 11 |
| 120 +** | 120 +** |
| 121 +** The author disclaims copyright to this source code. In place of | 121 +** The author disclaims copyright to this source code. In place of |
| 122 +** a legal notice, here is a blessing: | 122 +** a legal notice, here is a blessing: |
| 123 +** | 123 +** |
| 124 +** May you do good and not evil. | 124 +** May you do good and not evil. |
| 125 +** May you find forgiveness for yourself and forgive others. | 125 +** May you find forgiveness for yourself and forgive others. |
| 126 +** May you share freely, never taking more than you give. | 126 +** May you share freely, never taking more than you give. |
| 127 +*/ | 127 +*/ |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 347 +#define FNENTRY() fprintf(stderr, "In %s\n", __FUNCTION__) | 347 +#define FNENTRY() fprintf(stderr, "In %s\n", __FUNCTION__) |
| 348 +#else | 348 +#else |
| 349 +#define FNENTRY() | 349 +#define FNENTRY() |
| 350 +#endif | 350 +#endif |
| 351 + | 351 + |
| 352 +/* Generic constants and helper functions. */ | 352 +/* Generic constants and helper functions. */ |
| 353 + | 353 + |
| 354 +static const unsigned char kTableLeafPage = 0x0D; | 354 +static const unsigned char kTableLeafPage = 0x0D; |
| 355 +static const unsigned char kTableInteriorPage = 0x05; | 355 +static const unsigned char kTableInteriorPage = 0x05; |
| 356 + | 356 + |
| 357 +/* From section 1.2. */ |
| 358 +static const unsigned kiHeaderPageSizeOffset = 16; |
| 359 +static const unsigned kiHeaderReservedSizeOffset = 20; |
| 360 +static const unsigned kiHeaderEncodingOffset = 56; |
| 361 +/* TODO(shess) |static const unsigned| fails creating the header in GetPager() |
| 362 +** because |knHeaderSize| isn't |constexpr|. But this isn't C++, either. |
| 363 +*/ |
| 364 +enum { knHeaderSize = 100}; |
| 365 + |
| 357 +/* From section 1.5. */ | 366 +/* From section 1.5. */ |
| 358 +static const unsigned kiPageTypeOffset = 0; | 367 +static const unsigned kiPageTypeOffset = 0; |
| 359 +static const unsigned kiPageFreeBlockOffset = 1; | 368 +static const unsigned kiPageFreeBlockOffset = 1; |
| 360 +static const unsigned kiPageCellCountOffset = 3; | 369 +static const unsigned kiPageCellCountOffset = 3; |
| 361 +static const unsigned kiPageCellContentOffset = 5; | 370 +static const unsigned kiPageCellContentOffset = 5; |
| 362 +static const unsigned kiPageFragmentedBytesOffset = 7; | 371 +static const unsigned kiPageFragmentedBytesOffset = 7; |
| 363 +static const unsigned knPageLeafHeaderBytes = 8; | 372 +static const unsigned knPageLeafHeaderBytes = 8; |
| 364 +/* Interior pages contain an additional field. */ | 373 +/* Interior pages contain an additional field. */ |
| 365 +static const unsigned kiPageRightChildOffset = 8; | 374 +static const unsigned kiPageRightChildOffset = 8; |
| 366 +static const unsigned kiPageInteriorHeaderBytes = 12; | 375 +static const unsigned kiPageInteriorHeaderBytes = 12; |
| (...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 + } | 489 + } |
| 481 + return n ? ascii_tolower(*us1)-ascii_tolower(*us2) : 0; | 490 + return n ? ascii_tolower(*us1)-ascii_tolower(*us2) : 0; |
| 482 +} | 491 +} |
| 483 +static int ascii_strcasecmp(const char *s1, const char *s2){ | 492 +static int ascii_strcasecmp(const char *s1, const char *s2){ |
| 484 + /* If s2 is equal through strlen(s1), will exit while() due to s1's | 493 + /* If s2 is equal through strlen(s1), will exit while() due to s1's |
| 485 + * trailing NUL, and return NUL-s2[strlen(s1)]. | 494 + * trailing NUL, and return NUL-s2[strlen(s1)]. |
| 486 + */ | 495 + */ |
| 487 + return ascii_strncasecmp(s1, s2, strlen(s1)+1); | 496 + return ascii_strncasecmp(s1, s2, strlen(s1)+1); |
| 488 +} | 497 +} |
| 489 + | 498 + |
| 499 +/* Provide access to the pages of a SQLite database in a way similar to SQLite'
s |
| 500 +** Pager. |
| 501 +*/ |
| 502 +typedef struct RecoverPager RecoverPager; |
| 503 +struct RecoverPager { |
| 504 + sqlite3_file *pSqliteFile; /* Reference to database's file handle */ |
| 505 + u32 nPageSize; /* Size of pages in pSqliteFile */ |
| 506 +}; |
| 507 + |
| 508 +static void pagerDestroy(RecoverPager *pPager){ |
| 509 + pPager->pSqliteFile->pMethods->xUnlock(pPager->pSqliteFile, SQLITE_LOCK_NONE)
; |
| 510 + memset(pPager, 0xA5, sizeof(*pPager)); |
| 511 + sqlite3_free(pPager); |
| 512 +} |
| 513 + |
| 514 +/* pSqliteFile should already have a SHARED lock. */ |
| 515 +static int pagerCreate(sqlite3_file *pSqliteFile, u32 nPageSize, |
| 516 + RecoverPager **ppPager){ |
| 517 + RecoverPager *pPager = sqlite3_malloc(sizeof(RecoverPager)); |
| 518 + if( !pPager ){ |
| 519 + return SQLITE_NOMEM; |
| 520 + } |
| 521 + |
| 522 + memset(pPager, 0, sizeof(*pPager)); |
| 523 + pPager->pSqliteFile = pSqliteFile; |
| 524 + pPager->nPageSize = nPageSize; |
| 525 + *ppPager = pPager; |
| 526 + return SQLITE_OK; |
| 527 +} |
| 528 + |
| 529 +/* Matches DbPage (aka PgHdr) from SQLite internals. */ |
| 530 +/* TODO(shess): SQLite by default allocates page metadata in a single allocatio
n |
| 531 +** such that the page's data and metadata are contiguous, see pcache1AllocPage |
| 532 +** in pcache1.c. I believe this was intended to reduce malloc churn. It means |
| 533 +** that Chromium's automated tooling would be unlikely to see page-buffer |
| 534 +** overruns. I believe that this code is safe, but for now replicate SQLite's |
| 535 +** approach with kExcessSpace. |
| 536 +*/ |
| 537 +const int kExcessSpace = 128; |
| 538 +typedef struct RecoverPage RecoverPage; |
| 539 +struct RecoverPage { |
| 540 + Pgno pgno; /* Page number for this page */ |
| 541 + void *pData; /* Page data for pgno */ |
| 542 + RecoverPager *pPager; /* The pager this page is part of */ |
| 543 +}; |
| 544 + |
| 545 +static void pageDestroy(RecoverPage *pPage){ |
| 546 + sqlite3_free(pPage->pData); |
| 547 + memset(pPage, 0xA5, sizeof(*pPage)); |
| 548 + sqlite3_free(pPage); |
| 549 +} |
| 550 + |
| 551 +static int pageCreate(RecoverPager *pPager, u32 pgno, RecoverPage **ppPage){ |
| 552 + RecoverPage *pPage = sqlite3_malloc(sizeof(RecoverPage)); |
| 553 + if( !pPage ){ |
| 554 + return SQLITE_NOMEM; |
| 555 + } |
| 556 + |
| 557 + memset(pPage, 0, sizeof(*pPage)); |
| 558 + pPage->pPager = pPager; |
| 559 + pPage->pgno = pgno; |
| 560 + pPage->pData = sqlite3_malloc(pPager->nPageSize + kExcessSpace); |
| 561 + if( pPage->pData==NULL ){ |
| 562 + pageDestroy(pPage); |
| 563 + return SQLITE_NOMEM; |
| 564 + } |
| 565 + memset((u8 *)pPage->pData + pPager->nPageSize, 0, kExcessSpace); |
| 566 + |
| 567 + *ppPage = pPage; |
| 568 + return SQLITE_OK; |
| 569 +} |
| 570 + |
| 571 +static int pagerGetPage(RecoverPager *pPager, u32 iPage, RecoverPage **ppPage)
{ |
| 572 + sqlite3_int64 iOfst; |
| 573 + sqlite3_file *pFile = pPager->pSqliteFile; |
| 574 + RecoverPage *pPage; |
| 575 + int rc = pageCreate(pPager, iPage, &pPage); |
| 576 + if( rc!=SQLITE_OK ){ |
| 577 + return rc; |
| 578 + } |
| 579 + |
| 580 + /* xRead() can return SQLITE_IOERR_SHORT_READ, which should be treated as |
| 581 + ** SQLITE_OK plus an EOF indicator. The excess space is zero-filled. |
| 582 + */ |
| 583 + iOfst = ((sqlite3_int64)iPage - 1) * pPager->nPageSize; |
| 584 + rc = pFile->pMethods->xRead(pFile, pPage->pData, pPager->nPageSize, iOfst); |
| 585 + if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){ |
| 586 + pageDestroy(pPage); |
| 587 + return rc; |
| 588 + } |
| 589 + |
| 590 + *ppPage = pPage; |
| 591 + return SQLITE_OK; |
| 592 +} |
| 593 + |
| 490 +/* For some reason I kept making mistakes with offset calculations. */ | 594 +/* For some reason I kept making mistakes with offset calculations. */ |
| 491 +static const unsigned char *PageData(DbPage *pPage, unsigned iOffset){ | 595 +static const unsigned char *PageData(RecoverPage *pPage, unsigned iOffset){ |
| 492 + assert( iOffset<=pPage->nPageSize ); | 596 + assert( iOffset<=pPage->pPager->nPageSize ); |
| 493 + return (unsigned char *)pPage->pData + iOffset; | 597 + return (unsigned char *)pPage->pData + iOffset; |
| 494 +} | 598 +} |
| 495 + | 599 + |
| 496 +/* The first page in the file contains a file header in the first 100 | 600 +/* The first page in the file contains a file header in the first 100 |
| 497 + * bytes. The page's header information comes after that. Note that | 601 + * bytes. The page's header information comes after that. Note that |
| 498 + * the offsets in the page's header information are relative to the | 602 + * the offsets in the page's header information are relative to the |
| 499 + * beginning of the page, NOT the end of the page header. | 603 + * beginning of the page, NOT the end of the page header. |
| 500 + */ | 604 + */ |
| 501 +static const unsigned char *PageHeader(DbPage *pPage){ | 605 +static const unsigned char *PageHeader(RecoverPage *pPage){ |
| 502 + if( pPage->pgno==1 ){ | 606 + if( pPage->pgno==1 ){ |
| 503 + const unsigned nDatabaseHeader = 100; | 607 + return PageData(pPage, knHeaderSize); |
| 504 + return PageData(pPage, nDatabaseHeader); | |
| 505 + }else{ | 608 + }else{ |
| 506 + return PageData(pPage, 0); | 609 + return PageData(pPage, 0); |
| 507 + } | 610 + } |
| 508 +} | 611 +} |
| 509 + | 612 + |
| 510 +/* Helper to fetch the pager and page size for the named database. */ | 613 +/* Helper to fetch the pager and page size for the named database. */ |
| 511 +static int GetPager(sqlite3 *db, const char *zName, | 614 +static int GetPager(sqlite3 *db, const char *zName, |
| 512 + Pager **pPager, unsigned *pnPageSize){ | 615 + RecoverPager **ppPager, unsigned *pnPageSize, |
| 513 + Btree *pBt = NULL; | 616 + int *piEncoding){ |
| 514 + int i; | 617 + int rc, iEncoding; |
| 515 + for( i=0; i<db->nDb; ++i ){ | 618 + unsigned nPageSize, nReservedSize; |
| 516 + if( ascii_strcasecmp(db->aDb[i].zName, zName)==0 ){ | 619 + unsigned char header[knHeaderSize]; |
| 517 + pBt = db->aDb[i].pBt; | 620 + sqlite3_file *pFile = NULL; |
| 518 + break; | 621 + RecoverPager *pPager; |
| 519 + } | 622 + |
| 520 + } | 623 + rc = sqlite3_file_control(db, zName, SQLITE_FCNTL_FILE_POINTER, &pFile); |
| 521 + if( !pBt ){ | 624 + if( rc!=SQLITE_OK ) { |
| 522 + return SQLITE_ERROR; | 625 + return rc; |
| 626 + } else if( pFile==NULL ){ |
| 627 + /* The documentation for sqlite3PagerFile() indicates it can return NULL if |
| 628 + ** the file has not yet been opened. That should not be possible here... |
| 629 + */ |
| 630 + return SQLITE_MISUSE; |
| 523 + } | 631 + } |
| 524 + | 632 + |
| 525 + *pPager = sqlite3BtreePager(pBt); | 633 + /* Get a shared lock to make sure the on-disk version of the file is truth. *
/ |
| 526 + *pnPageSize = | 634 + rc = pFile->pMethods->xLock(pFile, SQLITE_LOCK_SHARED); |
| 527 + sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetOptimalReserve(pBt); | 635 + if( rc != SQLITE_OK ){ |
| 636 + return rc; |
| 637 + } |
| 638 + |
| 639 + /* Read the Initial header information. In case of SQLITE_IOERR_SHORT_READ, |
| 640 + ** the header is incomplete, which means no data could be recovered anyhow. |
| 641 + */ |
| 642 + rc = pFile->pMethods->xRead(pFile, header, sizeof(header), 0); |
| 643 + if( rc != SQLITE_OK ){ |
| 644 + pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE); |
| 645 + if( rc==SQLITE_IOERR_SHORT_READ ){ |
| 646 + return SQLITE_CORRUPT; |
| 647 + } |
| 648 + return rc; |
| 649 + } |
| 650 + |
| 651 + /* Page size must be a power of two between 512 and 32768 inclusive. */ |
| 652 + nPageSize = decodeUnsigned16(header + kiHeaderPageSizeOffset); |
| 653 + if( (nPageSize&(nPageSize-1)) || nPageSize>32768 || nPageSize<512 ){ |
| 654 + pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE); |
| 655 + return rc; |
| 656 + } |
| 657 + |
| 658 + /* Space reserved a the end of the page for extensions. Usually 0. */ |
| 659 + nReservedSize = header[kiHeaderReservedSizeOffset]; |
| 660 + |
| 661 + /* 1 for UTF-8, 2 for UTF-16le, 3 for UTF-16be. */ |
| 662 + iEncoding = decodeUnsigned32(header + kiHeaderEncodingOffset); |
| 663 + if( iEncoding==3 ){ |
| 664 + *piEncoding = SQLITE_UTF16BE; |
| 665 + } else if( iEncoding==2 ){ |
| 666 + *piEncoding = SQLITE_UTF16LE; |
| 667 + } else if( iEncoding==1 ){ |
| 668 + *piEncoding = SQLITE_UTF8; |
| 669 + } else { |
| 670 + /* This case should not be possible. */ |
| 671 + *piEncoding = SQLITE_UTF8; |
| 672 + } |
| 673 + |
| 674 + rc = pagerCreate(pFile, nPageSize, &pPager); |
| 675 + if( rc!=SQLITE_OK ){ |
| 676 + pFile->pMethods->xUnlock(pFile, SQLITE_LOCK_NONE); |
| 677 + return rc; |
| 678 + } |
| 679 + |
| 680 + *ppPager = pPager; |
| 681 + *pnPageSize = nPageSize - nReservedSize; |
| 682 + *piEncoding = iEncoding; |
| 528 + return SQLITE_OK; | 683 + return SQLITE_OK; |
| 529 +} | 684 +} |
| 530 + | 685 + |
| 531 +/* iSerialType is a type read from a record header. See "2.1 Record Format". | 686 +/* iSerialType is a type read from a record header. See "2.1 Record Format". |
| 532 + */ | 687 + */ |
| 533 + | 688 + |
| 534 +/* Storage size of iSerialType in bytes. My interpretation of SQLite | 689 +/* Storage size of iSerialType in bytes. My interpretation of SQLite |
| 535 + * documentation is that text and blob fields can have 32-bit length. | 690 + * documentation is that text and blob fields can have 32-bit length. |
| 536 + * Values past 2^31-12 will need more than 32 bits to encode, which is | 691 + * Values past 2^31-12 will need more than 32 bits to encode, which is |
| 537 + * why iSerialType is u64. | 692 + * why iSerialType is u64. |
| 538 + */ | 693 + */ |
| 539 +static u32 SerialTypeLength(u64 iSerialType){ | 694 +static u32 SerialTypeLength(u64 iSerialType){ |
| 540 + switch( iSerialType ){ | 695 + switch( iSerialType ){ |
| 541 + case 0 : return 0; /* NULL */ | 696 + case 0 : return 0; /* NULL */ |
| 542 + case 1 : return 1; /* Various integers. */ | 697 + case 1 : return 1; /* Various integers. */ |
| 543 + case 2 : return 2; | 698 + case 2 : return 2; |
| 544 + case 3 : return 3; | 699 + case 3 : return 3; |
| 545 + case 4 : return 4; | 700 + case 4 : return 4; |
| 546 + case 5 : return 6; | 701 + case 5 : return 6; |
| 547 + case 6 : return 8; | 702 + case 6 : return 8; |
| 548 + case 7 : return 8; /* 64-bit float. */ | 703 + case 7 : return 8; /* 64-bit float. */ |
| 549 + case 8 : return 0; /* Constant 0. */ | 704 + case 8 : return 0; /* Constant 0. */ |
| 550 + case 9 : return 0; /* Constant 1. */ | 705 + case 9 : return 0; /* Constant 1. */ |
| 551 + case 10 : case 11 : assert( !"RESERVED TYPE"); return 0; | 706 + case 10 : case 11 : assert( "RESERVED TYPE"==NULL ); return 0; |
| 552 + } | 707 + } |
| 553 + return (u32)((iSerialType>>1) - 6); | 708 + return (u32)((iSerialType>>1) - 6); |
| 554 +} | 709 +} |
| 555 + | 710 + |
| 556 +/* True if iSerialType refers to a blob. */ | 711 +/* True if iSerialType refers to a blob. */ |
| 557 +static int SerialTypeIsBlob(u64 iSerialType){ | 712 +static int SerialTypeIsBlob(u64 iSerialType){ |
| 558 + assert( iSerialType>=12 ); | 713 + assert( iSerialType>=12 ); |
| 559 + return (iSerialType%2)==0; | 714 + return (iSerialType%2)==0; |
| 560 +} | 715 +} |
| 561 + | 716 + |
| 562 +/* Returns true if the serialized type represented by iSerialType is | 717 +/* Returns true if the serialized type represented by iSerialType is |
| 563 + * compatible with the given type mask. | 718 + * compatible with the given type mask. |
| 564 + */ | 719 + */ |
| 565 +static int SerialTypeIsCompatible(u64 iSerialType, unsigned char mask){ | 720 +static int SerialTypeIsCompatible(u64 iSerialType, unsigned char mask){ |
| 566 + switch( iSerialType ){ | 721 + switch( iSerialType ){ |
| 567 + case 0 : return (mask&MASK_NULL)!=0; | 722 + case 0 : return (mask&MASK_NULL)!=0; |
| 568 + case 1 : return (mask&MASK_INTEGER)!=0; | 723 + case 1 : return (mask&MASK_INTEGER)!=0; |
| 569 + case 2 : return (mask&MASK_INTEGER)!=0; | 724 + case 2 : return (mask&MASK_INTEGER)!=0; |
| 570 + case 3 : return (mask&MASK_INTEGER)!=0; | 725 + case 3 : return (mask&MASK_INTEGER)!=0; |
| 571 + case 4 : return (mask&MASK_INTEGER)!=0; | 726 + case 4 : return (mask&MASK_INTEGER)!=0; |
| 572 + case 5 : return (mask&MASK_INTEGER)!=0; | 727 + case 5 : return (mask&MASK_INTEGER)!=0; |
| 573 + case 6 : return (mask&MASK_INTEGER)!=0; | 728 + case 6 : return (mask&MASK_INTEGER)!=0; |
| 574 + case 7 : return (mask&MASK_FLOAT)!=0; | 729 + case 7 : return (mask&MASK_FLOAT)!=0; |
| 575 + case 8 : return (mask&MASK_INTEGER)!=0; | 730 + case 8 : return (mask&MASK_INTEGER)!=0; |
| 576 + case 9 : return (mask&MASK_INTEGER)!=0; | 731 + case 9 : return (mask&MASK_INTEGER)!=0; |
| 577 + case 10 : assert( !"RESERVED TYPE"); return 0; | 732 + case 10 : assert( "RESERVED TYPE"==NULL ); return 0; |
| 578 + case 11 : assert( !"RESERVED TYPE"); return 0; | 733 + case 11 : assert( "RESERVED TYPE"==NULL ); return 0; |
| 579 + } | 734 + } |
| 580 + return (mask&(SerialTypeIsBlob(iSerialType) ? MASK_BLOB : MASK_TEXT)); | 735 + return (mask&(SerialTypeIsBlob(iSerialType) ? MASK_BLOB : MASK_TEXT)); |
| 581 +} | 736 +} |
| 582 + | 737 + |
| 583 +/* Versions of strdup() with return values appropriate for | 738 +/* Versions of strdup() with return values appropriate for |
| 584 + * sqlite3_free(). malloc.c has sqlite3DbStrDup()/NDup(), but those | 739 + * sqlite3_free(). malloc.c has sqlite3DbStrDup()/NDup(), but those |
| 585 + * need sqlite3DbFree(), which seems intrusive. | 740 + * need sqlite3DbFree(), which seems intrusive. |
| 586 + */ | 741 + */ |
| 587 +static char *sqlite3_strndup(const char *z, unsigned n){ | 742 +static char *sqlite3_strndup(const char *z, unsigned n){ |
| 588 + char *zNew; | 743 + char *zNew; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 644 + if( rc==SQLITE_DONE ){ | 799 + if( rc==SQLITE_DONE ){ |
| 645 + rc = SQLITE_OK; | 800 + rc = SQLITE_OK; |
| 646 + }else if( rc==SQLITE_ROW ){ | 801 + }else if( rc==SQLITE_ROW ){ |
| 647 + rc = SQLITE_CORRUPT; | 802 + rc = SQLITE_CORRUPT; |
| 648 + } | 803 + } |
| 649 + } | 804 + } |
| 650 + sqlite3_finalize(pStmt); | 805 + sqlite3_finalize(pStmt); |
| 651 + return rc; | 806 + return rc; |
| 652 +} | 807 +} |
| 653 + | 808 + |
| 654 +static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){ | |
| 655 + sqlite3_stmt *pStmt; | |
| 656 + int rc; | |
| 657 + char *zSql = sqlite3_mprintf("PRAGMA %s.encoding", zDb); | |
| 658 + if( !zSql ){ | |
| 659 + return SQLITE_NOMEM; | |
| 660 + } | |
| 661 + | |
| 662 + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); | |
| 663 + sqlite3_free(zSql); | |
| 664 + if( rc!=SQLITE_OK ){ | |
| 665 + return rc; | |
| 666 + } | |
| 667 + | |
| 668 + /* Require a result. */ | |
| 669 + rc = sqlite3_step(pStmt); | |
| 670 + if( rc==SQLITE_DONE ){ | |
| 671 + /* This case should not be possible. */ | |
| 672 + rc = SQLITE_CORRUPT; | |
| 673 + }else if( rc==SQLITE_ROW ){ | |
| 674 + if( sqlite3_column_type(pStmt, 0)==SQLITE_TEXT ){ | |
| 675 + const char* z = (const char *)sqlite3_column_text(pStmt, 0); | |
| 676 + /* These strings match the literals in pragma.c. */ | |
| 677 + if( !strcmp(z, "UTF-16le") ){ | |
| 678 + *piEncoding = SQLITE_UTF16LE; | |
| 679 + }else if( !strcmp(z, "UTF-16be") ){ | |
| 680 + *piEncoding = SQLITE_UTF16BE; | |
| 681 + }else if( !strcmp(z, "UTF-8") ){ | |
| 682 + *piEncoding = SQLITE_UTF8; | |
| 683 + }else{ | |
| 684 + /* This case should not be possible. */ | |
| 685 + *piEncoding = SQLITE_UTF8; | |
| 686 + } | |
| 687 + }else{ | |
| 688 + /* This case should not be possible. */ | |
| 689 + *piEncoding = SQLITE_UTF8; | |
| 690 + } | |
| 691 + | |
| 692 + /* Require only one result. */ | |
| 693 + rc = sqlite3_step(pStmt); | |
| 694 + if( rc==SQLITE_DONE ){ | |
| 695 + rc = SQLITE_OK; | |
| 696 + }else if( rc==SQLITE_ROW ){ | |
| 697 + /* This case should not be possible. */ | |
| 698 + rc = SQLITE_CORRUPT; | |
| 699 + } | |
| 700 + } | |
| 701 + sqlite3_finalize(pStmt); | |
| 702 + return rc; | |
| 703 +} | |
| 704 + | |
| 705 +/* Cursor for iterating interior nodes. Interior page cells contain a | 809 +/* Cursor for iterating interior nodes. Interior page cells contain a |
| 706 + * child page number and a rowid. The child page contains items left | 810 + * child page number and a rowid. The child page contains items left |
| 707 + * of the rowid (less than). The rightmost page of the subtree is | 811 + * of the rowid (less than). The rightmost page of the subtree is |
| 708 + * stored in the page header. | 812 + * stored in the page header. |
| 709 + * | 813 + * |
| 710 + * interiorCursorDestroy - release all resources associated with the | 814 + * interiorCursorDestroy - release all resources associated with the |
| 711 + * cursor and any parent cursors. | 815 + * cursor and any parent cursors. |
| 712 + * interiorCursorCreate - create a cursor with the given parent and page. | 816 + * interiorCursorCreate - create a cursor with the given parent and page. |
| 713 + * interiorCursorEOF - returns true if neither the cursor nor the | 817 + * interiorCursorEOF - returns true if neither the cursor nor the |
| 714 + * parent cursors can return any more data. | 818 + * parent cursors can return any more data. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 730 + * preventing duplication. | 834 + * preventing duplication. |
| 731 + * | 835 + * |
| 732 + * Note that interiorCursorEOF() could return false (not at EOF), and | 836 + * Note that interiorCursorEOF() could return false (not at EOF), and |
| 733 + * interiorCursorNextPage() could still return SQLITE_DONE. This | 837 + * interiorCursorNextPage() could still return SQLITE_DONE. This |
| 734 + * could happen if there are more cells to iterate in an interior | 838 + * could happen if there are more cells to iterate in an interior |
| 735 + * page, but those cells refer to invalid pages. | 839 + * page, but those cells refer to invalid pages. |
| 736 + */ | 840 + */ |
| 737 +typedef struct RecoverInteriorCursor RecoverInteriorCursor; | 841 +typedef struct RecoverInteriorCursor RecoverInteriorCursor; |
| 738 +struct RecoverInteriorCursor { | 842 +struct RecoverInteriorCursor { |
| 739 + RecoverInteriorCursor *pParent; /* Parent node to this node. */ | 843 + RecoverInteriorCursor *pParent; /* Parent node to this node. */ |
| 740 + DbPage *pPage; /* Reference to leaf page. */ | 844 + RecoverPage *pPage; /* Reference to leaf page. */ |
| 741 + unsigned nPageSize; /* Size of page. */ | 845 + unsigned nPageSize; /* Size of page. */ |
| 742 + unsigned nChildren; /* Number of children on the page. */ | 846 + unsigned nChildren; /* Number of children on the page. */ |
| 743 + unsigned iChild; /* Index of next child to return. */ | 847 + unsigned iChild; /* Index of next child to return. */ |
| 744 +}; | 848 +}; |
| 745 + | 849 + |
| 746 +static void interiorCursorDestroy(RecoverInteriorCursor *pCursor){ | 850 +static void interiorCursorDestroy(RecoverInteriorCursor *pCursor){ |
| 747 + /* Destroy all the cursors to the root. */ | 851 + /* Destroy all the cursors to the root. */ |
| 748 + while( pCursor ){ | 852 + while( pCursor ){ |
| 749 + RecoverInteriorCursor *p = pCursor; | 853 + RecoverInteriorCursor *p = pCursor; |
| 750 + pCursor = pCursor->pParent; | 854 + pCursor = pCursor->pParent; |
| 751 + | 855 + |
| 752 + if( p->pPage ){ | 856 + if( p->pPage ){ |
| 753 + sqlite3PagerUnref(p->pPage); | 857 + pageDestroy(p->pPage); |
| 754 + p->pPage = NULL; | 858 + p->pPage = NULL; |
| 755 + } | 859 + } |
| 756 + | 860 + |
| 757 + memset(p, 0xA5, sizeof(*p)); | 861 + memset(p, 0xA5, sizeof(*p)); |
| 758 + sqlite3_free(p); | 862 + sqlite3_free(p); |
| 759 + } | 863 + } |
| 760 +} | 864 +} |
| 761 + | 865 + |
| 762 +/* Internal helper. Reset storage in preparation for iterating pPage. */ | 866 +/* Internal helper. Reset storage in preparation for iterating pPage. */ |
| 763 +static void interiorCursorSetPage(RecoverInteriorCursor *pCursor, | 867 +static void interiorCursorSetPage(RecoverInteriorCursor *pCursor, |
| 764 + DbPage *pPage){ | 868 + RecoverPage *pPage){ |
| 765 + const unsigned knMinCellLength = 2 + 4 + 1; | 869 + const unsigned knMinCellLength = 2 + 4 + 1; |
| 766 + unsigned nMaxChildren; | 870 + unsigned nMaxChildren; |
| 767 + assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage ); | 871 + assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage ); |
| 768 + | 872 + |
| 769 + if( pCursor->pPage ){ | 873 + if( pCursor->pPage ){ |
| 770 + sqlite3PagerUnref(pCursor->pPage); | 874 + pageDestroy(pCursor->pPage); |
| 771 + pCursor->pPage = NULL; | 875 + pCursor->pPage = NULL; |
| 772 + } | 876 + } |
| 773 + pCursor->pPage = pPage; | 877 + pCursor->pPage = pPage; |
| 774 + pCursor->iChild = 0; | 878 + pCursor->iChild = 0; |
| 775 + | 879 + |
| 776 + /* A child for each cell, plus one in the header. */ | 880 + /* A child for each cell, plus one in the header. */ |
| 777 + pCursor->nChildren = decodeUnsigned16(PageHeader(pPage) + | 881 + pCursor->nChildren = decodeUnsigned16(PageHeader(pPage) + |
| 778 + kiPageCellCountOffset) + 1; | 882 + kiPageCellCountOffset) + 1; |
| 779 + | 883 + |
| 780 + /* Each child requires a 16-bit offset from an array after the header, | 884 + /* Each child requires a 16-bit offset from an array after the header, |
| (...skipping 10 matching lines...) Expand all Loading... |
| 791 + * a very large database. | 895 + * a very large database. |
| 792 + */ | 896 + */ |
| 793 + nMaxChildren = | 897 + nMaxChildren = |
| 794 + (pCursor->nPageSize - kiPageInteriorHeaderBytes) / knMinCellLength + 1; | 898 + (pCursor->nPageSize - kiPageInteriorHeaderBytes) / knMinCellLength + 1; |
| 795 + if (pCursor->nChildren > nMaxChildren) { | 899 + if (pCursor->nChildren > nMaxChildren) { |
| 796 + pCursor->nChildren = nMaxChildren; | 900 + pCursor->nChildren = nMaxChildren; |
| 797 + } | 901 + } |
| 798 +} | 902 +} |
| 799 + | 903 + |
| 800 +static int interiorCursorCreate(RecoverInteriorCursor *pParent, | 904 +static int interiorCursorCreate(RecoverInteriorCursor *pParent, |
| 801 + DbPage *pPage, int nPageSize, | 905 + RecoverPage *pPage, int nPageSize, |
| 802 + RecoverInteriorCursor **ppCursor){ | 906 + RecoverInteriorCursor **ppCursor){ |
| 803 + RecoverInteriorCursor *pCursor = | 907 + RecoverInteriorCursor *pCursor = |
| 804 + sqlite3_malloc(sizeof(RecoverInteriorCursor)); | 908 + sqlite3_malloc(sizeof(RecoverInteriorCursor)); |
| 805 + if( !pCursor ){ | 909 + if( !pCursor ){ |
| 806 + return SQLITE_NOMEM; | 910 + return SQLITE_NOMEM; |
| 807 + } | 911 + } |
| 808 + | 912 + |
| 809 + memset(pCursor, 0, sizeof(*pCursor)); | 913 + memset(pCursor, 0, sizeof(*pCursor)); |
| 810 + pCursor->pParent = pParent; | 914 + pCursor->pParent = pParent; |
| 811 + pCursor->nPageSize = nPageSize; | 915 + pCursor->nPageSize = nPageSize; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 876 + * updated to point to the parent cursor (this cursor is freed). | 980 + * updated to point to the parent cursor (this cursor is freed). |
| 877 + */ | 981 + */ |
| 878 +/* TODO(shess): I've tried to avoid recursion in most of this code, | 982 +/* TODO(shess): I've tried to avoid recursion in most of this code, |
| 879 + * but this case is more challenging because the recursive call is in | 983 + * but this case is more challenging because the recursive call is in |
| 880 + * the middle of operation. One option for converting it without | 984 + * the middle of operation. One option for converting it without |
| 881 + * adding memory management would be to retain the head pointer and | 985 + * adding memory management would be to retain the head pointer and |
| 882 + * use a helper to "back up" as needed. Another option would be to | 986 + * use a helper to "back up" as needed. Another option would be to |
| 883 + * reverse the list during traversal. | 987 + * reverse the list during traversal. |
| 884 + */ | 988 + */ |
| 885 +static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, | 989 +static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, |
| 886 + DbPage **ppPage){ | 990 + RecoverPage **ppPage){ |
| 887 + RecoverInteriorCursor *pCursor = *ppCursor; | 991 + RecoverInteriorCursor *pCursor = *ppCursor; |
| 888 + while( 1 ){ | 992 + while( 1 ){ |
| 889 + int rc; | 993 + int rc; |
| 890 + const unsigned char *pPageHeader; /* Header of found page. */ | 994 + const unsigned char *pPageHeader; /* Header of found page. */ |
| 891 + | 995 + |
| 892 + /* Find a valid child page which isn't on the stack. */ | 996 + /* Find a valid child page which isn't on the stack. */ |
| 893 + while( pCursor->iChild<pCursor->nChildren ){ | 997 + while( pCursor->iChild<pCursor->nChildren ){ |
| 894 + const unsigned iPage = interiorCursorChildPage(pCursor); | 998 + const unsigned iPage = interiorCursorChildPage(pCursor); |
| 895 + pCursor->iChild++; | 999 + pCursor->iChild++; |
| 896 + if( interiorCursorPageInUse(pCursor, iPage) ){ | 1000 + if( interiorCursorPageInUse(pCursor, iPage) ){ |
| 897 + fprintf(stderr, "Loop detected at %d\n", iPage); | 1001 + fprintf(stderr, "Loop detected at %d\n", iPage); |
| 898 + }else{ | 1002 + }else{ |
| 899 + int rc = sqlite3PagerGet(pCursor->pPage->pPager, iPage, ppPage, 0); | 1003 + int rc = pagerGetPage(pCursor->pPage->pPager, iPage, ppPage); |
| 900 + if( rc==SQLITE_OK ){ | 1004 + if( rc==SQLITE_OK ){ |
| 901 + return SQLITE_ROW; | 1005 + return SQLITE_ROW; |
| 902 + } | 1006 + } |
| 903 + } | 1007 + } |
| 904 + } | 1008 + } |
| 905 + | 1009 + |
| 906 + /* This page has no more children. Get next page from parent. */ | 1010 + /* This page has no more children. Get next page from parent. */ |
| 907 + if( !pCursor->pParent ){ | 1011 + if( !pCursor->pParent ){ |
| 908 + return SQLITE_DONE; | 1012 + return SQLITE_DONE; |
| 909 + } | 1013 + } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 943 + * overflowMaybeCreate - create the overflow structure if it is needed | 1047 + * overflowMaybeCreate - create the overflow structure if it is needed |
| 944 + * to represent the given record. See function comment. | 1048 + * to represent the given record. See function comment. |
| 945 + * overflowGetSegment - fetch a segment from the record, accounting | 1049 + * overflowGetSegment - fetch a segment from the record, accounting |
| 946 + * for overflow pages. Segments which are not | 1050 + * for overflow pages. Segments which are not |
| 947 + * entirely contained with a page are constructed | 1051 + * entirely contained with a page are constructed |
| 948 + * into a buffer which is returned. See function comment. | 1052 + * into a buffer which is returned. See function comment. |
| 949 + */ | 1053 + */ |
| 950 +typedef struct RecoverOverflow RecoverOverflow; | 1054 +typedef struct RecoverOverflow RecoverOverflow; |
| 951 +struct RecoverOverflow { | 1055 +struct RecoverOverflow { |
| 952 + RecoverOverflow *pNextOverflow; | 1056 + RecoverOverflow *pNextOverflow; |
| 953 + DbPage *pPage; | 1057 + RecoverPage *pPage; |
| 954 + unsigned nPageSize; | 1058 + unsigned nPageSize; |
| 955 +}; | 1059 +}; |
| 956 + | 1060 + |
| 957 +static void overflowDestroy(RecoverOverflow *pOverflow){ | 1061 +static void overflowDestroy(RecoverOverflow *pOverflow){ |
| 958 + while( pOverflow ){ | 1062 + while( pOverflow ){ |
| 959 + RecoverOverflow *p = pOverflow; | 1063 + RecoverOverflow *p = pOverflow; |
| 960 + pOverflow = p->pNextOverflow; | 1064 + pOverflow = p->pNextOverflow; |
| 961 + | 1065 + |
| 962 + if( p->pPage ){ | 1066 + if( p->pPage ){ |
| 963 + sqlite3PagerUnref(p->pPage); | 1067 + pageDestroy(p->pPage); |
| 964 + p->pPage = NULL; | 1068 + p->pPage = NULL; |
| 965 + } | 1069 + } |
| 966 + | 1070 + |
| 967 + memset(p, 0xA5, sizeof(*p)); | 1071 + memset(p, 0xA5, sizeof(*p)); |
| 968 + sqlite3_free(p); | 1072 + sqlite3_free(p); |
| 969 + } | 1073 + } |
| 970 +} | 1074 +} |
| 971 + | 1075 + |
| 972 +/* Internal helper. Used to detect if iPage would cause a loop. */ | 1076 +/* Internal helper. Used to detect if iPage would cause a loop. */ |
| 973 +static int overflowPageInUse(RecoverOverflow *pOverflow, unsigned iPage){ | 1077 +static int overflowPageInUse(RecoverOverflow *pOverflow, unsigned iPage){ |
| 974 + while( pOverflow && pOverflow->pPage->pgno!=iPage ){ | 1078 + while( pOverflow && pOverflow->pPage->pgno!=iPage ){ |
| 975 + pOverflow = pOverflow->pNextOverflow; | 1079 + pOverflow = pOverflow->pNextOverflow; |
| 976 + } | 1080 + } |
| 977 + return pOverflow!=NULL; | 1081 + return pOverflow!=NULL; |
| 978 +} | 1082 +} |
| 979 + | 1083 + |
| 980 +/* Setup to access an nRecordBytes record beginning at iRecordOffset | 1084 +/* Setup to access an nRecordBytes record beginning at iRecordOffset |
| 981 + * in pPage. If nRecordBytes can be satisfied entirely from pPage, | 1085 + * in pPage. If nRecordBytes can be satisfied entirely from pPage, |
| 982 + * then no overflow pages are needed an *pnLocalRecordBytes is set to | 1086 + * then no overflow pages are needed an *pnLocalRecordBytes is set to |
| 983 + * nRecordBytes. Otherwise, *ppOverflow is set to the head of a list | 1087 + * nRecordBytes. Otherwise, *ppOverflow is set to the head of a list |
| 984 + * of overflow pages, and *pnLocalRecordBytes is set to the number of | 1088 + * of overflow pages, and *pnLocalRecordBytes is set to the number of |
| 985 + * bytes local to pPage. | 1089 + * bytes local to pPage. |
| 986 + * | 1090 + * |
| 987 + * overflowGetSegment() will do the right thing regardless of whether | 1091 + * overflowGetSegment() will do the right thing regardless of whether |
| 988 + * those values are set to be in-page or not. | 1092 + * those values are set to be in-page or not. |
| 989 + */ | 1093 + */ |
| 990 +static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, | 1094 +static int overflowMaybeCreate(RecoverPage *pPage, unsigned nPageSize, |
| 991 + unsigned iRecordOffset, unsigned nRecordBytes, | 1095 + unsigned iRecordOffset, unsigned nRecordBytes, |
| 992 + unsigned *pnLocalRecordBytes, | 1096 + unsigned *pnLocalRecordBytes, |
| 993 + RecoverOverflow **ppOverflow){ | 1097 + RecoverOverflow **ppOverflow){ |
| 994 + unsigned nLocalRecordBytes; /* Record bytes in the leaf page. */ | 1098 + unsigned nLocalRecordBytes; /* Record bytes in the leaf page. */ |
| 995 + unsigned iNextPage; /* Next page number for record data. */ | 1099 + unsigned iNextPage; /* Next page number for record data. */ |
| 996 + unsigned nBytes; /* Maximum record bytes as of current page. */ | 1100 + unsigned nBytes; /* Maximum record bytes as of current page. */ |
| 997 + int rc; | 1101 + int rc; |
| 998 + RecoverOverflow *pFirstOverflow; /* First in linked list of pages. */ | 1102 + RecoverOverflow *pFirstOverflow; /* First in linked list of pages. */ |
| 999 + RecoverOverflow *pLastOverflow; /* End of linked list. */ | 1103 + RecoverOverflow *pLastOverflow; /* End of linked list. */ |
| 1000 + | 1104 + |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1033 + nBytes = nLocalRecordBytes; | 1137 + nBytes = nLocalRecordBytes; |
| 1034 + | 1138 + |
| 1035 + /* While there are more pages to read, and more bytes are needed, | 1139 + /* While there are more pages to read, and more bytes are needed, |
| 1036 + * get another page. | 1140 + * get another page. |
| 1037 + */ | 1141 + */ |
| 1038 + pFirstOverflow = pLastOverflow = NULL; | 1142 + pFirstOverflow = pLastOverflow = NULL; |
| 1039 + rc = SQLITE_OK; | 1143 + rc = SQLITE_OK; |
| 1040 + while( iNextPage && nBytes<nRecordBytes ){ | 1144 + while( iNextPage && nBytes<nRecordBytes ){ |
| 1041 + RecoverOverflow *pOverflow; /* New overflow page for the list. */ | 1145 + RecoverOverflow *pOverflow; /* New overflow page for the list. */ |
| 1042 + | 1146 + |
| 1043 + rc = sqlite3PagerGet(pPage->pPager, iNextPage, &pPage, 0); | 1147 + rc = pagerGetPage(pPage->pPager, iNextPage, &pPage); |
| 1044 + if( rc!=SQLITE_OK ){ | 1148 + if( rc!=SQLITE_OK ){ |
| 1045 + break; | 1149 + break; |
| 1046 + } | 1150 + } |
| 1047 + | 1151 + |
| 1048 + pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); | 1152 + pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); |
| 1049 + if( !pOverflow ){ | 1153 + if( !pOverflow ){ |
| 1050 + sqlite3PagerUnref(pPage); | 1154 + pageDestroy(pPage); |
| 1051 + rc = SQLITE_NOMEM; | 1155 + rc = SQLITE_NOMEM; |
| 1052 + break; | 1156 + break; |
| 1053 + } | 1157 + } |
| 1054 + memset(pOverflow, 0, sizeof(*pOverflow)); | 1158 + memset(pOverflow, 0, sizeof(*pOverflow)); |
| 1055 + pOverflow->pPage = pPage; | 1159 + pOverflow->pPage = pPage; |
| 1056 + pOverflow->nPageSize = nPageSize; | 1160 + pOverflow->nPageSize = nPageSize; |
| 1057 + | 1161 + |
| 1058 + if( !pFirstOverflow ){ | 1162 + if( !pFirstOverflow ){ |
| 1059 + pFirstOverflow = pOverflow; | 1163 + pFirstOverflow = pOverflow; |
| 1060 + }else{ | 1164 + }else{ |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1104 + * sqlite3_malloc() is returned with the data. *pbFree is set true if | 1208 + * sqlite3_malloc() is returned with the data. *pbFree is set true if |
| 1105 + * sqlite3_free() should be called on *ppBase. | 1209 + * sqlite3_free() should be called on *ppBase. |
| 1106 + */ | 1210 + */ |
| 1107 +/* Operation of this function is subtle. At any time, pPage is the | 1211 +/* Operation of this function is subtle. At any time, pPage is the |
| 1108 + * current page, with iRecordOffset and nLocalRecordBytes being record | 1212 + * current page, with iRecordOffset and nLocalRecordBytes being record |
| 1109 + * data within pPage, and pOverflow being the overflow page after | 1213 + * data within pPage, and pOverflow being the overflow page after |
| 1110 + * pPage. This allows the code to handle both the initial leaf page | 1214 + * pPage. This allows the code to handle both the initial leaf page |
| 1111 + * and overflow pages consistently by adjusting the values | 1215 + * and overflow pages consistently by adjusting the values |
| 1112 + * appropriately. | 1216 + * appropriately. |
| 1113 + */ | 1217 + */ |
| 1114 +static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, | 1218 +static int overflowGetSegment(RecoverPage *pPage, unsigned iRecordOffset, |
| 1115 + unsigned nLocalRecordBytes, | 1219 + unsigned nLocalRecordBytes, |
| 1116 + RecoverOverflow *pOverflow, | 1220 + RecoverOverflow *pOverflow, |
| 1117 + unsigned iRequestOffset, unsigned nRequestBytes, | 1221 + unsigned iRequestOffset, unsigned nRequestBytes, |
| 1118 + unsigned char **ppBase, int *pbFree){ | 1222 + unsigned char **ppBase, int *pbFree){ |
| 1119 + unsigned nBase; /* Amount of data currently collected. */ | 1223 + unsigned nBase; /* Amount of data currently collected. */ |
| 1120 + unsigned char *pBase; /* Buffer to collect record data into. */ | 1224 + unsigned char *pBase; /* Buffer to collect record data into. */ |
| 1121 + | 1225 + |
| 1122 + /* Skip to the page containing the start of the data. */ | 1226 + /* Skip to the page containing the start of the data. */ |
| 1123 + while( iRequestOffset>=nLocalRecordBytes && pOverflow ){ | 1227 + while( iRequestOffset>=nLocalRecordBytes && pOverflow ){ |
| 1124 + /* Factor out current page's contribution. */ | 1228 + /* Factor out current page's contribution. */ |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1211 + * impossible offsets, or where header data doesn't correctly describe | 1315 + * impossible offsets, or where header data doesn't correctly describe |
| 1212 + * payload data. Returns SQLITE_ROW if a valid cell is found, | 1316 + * payload data. Returns SQLITE_ROW if a valid cell is found, |
| 1213 + * SQLITE_DONE if all pages in the tree were exhausted. | 1317 + * SQLITE_DONE if all pages in the tree were exhausted. |
| 1214 + * | 1318 + * |
| 1215 + * leafCursorCellColInfo() accounts for overflow pages in the style of | 1319 + * leafCursorCellColInfo() accounts for overflow pages in the style of |
| 1216 + * overflowGetSegment(). | 1320 + * overflowGetSegment(). |
| 1217 + */ | 1321 + */ |
| 1218 +typedef struct RecoverLeafCursor RecoverLeafCursor; | 1322 +typedef struct RecoverLeafCursor RecoverLeafCursor; |
| 1219 +struct RecoverLeafCursor { | 1323 +struct RecoverLeafCursor { |
| 1220 + RecoverInteriorCursor *pParent; /* Parent node to this node. */ | 1324 + RecoverInteriorCursor *pParent; /* Parent node to this node. */ |
| 1221 + DbPage *pPage; /* Reference to leaf page. */ | 1325 + RecoverPager *pPager; /* Page provider. */ |
| 1326 + RecoverPage *pPage; /* Current leaf page. */ |
| 1222 + unsigned nPageSize; /* Size of pPage. */ | 1327 + unsigned nPageSize; /* Size of pPage. */ |
| 1223 + unsigned nCells; /* Number of cells in pPage. */ | 1328 + unsigned nCells; /* Number of cells in pPage. */ |
| 1224 + unsigned iCell; /* Current cell. */ | 1329 + unsigned iCell; /* Current cell. */ |
| 1225 + | 1330 + |
| 1226 + /* Info parsed from data in iCell. */ | 1331 + /* Info parsed from data in iCell. */ |
| 1227 + i64 iRowid; /* rowid parsed. */ | 1332 + i64 iRowid; /* rowid parsed. */ |
| 1228 + unsigned nRecordCols; /* how many items in the record. */ | 1333 + unsigned nRecordCols; /* how many items in the record. */ |
| 1229 + u64 iRecordOffset; /* offset to record data. */ | 1334 + u64 iRecordOffset; /* offset to record data. */ |
| 1230 + /* TODO(shess): nRecordBytes and nRecordHeaderBytes are used in | 1335 + /* TODO(shess): nRecordBytes and nRecordHeaderBytes are used in |
| 1231 + * leafCursorCellColInfo() to prevent buffer overruns. | 1336 + * leafCursorCellColInfo() to prevent buffer overruns. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1246 + * | 1351 + * |
| 1247 + * If pPage is an interior page, a new parent cursor is created and | 1352 + * If pPage is an interior page, a new parent cursor is created and |
| 1248 + * injected on the stack. This is necessary to handle trees with | 1353 + * injected on the stack. This is necessary to handle trees with |
| 1249 + * uneven depth, but also is used during initial setup. | 1354 + * uneven depth, but also is used during initial setup. |
| 1250 + * | 1355 + * |
| 1251 + * If pPage is not a table page at all, it is discarded. | 1356 + * If pPage is not a table page at all, it is discarded. |
| 1252 + * | 1357 + * |
| 1253 + * If SQLITE_OK is returned, the caller no longer owns pPage, | 1358 + * If SQLITE_OK is returned, the caller no longer owns pPage, |
| 1254 + * otherwise the caller is responsible for discarding it. | 1359 + * otherwise the caller is responsible for discarding it. |
| 1255 + */ | 1360 + */ |
| 1256 +static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){ | 1361 +static int leafCursorLoadPage(RecoverLeafCursor *pCursor, RecoverPage *pPage){ |
| 1257 + const unsigned char *pPageHeader; /* Header of *pPage */ | 1362 + const unsigned char *pPageHeader; /* Header of *pPage */ |
| 1363 + unsigned nCells; /* Number of cells in the page */ |
| 1258 + | 1364 + |
| 1259 + /* Release the current page. */ | 1365 + /* Release the current page. */ |
| 1260 + if( pCursor->pPage ){ | 1366 + if( pCursor->pPage ){ |
| 1261 + sqlite3PagerUnref(pCursor->pPage); | 1367 + pageDestroy(pCursor->pPage); |
| 1262 + pCursor->pPage = NULL; | 1368 + pCursor->pPage = NULL; |
| 1263 + pCursor->iCell = pCursor->nCells = 0; | 1369 + pCursor->iCell = pCursor->nCells = 0; |
| 1264 + } | 1370 + } |
| 1265 + | 1371 + |
| 1266 + /* If the page is an unexpected interior node, inject a new stack | 1372 + /* If the page is an unexpected interior node, inject a new stack |
| 1267 + * layer and try again from there. | 1373 + * layer and try again from there. |
| 1268 + */ | 1374 + */ |
| 1269 + pPageHeader = PageHeader(pPage); | 1375 + pPageHeader = PageHeader(pPage); |
| 1270 + if( pPageHeader[kiPageTypeOffset]==kTableInteriorPage ){ | 1376 + if( pPageHeader[kiPageTypeOffset]==kTableInteriorPage ){ |
| 1271 + RecoverInteriorCursor *pParent; | 1377 + RecoverInteriorCursor *pParent; |
| 1272 + int rc = interiorCursorCreate(pCursor->pParent, pPage, pCursor->nPageSize, | 1378 + int rc = interiorCursorCreate(pCursor->pParent, pPage, pCursor->nPageSize, |
| 1273 + &pParent); | 1379 + &pParent); |
| 1274 + if( rc!=SQLITE_OK ){ | 1380 + if( rc!=SQLITE_OK ){ |
| 1275 + return rc; | 1381 + return rc; |
| 1276 + } | 1382 + } |
| 1277 + pCursor->pParent = pParent; | 1383 + pCursor->pParent = pParent; |
| 1278 + return SQLITE_OK; | 1384 + return SQLITE_OK; |
| 1279 + } | 1385 + } |
| 1280 + | 1386 + |
| 1281 + /* Not a leaf page, skip it. */ | 1387 + /* Not a leaf page, skip it. */ |
| 1282 + if( pPageHeader[kiPageTypeOffset]!=kTableLeafPage ){ | 1388 + if( pPageHeader[kiPageTypeOffset]!=kTableLeafPage ){ |
| 1283 + sqlite3PagerUnref(pPage); | 1389 + pageDestroy(pPage); |
| 1390 + return SQLITE_OK; |
| 1391 + } |
| 1392 + |
| 1393 + /* Leaf contains no data, skip it. Empty tables, for instance. */ |
| 1394 + nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset);; |
| 1395 + if( nCells<1 ){ |
| 1396 + pageDestroy(pPage); |
| 1284 + return SQLITE_OK; | 1397 + return SQLITE_OK; |
| 1285 + } | 1398 + } |
| 1286 + | 1399 + |
| 1287 + /* Take ownership of the page and start decoding. */ | 1400 + /* Take ownership of the page and start decoding. */ |
| 1288 + pCursor->pPage = pPage; | 1401 + pCursor->pPage = pPage; |
| 1289 + pCursor->iCell = 0; | 1402 + pCursor->iCell = 0; |
| 1290 + pCursor->nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset); | 1403 + pCursor->nCells = nCells; |
| 1291 + return SQLITE_OK; | 1404 + return SQLITE_OK; |
| 1292 +} | 1405 +} |
| 1293 + | 1406 + |
| 1294 +/* Get the next leaf-level page in the tree. Returns SQLITE_ROW when | 1407 +/* Get the next leaf-level page in the tree. Returns SQLITE_ROW when |
| 1295 + * a leaf page is found, SQLITE_DONE when no more leaves exist, or any | 1408 + * a leaf page is found, SQLITE_DONE when no more leaves exist, or any |
| 1296 + * error which occurred. | 1409 + * error which occurred. |
| 1297 + */ | 1410 + */ |
| 1298 +static int leafCursorNextPage(RecoverLeafCursor *pCursor){ | 1411 +static int leafCursorNextPage(RecoverLeafCursor *pCursor){ |
| 1299 + if( !pCursor->pParent ){ | 1412 + if( !pCursor->pParent ){ |
| 1300 + return SQLITE_DONE; | 1413 + return SQLITE_DONE; |
| 1301 + } | 1414 + } |
| 1302 + | 1415 + |
| 1303 + /* Repeatedly load the parent's next child page until a leaf is found. */ | 1416 + /* Repeatedly load the parent's next child page until a leaf is found. */ |
| 1304 + do { | 1417 + do { |
| 1305 + DbPage *pNextPage; | 1418 + RecoverPage *pNextPage; |
| 1306 + int rc = interiorCursorNextPage(&pCursor->pParent, &pNextPage); | 1419 + int rc = interiorCursorNextPage(&pCursor->pParent, &pNextPage); |
| 1307 + if( rc!=SQLITE_ROW ){ | 1420 + if( rc!=SQLITE_ROW ){ |
| 1308 + assert( rc==SQLITE_DONE ); | 1421 + assert( rc==SQLITE_DONE ); |
| 1309 + return rc; | 1422 + return rc; |
| 1310 + } | 1423 + } |
| 1311 + | 1424 + |
| 1312 + rc = leafCursorLoadPage(pCursor, pNextPage); | 1425 + rc = leafCursorLoadPage(pCursor, pNextPage); |
| 1313 + if( rc!=SQLITE_OK ){ | 1426 + if( rc!=SQLITE_OK ){ |
| 1314 + sqlite3PagerUnref(pNextPage); | 1427 + pageDestroy(pNextPage); |
| 1315 + return rc; | 1428 + return rc; |
| 1316 + } | 1429 + } |
| 1317 + } while( !pCursor->pPage ); | 1430 + } while( !pCursor->pPage ); |
| 1318 + | 1431 + |
| 1319 + return SQLITE_ROW; | 1432 + return SQLITE_ROW; |
| 1320 +} | 1433 +} |
| 1321 + | 1434 + |
| 1322 +static void leafCursorDestroyCellData(RecoverLeafCursor *pCursor){ | 1435 +static void leafCursorDestroyCellData(RecoverLeafCursor *pCursor){ |
| 1323 + if( pCursor->bFreeRecordHeader ){ | 1436 + if( pCursor->bFreeRecordHeader ){ |
| 1324 + sqlite3_free(pCursor->pRecordHeader); | 1437 + sqlite3_free(pCursor->pRecordHeader); |
| 1325 + } | 1438 + } |
| 1326 + pCursor->bFreeRecordHeader = 0; | 1439 + pCursor->bFreeRecordHeader = 0; |
| 1327 + pCursor->pRecordHeader = NULL; | 1440 + pCursor->pRecordHeader = NULL; |
| 1328 + | 1441 + |
| 1329 + if( pCursor->pOverflow ){ | 1442 + if( pCursor->pOverflow ){ |
| 1330 + overflowDestroy(pCursor->pOverflow); | 1443 + overflowDestroy(pCursor->pOverflow); |
| 1331 + pCursor->pOverflow = NULL; | 1444 + pCursor->pOverflow = NULL; |
| 1332 + } | 1445 + } |
| 1333 +} | 1446 +} |
| 1334 + | 1447 + |
| 1335 +static void leafCursorDestroy(RecoverLeafCursor *pCursor){ | 1448 +static void leafCursorDestroy(RecoverLeafCursor *pCursor){ |
| 1336 + leafCursorDestroyCellData(pCursor); | 1449 + leafCursorDestroyCellData(pCursor); |
| 1337 + | 1450 + |
| 1338 + if( pCursor->pParent ){ | 1451 + if( pCursor->pParent ){ |
| 1339 + interiorCursorDestroy(pCursor->pParent); | 1452 + interiorCursorDestroy(pCursor->pParent); |
| 1340 + pCursor->pParent = NULL; | 1453 + pCursor->pParent = NULL; |
| 1341 + } | 1454 + } |
| 1342 + | 1455 + |
| 1343 + if( pCursor->pPage ){ | 1456 + if( pCursor->pPage ){ |
| 1344 + sqlite3PagerUnref(pCursor->pPage); | 1457 + pageDestroy(pCursor->pPage); |
| 1345 + pCursor->pPage = NULL; | 1458 + pCursor->pPage = NULL; |
| 1346 + } | 1459 + } |
| 1347 + | 1460 + |
| 1461 + if( pCursor->pPager ){ |
| 1462 + pagerDestroy(pCursor->pPager); |
| 1463 + pCursor->pPager = NULL; |
| 1464 + } |
| 1465 + |
| 1348 + memset(pCursor, 0xA5, sizeof(*pCursor)); | 1466 + memset(pCursor, 0xA5, sizeof(*pCursor)); |
| 1349 + sqlite3_free(pCursor); | 1467 + sqlite3_free(pCursor); |
| 1350 +} | 1468 +} |
| 1351 + | 1469 + |
| 1352 +/* Create a cursor to iterate the rows from the leaf pages of a table | 1470 +/* Create a cursor to iterate the rows from the leaf pages of a table |
| 1353 + * rooted at iRootPage. | 1471 + * rooted at iRootPage. |
| 1354 + */ | 1472 + */ |
| 1355 +/* TODO(shess): recoverOpen() calls this to setup the cursor, and I | 1473 +/* TODO(shess): recoverOpen() calls this to setup the cursor, and I |
| 1356 + * think that recoverFilter() may make a hard assumption that the | 1474 + * think that recoverFilter() may make a hard assumption that the |
| 1357 + * cursor returned will turn up at least one valid cell. | 1475 + * cursor returned will turn up at least one valid cell. |
| 1358 + * | 1476 + * |
| 1359 + * The cases I can think of which break this assumption are: | 1477 + * The cases I can think of which break this assumption are: |
| 1360 + * - pPage is a valid leaf page with no valid cells. | 1478 + * - pPage is a valid leaf page with no valid cells. |
| 1361 + * - pPage is a valid interior page with no valid leaves. | 1479 + * - pPage is a valid interior page with no valid leaves. |
| 1362 + * - pPage is a valid interior page who's leaves contain no valid cells. | 1480 + * - pPage is a valid interior page who's leaves contain no valid cells. |
| 1363 + * - pPage is not a valid leaf or interior page. | 1481 + * - pPage is not a valid leaf or interior page. |
| 1364 + */ | 1482 + */ |
| 1365 +static int leafCursorCreate(Pager *pPager, unsigned nPageSize, | 1483 +static int leafCursorCreate(RecoverPager *pPager, unsigned nPageSize, |
| 1366 + u32 iRootPage, RecoverLeafCursor **ppCursor){ | 1484 + u32 iRootPage, RecoverLeafCursor **ppCursor){ |
| 1367 + DbPage *pPage; /* Reference to page at iRootPage. */ | 1485 + RecoverPage *pPage; /* Reference to page at iRootPage. */ |
| 1368 + RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */ | 1486 + RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */ |
| 1369 + int rc; | 1487 + int rc; |
| 1370 + | 1488 + |
| 1371 + /* Start out with the root page. */ | 1489 + /* Start out with the root page. */ |
| 1372 + rc = sqlite3PagerGet(pPager, iRootPage, &pPage, 0); | 1490 + rc = pagerGetPage(pPager, iRootPage, &pPage); |
| 1373 + if( rc!=SQLITE_OK ){ | 1491 + if( rc!=SQLITE_OK ){ |
| 1374 + return rc; | 1492 + return rc; |
| 1375 + } | 1493 + } |
| 1376 + | 1494 + |
| 1377 + pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); | 1495 + pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); |
| 1378 + if( !pCursor ){ | 1496 + if( !pCursor ){ |
| 1379 + sqlite3PagerUnref(pPage); | 1497 + pageDestroy(pPage); |
| 1380 + return SQLITE_NOMEM; | 1498 + return SQLITE_NOMEM; |
| 1381 + } | 1499 + } |
| 1382 + memset(pCursor, 0, sizeof(*pCursor)); | 1500 + memset(pCursor, 0, sizeof(*pCursor)); |
| 1383 + | 1501 + |
| 1384 + pCursor->nPageSize = nPageSize; | 1502 + pCursor->nPageSize = nPageSize; |
| 1503 + pCursor->pPager = pPager; |
| 1385 + | 1504 + |
| 1386 + rc = leafCursorLoadPage(pCursor, pPage); | 1505 + rc = leafCursorLoadPage(pCursor, pPage); |
| 1387 + if( rc!=SQLITE_OK ){ | 1506 + if( rc!=SQLITE_OK ){ |
| 1388 + sqlite3PagerUnref(pPage); | 1507 + pageDestroy(pPage); |
| 1389 + leafCursorDestroy(pCursor); | 1508 + leafCursorDestroy(pCursor); |
| 1390 + return rc; | 1509 + return rc; |
| 1391 + } | 1510 + } |
| 1392 + | 1511 + |
| 1393 + /* pPage wasn't a leaf page, find the next leaf page. */ | 1512 + /* pPage wasn't a leaf page, find the next leaf page. */ |
| 1394 + if( !pCursor->pPage ){ | 1513 + if( !pCursor->pPage ){ |
| 1395 + rc = leafCursorNextPage(pCursor); | 1514 + rc = leafCursorNextPage(pCursor); |
| 1396 + if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ){ | 1515 + if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ){ |
| 1397 + leafCursorDestroy(pCursor); | 1516 + leafCursorDestroy(pCursor); |
| 1398 + return rc; | 1517 + return rc; |
| (...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1721 + RecoverLeafCursor *pLeafCursor; | 1840 + RecoverLeafCursor *pLeafCursor; |
| 1722 + int iEncoding; | 1841 + int iEncoding; |
| 1723 + int bEOF; | 1842 + int bEOF; |
| 1724 +}; | 1843 +}; |
| 1725 + | 1844 + |
| 1726 +static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ | 1845 +static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ |
| 1727 + Recover *pRecover = (Recover*)pVTab; | 1846 + Recover *pRecover = (Recover*)pVTab; |
| 1728 + u32 iRootPage; /* Root page of the backing table. */ | 1847 + u32 iRootPage; /* Root page of the backing table. */ |
| 1729 + int iEncoding; /* UTF encoding for backing database. */ | 1848 + int iEncoding; /* UTF encoding for backing database. */ |
| 1730 + unsigned nPageSize; /* Size of pages in backing database. */ | 1849 + unsigned nPageSize; /* Size of pages in backing database. */ |
| 1731 + Pager *pPager; /* Backing database pager. */ | 1850 + RecoverPager *pPager; /* Backing database pager. */ |
| 1732 + RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */ | 1851 + RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */ |
| 1733 + RecoverCursor *pCursor; /* Cursor to read rows from leaves. */ | 1852 + RecoverCursor *pCursor; /* Cursor to read rows from leaves. */ |
| 1734 + int rc; | 1853 + int rc; |
| 1735 + | 1854 + |
| 1736 + FNENTRY(); | 1855 + FNENTRY(); |
| 1737 + | 1856 + |
| 1738 + iRootPage = 0; | 1857 + iRootPage = 0; |
| 1739 + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, | 1858 + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, |
| 1740 + &iRootPage); | 1859 + &iRootPage); |
| 1741 + if( rc!=SQLITE_OK ){ | 1860 + if( rc!=SQLITE_OK ){ |
| 1742 + return rc; | 1861 + return rc; |
| 1743 + } | 1862 + } |
| 1744 + | 1863 + |
| 1745 + iEncoding = 0; | 1864 + rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize, &iEncoding); |
| 1746 + rc = getEncoding(pRecover->db, pRecover->zDb, &iEncoding); | |
| 1747 + if( rc!=SQLITE_OK ){ | |
| 1748 + return rc; | |
| 1749 + } | |
| 1750 + | |
| 1751 + rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize); | |
| 1752 + if( rc!=SQLITE_OK ){ | 1865 + if( rc!=SQLITE_OK ){ |
| 1753 + return rc; | 1866 + return rc; |
| 1754 + } | 1867 + } |
| 1755 + | 1868 + |
| 1756 + rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor); | 1869 + rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor); |
| 1757 + if( rc!=SQLITE_OK ){ | 1870 + if( rc!=SQLITE_OK ){ |
| 1871 + pagerDestroy(pPager); |
| 1758 + return rc; | 1872 + return rc; |
| 1759 + } | 1873 + } |
| 1760 + | 1874 + |
| 1761 + pCursor = sqlite3_malloc(sizeof(RecoverCursor)); | 1875 + pCursor = sqlite3_malloc(sizeof(RecoverCursor)); |
| 1762 + if( !pCursor ){ | 1876 + if( !pCursor ){ |
| 1763 + leafCursorDestroy(pLeafCursor); | 1877 + leafCursorDestroy(pLeafCursor); |
| 1764 + return SQLITE_NOMEM; | 1878 + return SQLITE_NOMEM; |
| 1765 + } | 1879 + } |
| 1766 + memset(pCursor, 0, sizeof(*pCursor)); | 1880 + memset(pCursor, 0, sizeof(*pCursor)); |
| 1767 + pCursor->base.pVtab = pVTab; | 1881 + pCursor->base.pVtab = pVTab; |
| (...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2304 +*/ | 2418 +*/ |
| 2305 +CHROMIUM_SQLITE_API | 2419 +CHROMIUM_SQLITE_API |
| 2306 +int recoverVtableInit(sqlite3 *db); | 2420 +int recoverVtableInit(sqlite3 *db); |
| 2307 +/* End recover virtual table patch for Chromium */ | 2421 +/* End recover virtual table patch for Chromium */ |
| 2308 + | 2422 + |
| 2309 /* Begin WebDatabase patch for Chromium */ | 2423 /* Begin WebDatabase patch for Chromium */ |
| 2310 /* Expose some SQLite internals for the WebDatabase vfs. | 2424 /* Expose some SQLite internals for the WebDatabase vfs. |
| 2311 ** DO NOT EXTEND THE USE OF THIS. | 2425 ** DO NOT EXTEND THE USE OF THIS. |
| 2312 diff --git a/third_party/sqlite/src/test/recover.test b/third_party/sqlite/src/t
est/recover.test | 2426 diff --git a/third_party/sqlite/src/test/recover.test b/third_party/sqlite/src/t
est/recover.test |
| 2313 new file mode 100644 | 2427 new file mode 100644 |
| 2314 index 0000000..b5aa182 | 2428 index 0000000..bfb7888 |
| 2315 --- /dev/null | 2429 --- /dev/null |
| 2316 +++ b/third_party/sqlite/src/test/recover.test | 2430 +++ b/third_party/sqlite/src/test/recover.test |
| 2317 @@ -0,0 +1,147 @@ | 2431 @@ -0,0 +1,164 @@ |
| 2318 +# 2012 January 11 {} | 2432 +# 2012 January 11 {} |
| 2319 +# | 2433 +# |
| 2320 +# The author disclaims copyright to this source code. In place of | 2434 +# The author disclaims copyright to this source code. In place of |
| 2321 +# a legal notice, here is a blessing: | 2435 +# a legal notice, here is a blessing: |
| 2322 +# | 2436 +# |
| 2323 +# May you do good and not evil. | 2437 +# May you do good and not evil. |
| 2324 +# May you find forgiveness for yourself and forgive others. | 2438 +# May you find forgiveness for yourself and forgive others. |
| 2325 +# May you share freely, never taking more than you give. | 2439 +# May you share freely, never taking more than you give. |
| 2326 +# | 2440 +# |
| 2327 +#*********************************************************************** | 2441 +#*********************************************************************** |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2420 + DROP TABLE IF EXISTS temp.leaf_recover; | 2534 + DROP TABLE IF EXISTS temp.leaf_recover; |
| 2421 + CREATE VIRTUAL TABLE temp.leaf_recover USING recover( | 2535 + CREATE VIRTUAL TABLE temp.leaf_recover USING recover( |
| 2422 + leaf, | 2536 + leaf, |
| 2423 + t TEXT, | 2537 + t TEXT, |
| 2424 + n INTEGER | 2538 + n INTEGER |
| 2425 + ); | 2539 + ); |
| 2426 + } | 2540 + } |
| 2427 + execsql {SELECT t, n FROM leaf_recover ORDER BY rowid} | 2541 + execsql {SELECT t, n FROM leaf_recover ORDER BY rowid} |
| 2428 +} {{Leaf-node-generating line 0} 0 {Leaf-node-generating line 1} 1 {Leaf-node-g
enerating 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-g
enerating line 7} 7 {Leaf-node-generating line 8} 8 {Leaf-node-generating line 9
} 9} | 2542 +} {{Leaf-node-generating line 0} 0 {Leaf-node-generating line 1} 1 {Leaf-node-g
enerating 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-g
enerating line 7} 7 {Leaf-node-generating line 8} 8 {Leaf-node-generating line 9
} 9} |
| 2429 + | 2543 + |
| 2544 +# Empty table gives empty results. |
| 2545 +do_test recover-leaf-2.0 { |
| 2546 + db close |
| 2547 + sqlite3 db test.db |
| 2548 + generate "empty" "Leaf-node-generating line " 0 |
| 2549 + |
| 2550 + db eval { |
| 2551 + DROP TABLE IF EXISTS temp.leaf_recover; |
| 2552 + CREATE VIRTUAL TABLE temp.leaf_recover USING recover( |
| 2553 + empty, |
| 2554 + t TEXT, |
| 2555 + n INTEGER |
| 2556 + ); |
| 2557 + } |
| 2558 + execsql {SELECT t, n FROM leaf_recover ORDER BY rowid} |
| 2559 +} {} |
| 2560 + |
| 2430 +# Single level of interior node. | 2561 +# Single level of interior node. |
| 2431 +do_test recover-interior-1.0 { | 2562 +do_test recover-interior-1.0 { |
| 2432 + db close | 2563 + db close |
| 2433 + sqlite3 db test.db | 2564 + sqlite3 db test.db |
| 2434 + generate "interior" "Interior-node-generating line " 100 | 2565 + generate "interior" "Interior-node-generating line " 100 |
| 2435 + | 2566 + |
| 2436 + db eval { | 2567 + db eval { |
| 2437 + DROP TABLE IF EXISTS temp.interior_recover; | 2568 + DROP TABLE IF EXISTS temp.interior_recover; |
| 2438 + CREATE VIRTUAL TABLE temp.interior_recover USING recover( | 2569 + CREATE VIRTUAL TABLE temp.interior_recover USING recover( |
| 2439 + interior, | 2570 + interior, |
| (...skipping 1167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3607 notify.c | 3738 notify.c |
| 3608 | 3739 |
| 3609 + recover.c | 3740 + recover.c |
| 3610 + | 3741 + |
| 3611 fts3.c | 3742 fts3.c |
| 3612 fts3_aux.c | 3743 fts3_aux.c |
| 3613 fts3_expr.c | 3744 fts3_expr.c |
| 3614 -- | 3745 -- |
| 3615 2.7.0 | 3746 2.7.0 |
| 3616 | 3747 |
| OLD | NEW |