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

Side by Side 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, 9 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
OLDNEW
« 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