| | @@ -565,10 +565,11 @@ |
| 565 | 565 | */ |
| 566 | 566 | static void utf8_width_print(FILE *pOut, int w, const char *zUtf){ |
| 567 | 567 | int i; |
| 568 | 568 | int n; |
| 569 | 569 | int aw = w<0 ? -w : w; |
| 570 | + if( zUtf==0 ) zUtf = ""; |
| 570 | 571 | for(i=n=0; zUtf[i]; i++){ |
| 571 | 572 | if( (zUtf[i]&0xc0)!=0x80 ){ |
| 572 | 573 | n++; |
| 573 | 574 | if( n==aw ){ |
| 574 | 575 | do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); |
| | @@ -11402,11 +11403,16 @@ |
| 11402 | 11403 | #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 11403 | 11404 | |
| 11404 | 11405 | /************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 11405 | 11406 | |
| 11406 | 11407 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 11407 | | -/************************* Begin ../ext/misc/dbdata.c ******************/ |
| 11408 | +#define SQLITE_SHELL_HAVE_RECOVER 1 |
| 11409 | +#else |
| 11410 | +#define SQLITE_SHELL_HAVE_RECOVER 0 |
| 11411 | +#endif |
| 11412 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 11413 | +/************************* Begin ../ext/recover/dbdata.c ******************/ |
| 11408 | 11414 | /* |
| 11409 | 11415 | ** 2019-04-17 |
| 11410 | 11416 | ** |
| 11411 | 11417 | ** The author disclaims copyright to this source code. In place of |
| 11412 | 11418 | ** a legal notice, here is a blessing: |
| | @@ -11476,19 +11482,23 @@ |
| 11476 | 11482 | ** ); |
| 11477 | 11483 | ** |
| 11478 | 11484 | ** It contains one entry for each b-tree pointer between a parent and |
| 11479 | 11485 | ** child page in the database. |
| 11480 | 11486 | */ |
| 11487 | + |
| 11481 | 11488 | #if !defined(SQLITEINT_H) |
| 11482 | 11489 | /* #include "sqlite3ext.h" */ |
| 11483 | 11490 | |
| 11484 | 11491 | /* typedef unsigned char u8; */ |
| 11492 | +typedef unsigned int u32; |
| 11485 | 11493 | |
| 11486 | 11494 | #endif |
| 11487 | 11495 | SQLITE_EXTENSION_INIT1 |
| 11488 | 11496 | #include <string.h> |
| 11489 | 11497 | #include <assert.h> |
| 11498 | + |
| 11499 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 11490 | 11500 | |
| 11491 | 11501 | #define DBDATA_PADDING_BYTES 100 |
| 11492 | 11502 | |
| 11493 | 11503 | typedef struct DbdataTable DbdataTable; |
| 11494 | 11504 | typedef struct DbdataCursor DbdataCursor; |
| | @@ -11507,15 +11517,16 @@ |
| 11507 | 11517 | int szDb; |
| 11508 | 11518 | sqlite3_int64 iRowid; |
| 11509 | 11519 | |
| 11510 | 11520 | /* Only for the sqlite_dbdata table */ |
| 11511 | 11521 | u8 *pRec; /* Buffer containing current record */ |
| 11512 | | - int nRec; /* Size of pRec[] in bytes */ |
| 11513 | | - int nHdr; /* Size of header in bytes */ |
| 11522 | + sqlite3_int64 nRec; /* Size of pRec[] in bytes */ |
| 11523 | + sqlite3_int64 nHdr; /* Size of header in bytes */ |
| 11514 | 11524 | int iField; /* Current field number */ |
| 11515 | 11525 | u8 *pHdrPtr; |
| 11516 | 11526 | u8 *pPtr; |
| 11527 | + u32 enc; /* Text encoding */ |
| 11517 | 11528 | |
| 11518 | 11529 | sqlite3_int64 iIntkey; /* Integer key value */ |
| 11519 | 11530 | }; |
| 11520 | 11531 | |
| 11521 | 11532 | /* Table object */ |
| | @@ -11704,18 +11715,18 @@ |
| 11704 | 11715 | } |
| 11705 | 11716 | |
| 11706 | 11717 | /* |
| 11707 | 11718 | ** Utility methods to decode 16 and 32-bit big-endian unsigned integers. |
| 11708 | 11719 | */ |
| 11709 | | -static unsigned int get_uint16(unsigned char *a){ |
| 11720 | +static u32 get_uint16(unsigned char *a){ |
| 11710 | 11721 | return (a[0]<<8)|a[1]; |
| 11711 | 11722 | } |
| 11712 | | -static unsigned int get_uint32(unsigned char *a){ |
| 11713 | | - return ((unsigned int)a[0]<<24) |
| 11714 | | - | ((unsigned int)a[1]<<16) |
| 11715 | | - | ((unsigned int)a[2]<<8) |
| 11716 | | - | ((unsigned int)a[3]); |
| 11723 | +static u32 get_uint32(unsigned char *a){ |
| 11724 | + return ((u32)a[0]<<24) |
| 11725 | + | ((u32)a[1]<<16) |
| 11726 | + | ((u32)a[2]<<8) |
| 11727 | + | ((u32)a[3]); |
| 11717 | 11728 | } |
| 11718 | 11729 | |
| 11719 | 11730 | /* |
| 11720 | 11731 | ** Load page pgno from the database via the sqlite_dbpage virtual table. |
| 11721 | 11732 | ** If successful, set (*ppPage) to point to a buffer containing the page |
| | @@ -11726,57 +11737,72 @@ |
| 11726 | 11737 | ** Or, if an error occurs, set both (*ppPage) and (*pnPage) to 0 and |
| 11727 | 11738 | ** return an SQLite error code. |
| 11728 | 11739 | */ |
| 11729 | 11740 | static int dbdataLoadPage( |
| 11730 | 11741 | DbdataCursor *pCsr, /* Cursor object */ |
| 11731 | | - unsigned int pgno, /* Page number of page to load */ |
| 11742 | + u32 pgno, /* Page number of page to load */ |
| 11732 | 11743 | u8 **ppPage, /* OUT: pointer to page buffer */ |
| 11733 | 11744 | int *pnPage /* OUT: Size of (*ppPage) in bytes */ |
| 11734 | 11745 | ){ |
| 11735 | 11746 | int rc2; |
| 11736 | 11747 | int rc = SQLITE_OK; |
| 11737 | 11748 | sqlite3_stmt *pStmt = pCsr->pStmt; |
| 11738 | 11749 | |
| 11739 | 11750 | *ppPage = 0; |
| 11740 | 11751 | *pnPage = 0; |
| 11741 | | - sqlite3_bind_int64(pStmt, 2, pgno); |
| 11742 | | - if( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 11743 | | - int nCopy = sqlite3_column_bytes(pStmt, 0); |
| 11744 | | - if( nCopy>0 ){ |
| 11745 | | - u8 *pPage; |
| 11746 | | - pPage = (u8*)sqlite3_malloc64(nCopy + DBDATA_PADDING_BYTES); |
| 11747 | | - if( pPage==0 ){ |
| 11748 | | - rc = SQLITE_NOMEM; |
| 11749 | | - }else{ |
| 11750 | | - const u8 *pCopy = sqlite3_column_blob(pStmt, 0); |
| 11751 | | - memcpy(pPage, pCopy, nCopy); |
| 11752 | | - memset(&pPage[nCopy], 0, DBDATA_PADDING_BYTES); |
| 11753 | | - } |
| 11754 | | - *ppPage = pPage; |
| 11755 | | - *pnPage = nCopy; |
| 11756 | | - } |
| 11757 | | - } |
| 11758 | | - rc2 = sqlite3_reset(pStmt); |
| 11759 | | - if( rc==SQLITE_OK ) rc = rc2; |
| 11752 | + if( pgno>0 ){ |
| 11753 | + sqlite3_bind_int64(pStmt, 2, pgno); |
| 11754 | + if( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 11755 | + int nCopy = sqlite3_column_bytes(pStmt, 0); |
| 11756 | + if( nCopy>0 ){ |
| 11757 | + u8 *pPage; |
| 11758 | + pPage = (u8*)sqlite3_malloc64(nCopy + DBDATA_PADDING_BYTES); |
| 11759 | + if( pPage==0 ){ |
| 11760 | + rc = SQLITE_NOMEM; |
| 11761 | + }else{ |
| 11762 | + const u8 *pCopy = sqlite3_column_blob(pStmt, 0); |
| 11763 | + memcpy(pPage, pCopy, nCopy); |
| 11764 | + memset(&pPage[nCopy], 0, DBDATA_PADDING_BYTES); |
| 11765 | + } |
| 11766 | + *ppPage = pPage; |
| 11767 | + *pnPage = nCopy; |
| 11768 | + } |
| 11769 | + } |
| 11770 | + rc2 = sqlite3_reset(pStmt); |
| 11771 | + if( rc==SQLITE_OK ) rc = rc2; |
| 11772 | + } |
| 11760 | 11773 | |
| 11761 | 11774 | return rc; |
| 11762 | 11775 | } |
| 11763 | 11776 | |
| 11764 | 11777 | /* |
| 11765 | 11778 | ** Read a varint. Put the value in *pVal and return the number of bytes. |
| 11766 | 11779 | */ |
| 11767 | 11780 | static int dbdataGetVarint(const u8 *z, sqlite3_int64 *pVal){ |
| 11768 | | - sqlite3_int64 v = 0; |
| 11781 | + sqlite3_uint64 u = 0; |
| 11769 | 11782 | int i; |
| 11770 | 11783 | for(i=0; i<8; i++){ |
| 11771 | | - v = (v<<7) + (z[i]&0x7f); |
| 11772 | | - if( (z[i]&0x80)==0 ){ *pVal = v; return i+1; } |
| 11784 | + u = (u<<7) + (z[i]&0x7f); |
| 11785 | + if( (z[i]&0x80)==0 ){ *pVal = (sqlite3_int64)u; return i+1; } |
| 11773 | 11786 | } |
| 11774 | | - v = (v<<8) + (z[i]&0xff); |
| 11775 | | - *pVal = v; |
| 11787 | + u = (u<<8) + (z[i]&0xff); |
| 11788 | + *pVal = (sqlite3_int64)u; |
| 11776 | 11789 | return 9; |
| 11777 | 11790 | } |
| 11791 | + |
| 11792 | +/* |
| 11793 | +** Like dbdataGetVarint(), but set the output to 0 if it is less than 0 |
| 11794 | +** or greater than 0xFFFFFFFF. This can be used for all varints in an |
| 11795 | +** SQLite database except for key values in intkey tables. |
| 11796 | +*/ |
| 11797 | +static int dbdataGetVarintU32(const u8 *z, sqlite3_int64 *pVal){ |
| 11798 | + sqlite3_int64 val; |
| 11799 | + int nRet = dbdataGetVarint(z, &val); |
| 11800 | + if( val<0 || val>0xFFFFFFFF ) val = 0; |
| 11801 | + *pVal = val; |
| 11802 | + return nRet; |
| 11803 | +} |
| 11778 | 11804 | |
| 11779 | 11805 | /* |
| 11780 | 11806 | ** Return the number of bytes of space used by an SQLite value of type |
| 11781 | 11807 | ** eType. |
| 11782 | 11808 | */ |
| | @@ -11810,10 +11836,11 @@ |
| 11810 | 11836 | ** Load a value of type eType from buffer pData and use it to set the |
| 11811 | 11837 | ** result of context object pCtx. |
| 11812 | 11838 | */ |
| 11813 | 11839 | static void dbdataValue( |
| 11814 | 11840 | sqlite3_context *pCtx, |
| 11841 | + u32 enc, |
| 11815 | 11842 | int eType, |
| 11816 | 11843 | u8 *pData, |
| 11817 | 11844 | int nData |
| 11818 | 11845 | ){ |
| 11819 | 11846 | if( eType>=0 && dbdataValueBytes(eType)<=nData ){ |
| | @@ -11854,11 +11881,23 @@ |
| 11854 | 11881 | } |
| 11855 | 11882 | |
| 11856 | 11883 | default: { |
| 11857 | 11884 | int n = ((eType-12) / 2); |
| 11858 | 11885 | if( eType % 2 ){ |
| 11859 | | - sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT); |
| 11886 | + switch( enc ){ |
| 11887 | +#ifndef SQLITE_OMIT_UTF16 |
| 11888 | + case SQLITE_UTF16BE: |
| 11889 | + sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT); |
| 11890 | + break; |
| 11891 | + case SQLITE_UTF16LE: |
| 11892 | + sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT); |
| 11893 | + break; |
| 11894 | +#endif |
| 11895 | + default: |
| 11896 | + sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT); |
| 11897 | + break; |
| 11898 | + } |
| 11860 | 11899 | }else{ |
| 11861 | 11900 | sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT); |
| 11862 | 11901 | } |
| 11863 | 11902 | } |
| 11864 | 11903 | } |
| | @@ -11882,10 +11921,11 @@ |
| 11882 | 11921 | while( 1 ){ |
| 11883 | 11922 | if( pCsr->bOnePage==0 && pCsr->iPgno>pCsr->szDb ) return SQLITE_OK; |
| 11884 | 11923 | rc = dbdataLoadPage(pCsr, pCsr->iPgno, &pCsr->aPage, &pCsr->nPage); |
| 11885 | 11924 | if( rc!=SQLITE_OK ) return rc; |
| 11886 | 11925 | if( pCsr->aPage ) break; |
| 11926 | + if( pCsr->bOnePage ) return SQLITE_OK; |
| 11887 | 11927 | pCsr->iPgno++; |
| 11888 | 11928 | } |
| 11889 | 11929 | pCsr->iCell = pTab->bPtr ? -2 : 0; |
| 11890 | 11930 | pCsr->nCell = get_uint16(&pCsr->aPage[iOff+3]); |
| 11891 | 11931 | } |
| | @@ -11945,11 +11985,11 @@ |
| 11945 | 11985 | |
| 11946 | 11986 | /* Load the "byte of payload including overflow" field */ |
| 11947 | 11987 | if( bNextPage || iOff>pCsr->nPage ){ |
| 11948 | 11988 | bNextPage = 1; |
| 11949 | 11989 | }else{ |
| 11950 | | - iOff += dbdataGetVarint(&pCsr->aPage[iOff], &nPayload); |
| 11990 | + iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload); |
| 11951 | 11991 | } |
| 11952 | 11992 | |
| 11953 | 11993 | /* If this is a leaf intkey cell, load the rowid */ |
| 11954 | 11994 | if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){ |
| 11955 | 11995 | iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey); |
| | @@ -11992,11 +12032,11 @@ |
| 11992 | 12032 | iOff += nLocal; |
| 11993 | 12033 | |
| 11994 | 12034 | /* Load content from overflow pages */ |
| 11995 | 12035 | if( nPayload>nLocal ){ |
| 11996 | 12036 | sqlite3_int64 nRem = nPayload - nLocal; |
| 11997 | | - unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); |
| 12037 | + u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]); |
| 11998 | 12038 | while( nRem>0 ){ |
| 11999 | 12039 | u8 *aOvfl = 0; |
| 12000 | 12040 | int nOvfl = 0; |
| 12001 | 12041 | int nCopy; |
| 12002 | 12042 | rc = dbdataLoadPage(pCsr, pgnoOvfl, &aOvfl, &nOvfl); |
| | @@ -12012,11 +12052,12 @@ |
| 12012 | 12052 | pgnoOvfl = get_uint32(aOvfl); |
| 12013 | 12053 | sqlite3_free(aOvfl); |
| 12014 | 12054 | } |
| 12015 | 12055 | } |
| 12016 | 12056 | |
| 12017 | | - iHdr = dbdataGetVarint(pCsr->pRec, &nHdr); |
| 12057 | + iHdr = dbdataGetVarintU32(pCsr->pRec, &nHdr); |
| 12058 | + if( nHdr>nPayload ) nHdr = 0; |
| 12018 | 12059 | pCsr->nHdr = nHdr; |
| 12019 | 12060 | pCsr->pHdrPtr = &pCsr->pRec[iHdr]; |
| 12020 | 12061 | pCsr->pPtr = &pCsr->pRec[pCsr->nHdr]; |
| 12021 | 12062 | pCsr->iField = (bHasRowid ? -1 : 0); |
| 12022 | 12063 | } |
| | @@ -12026,11 +12067,11 @@ |
| 12026 | 12067 | if( pCsr->iField>0 ){ |
| 12027 | 12068 | sqlite3_int64 iType; |
| 12028 | 12069 | if( pCsr->pHdrPtr>&pCsr->pRec[pCsr->nRec] ){ |
| 12029 | 12070 | bNextPage = 1; |
| 12030 | 12071 | }else{ |
| 12031 | | - pCsr->pHdrPtr += dbdataGetVarint(pCsr->pHdrPtr, &iType); |
| 12072 | + pCsr->pHdrPtr += dbdataGetVarintU32(pCsr->pHdrPtr, &iType); |
| 12032 | 12073 | pCsr->pPtr += dbdataValueBytes(iType); |
| 12033 | 12074 | } |
| 12034 | 12075 | } |
| 12035 | 12076 | } |
| 12036 | 12077 | |
| | @@ -12064,10 +12105,22 @@ |
| 12064 | 12105 | */ |
| 12065 | 12106 | static int dbdataEof(sqlite3_vtab_cursor *pCursor){ |
| 12066 | 12107 | DbdataCursor *pCsr = (DbdataCursor*)pCursor; |
| 12067 | 12108 | return pCsr->aPage==0; |
| 12068 | 12109 | } |
| 12110 | + |
| 12111 | +/* |
| 12112 | +** Return true if nul-terminated string zSchema ends in "()". Or false |
| 12113 | +** otherwise. |
| 12114 | +*/ |
| 12115 | +static int dbdataIsFunction(const char *zSchema){ |
| 12116 | + size_t n = strlen(zSchema); |
| 12117 | + if( n>2 && zSchema[n-2]=='(' && zSchema[n-1]==')' ){ |
| 12118 | + return (int)n-2; |
| 12119 | + } |
| 12120 | + return 0; |
| 12121 | +} |
| 12069 | 12122 | |
| 12070 | 12123 | /* |
| 12071 | 12124 | ** Determine the size in pages of database zSchema (where zSchema is |
| 12072 | 12125 | ** "main", "temp" or the name of an attached database) and set |
| 12073 | 12126 | ** pCsr->szDb accordingly. If successful, return SQLITE_OK. Otherwise, |
| | @@ -12075,23 +12128,48 @@ |
| 12075 | 12128 | */ |
| 12076 | 12129 | static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){ |
| 12077 | 12130 | DbdataTable *pTab = (DbdataTable*)pCsr->base.pVtab; |
| 12078 | 12131 | char *zSql = 0; |
| 12079 | 12132 | int rc, rc2; |
| 12133 | + int nFunc = 0; |
| 12080 | 12134 | sqlite3_stmt *pStmt = 0; |
| 12081 | 12135 | |
| 12082 | | - zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); |
| 12136 | + if( (nFunc = dbdataIsFunction(zSchema))>0 ){ |
| 12137 | + zSql = sqlite3_mprintf("SELECT %.*s(0)", nFunc, zSchema); |
| 12138 | + }else{ |
| 12139 | + zSql = sqlite3_mprintf("PRAGMA %Q.page_count", zSchema); |
| 12140 | + } |
| 12083 | 12141 | if( zSql==0 ) return SQLITE_NOMEM; |
| 12142 | + |
| 12084 | 12143 | rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0); |
| 12085 | 12144 | sqlite3_free(zSql); |
| 12086 | 12145 | if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 12087 | 12146 | pCsr->szDb = sqlite3_column_int(pStmt, 0); |
| 12088 | 12147 | } |
| 12089 | 12148 | rc2 = sqlite3_finalize(pStmt); |
| 12090 | 12149 | if( rc==SQLITE_OK ) rc = rc2; |
| 12091 | 12150 | return rc; |
| 12092 | 12151 | } |
| 12152 | + |
| 12153 | +/* |
| 12154 | +** Attempt to figure out the encoding of the database by retrieving page 1 |
| 12155 | +** and inspecting the header field. If successful, set the pCsr->enc variable |
| 12156 | +** and return SQLITE_OK. Otherwise, return an SQLite error code. |
| 12157 | +*/ |
| 12158 | +static int dbdataGetEncoding(DbdataCursor *pCsr){ |
| 12159 | + int rc = SQLITE_OK; |
| 12160 | + int nPg1 = 0; |
| 12161 | + u8 *aPg1 = 0; |
| 12162 | + rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1); |
| 12163 | + assert( rc!=SQLITE_OK || nPg1==0 || nPg1>=512 ); |
| 12164 | + if( rc==SQLITE_OK && nPg1>0 ){ |
| 12165 | + pCsr->enc = get_uint32(&aPg1[56]); |
| 12166 | + } |
| 12167 | + sqlite3_free(aPg1); |
| 12168 | + return rc; |
| 12169 | +} |
| 12170 | + |
| 12093 | 12171 | |
| 12094 | 12172 | /* |
| 12095 | 12173 | ** xFilter method for sqlite_dbdata and sqlite_dbptr. |
| 12096 | 12174 | */ |
| 12097 | 12175 | static int dbdataFilter( |
| | @@ -12106,23 +12184,32 @@ |
| 12106 | 12184 | |
| 12107 | 12185 | dbdataResetCursor(pCsr); |
| 12108 | 12186 | assert( pCsr->iPgno==1 ); |
| 12109 | 12187 | if( idxNum & 0x01 ){ |
| 12110 | 12188 | zSchema = (const char*)sqlite3_value_text(argv[0]); |
| 12189 | + if( zSchema==0 ) zSchema = ""; |
| 12111 | 12190 | } |
| 12112 | 12191 | if( idxNum & 0x02 ){ |
| 12113 | 12192 | pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]); |
| 12114 | 12193 | pCsr->bOnePage = 1; |
| 12115 | 12194 | }else{ |
| 12116 | | - pCsr->nPage = dbdataDbsize(pCsr, zSchema); |
| 12117 | 12195 | rc = dbdataDbsize(pCsr, zSchema); |
| 12118 | 12196 | } |
| 12119 | 12197 | |
| 12120 | 12198 | if( rc==SQLITE_OK ){ |
| 12199 | + int nFunc = 0; |
| 12121 | 12200 | if( pTab->pStmt ){ |
| 12122 | 12201 | pCsr->pStmt = pTab->pStmt; |
| 12123 | 12202 | pTab->pStmt = 0; |
| 12203 | + }else if( (nFunc = dbdataIsFunction(zSchema))>0 ){ |
| 12204 | + char *zSql = sqlite3_mprintf("SELECT %.*s(?2)", nFunc, zSchema); |
| 12205 | + if( zSql==0 ){ |
| 12206 | + rc = SQLITE_NOMEM; |
| 12207 | + }else{ |
| 12208 | + rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0); |
| 12209 | + sqlite3_free(zSql); |
| 12210 | + } |
| 12124 | 12211 | }else{ |
| 12125 | 12212 | rc = sqlite3_prepare_v2(pTab->db, |
| 12126 | 12213 | "SELECT data FROM sqlite_dbpage(?) WHERE pgno=?", -1, |
| 12127 | 12214 | &pCsr->pStmt, 0 |
| 12128 | 12215 | ); |
| | @@ -12131,17 +12218,24 @@ |
| 12131 | 12218 | if( rc==SQLITE_OK ){ |
| 12132 | 12219 | rc = sqlite3_bind_text(pCsr->pStmt, 1, zSchema, -1, SQLITE_TRANSIENT); |
| 12133 | 12220 | }else{ |
| 12134 | 12221 | pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db)); |
| 12135 | 12222 | } |
| 12223 | + |
| 12224 | + /* Try to determine the encoding of the db by inspecting the header |
| 12225 | + ** field on page 1. */ |
| 12226 | + if( rc==SQLITE_OK ){ |
| 12227 | + rc = dbdataGetEncoding(pCsr); |
| 12228 | + } |
| 12229 | + |
| 12136 | 12230 | if( rc==SQLITE_OK ){ |
| 12137 | 12231 | rc = dbdataNext(pCursor); |
| 12138 | 12232 | } |
| 12139 | 12233 | return rc; |
| 12140 | 12234 | } |
| 12141 | 12235 | |
| 12142 | | -/* |
| 12236 | +/* |
| 12143 | 12237 | ** Return a column for the sqlite_dbdata or sqlite_dbptr table. |
| 12144 | 12238 | */ |
| 12145 | 12239 | static int dbdataColumn( |
| 12146 | 12240 | sqlite3_vtab_cursor *pCursor, |
| 12147 | 12241 | sqlite3_context *ctx, |
| | @@ -12183,13 +12277,14 @@ |
| 12183 | 12277 | case DBDATA_COLUMN_VALUE: { |
| 12184 | 12278 | if( pCsr->iField<0 ){ |
| 12185 | 12279 | sqlite3_result_int64(ctx, pCsr->iIntkey); |
| 12186 | 12280 | }else{ |
| 12187 | 12281 | sqlite3_int64 iType; |
| 12188 | | - dbdataGetVarint(pCsr->pHdrPtr, &iType); |
| 12282 | + dbdataGetVarintU32(pCsr->pHdrPtr, &iType); |
| 12189 | 12283 | dbdataValue( |
| 12190 | | - ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr |
| 12284 | + ctx, pCsr->enc, iType, pCsr->pPtr, |
| 12285 | + &pCsr->pRec[pCsr->nRec] - pCsr->pPtr |
| 12191 | 12286 | ); |
| 12192 | 12287 | } |
| 12193 | 12288 | break; |
| 12194 | 12289 | } |
| 12195 | 12290 | } |
| | @@ -12255,11 +12350,3119 @@ |
| 12255 | 12350 | ){ |
| 12256 | 12351 | SQLITE_EXTENSION_INIT2(pApi); |
| 12257 | 12352 | return sqlite3DbdataRegister(db); |
| 12258 | 12353 | } |
| 12259 | 12354 | |
| 12260 | | -/************************* End ../ext/misc/dbdata.c ********************/ |
| 12355 | +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 12356 | + |
| 12357 | +/************************* End ../ext/recover/dbdata.c ********************/ |
| 12358 | +/************************* Begin ../ext/recover/sqlite3recover.h ******************/ |
| 12359 | +/* |
| 12360 | +** 2022-08-27 |
| 12361 | +** |
| 12362 | +** The author disclaims copyright to this source code. In place of |
| 12363 | +** a legal notice, here is a blessing: |
| 12364 | +** |
| 12365 | +** May you do good and not evil. |
| 12366 | +** May you find forgiveness for yourself and forgive others. |
| 12367 | +** May you share freely, never taking more than you give. |
| 12368 | +** |
| 12369 | +************************************************************************* |
| 12370 | +** |
| 12371 | +** This file contains the public interface to the "recover" extension - |
| 12372 | +** an SQLite extension designed to recover data from corrupted database |
| 12373 | +** files. |
| 12374 | +*/ |
| 12375 | + |
| 12376 | +/* |
| 12377 | +** OVERVIEW: |
| 12378 | +** |
| 12379 | +** To use the API to recover data from a corrupted database, an |
| 12380 | +** application: |
| 12381 | +** |
| 12382 | +** 1) Creates an sqlite3_recover handle by calling either |
| 12383 | +** sqlite3_recover_init() or sqlite3_recover_init_sql(). |
| 12384 | +** |
| 12385 | +** 2) Configures the new handle using one or more calls to |
| 12386 | +** sqlite3_recover_config(). |
| 12387 | +** |
| 12388 | +** 3) Executes the recovery by repeatedly calling sqlite3_recover_step() on |
| 12389 | +** the handle until it returns something other than SQLITE_OK. If it |
| 12390 | +** returns SQLITE_DONE, then the recovery operation completed without |
| 12391 | +** error. If it returns some other non-SQLITE_OK value, then an error |
| 12392 | +** has occurred. |
| 12393 | +** |
| 12394 | +** 4) Retrieves any error code and English language error message using the |
| 12395 | +** sqlite3_recover_errcode() and sqlite3_recover_errmsg() APIs, |
| 12396 | +** respectively. |
| 12397 | +** |
| 12398 | +** 5) Destroys the sqlite3_recover handle and frees all resources |
| 12399 | +** using sqlite3_recover_finish(). |
| 12400 | +** |
| 12401 | +** The application may abandon the recovery operation at any point |
| 12402 | +** before it is finished by passing the sqlite3_recover handle to |
| 12403 | +** sqlite3_recover_finish(). This is not an error, but the final state |
| 12404 | +** of the output database, or the results of running the partial script |
| 12405 | +** delivered to the SQL callback, are undefined. |
| 12406 | +*/ |
| 12407 | + |
| 12408 | +#ifndef _SQLITE_RECOVER_H |
| 12409 | +#define _SQLITE_RECOVER_H |
| 12410 | + |
| 12411 | +/* #include "sqlite3.h" */ |
| 12412 | + |
| 12413 | +#ifdef __cplusplus |
| 12414 | +extern "C" { |
| 12415 | +#endif |
| 12416 | + |
| 12417 | +/* |
| 12418 | +** An instance of the sqlite3_recover object represents a recovery |
| 12419 | +** operation in progress. |
| 12420 | +** |
| 12421 | +** Constructors: |
| 12422 | +** |
| 12423 | +** sqlite3_recover_init() |
| 12424 | +** sqlite3_recover_init_sql() |
| 12425 | +** |
| 12426 | +** Destructor: |
| 12427 | +** |
| 12428 | +** sqlite3_recover_finish() |
| 12429 | +** |
| 12430 | +** Methods: |
| 12431 | +** |
| 12432 | +** sqlite3_recover_config() |
| 12433 | +** sqlite3_recover_errcode() |
| 12434 | +** sqlite3_recover_errmsg() |
| 12435 | +** sqlite3_recover_run() |
| 12436 | +** sqlite3_recover_step() |
| 12437 | +*/ |
| 12438 | +typedef struct sqlite3_recover sqlite3_recover; |
| 12439 | + |
| 12440 | +/* |
| 12441 | +** These two APIs attempt to create and return a new sqlite3_recover object. |
| 12442 | +** In both cases the first two arguments identify the (possibly |
| 12443 | +** corrupt) database to recover data from. The first argument is an open |
| 12444 | +** database handle and the second the name of a database attached to that |
| 12445 | +** handle (i.e. "main", "temp" or the name of an attached database). |
| 12446 | +** |
| 12447 | +** If sqlite3_recover_init() is used to create the new sqlite3_recover |
| 12448 | +** handle, then data is recovered into a new database, identified by |
| 12449 | +** string parameter zUri. zUri may be an absolute or relative file path, |
| 12450 | +** or may be an SQLite URI. If the identified database file already exists, |
| 12451 | +** it is overwritten. |
| 12452 | +** |
| 12453 | +** If sqlite3_recover_init_sql() is invoked, then any recovered data will |
| 12454 | +** be returned to the user as a series of SQL statements. Executing these |
| 12455 | +** SQL statements results in the same database as would have been created |
| 12456 | +** had sqlite3_recover_init() been used. For each SQL statement in the |
| 12457 | +** output, the callback function passed as the third argument (xSql) is |
| 12458 | +** invoked once. The first parameter is a passed a copy of the fourth argument |
| 12459 | +** to this function (pCtx) as its first parameter, and a pointer to a |
| 12460 | +** nul-terminated buffer containing the SQL statement formated as UTF-8 as |
| 12461 | +** the second. If the xSql callback returns any value other than SQLITE_OK, |
| 12462 | +** then processing is immediately abandoned and the value returned used as |
| 12463 | +** the recover handle error code (see below). |
| 12464 | +** |
| 12465 | +** If an out-of-memory error occurs, NULL may be returned instead of |
| 12466 | +** a valid handle. In all other cases, it is the responsibility of the |
| 12467 | +** application to avoid resource leaks by ensuring that |
| 12468 | +** sqlite3_recover_finish() is called on all allocated handles. |
| 12469 | +*/ |
| 12470 | +sqlite3_recover *sqlite3_recover_init( |
| 12471 | + sqlite3* db, |
| 12472 | + const char *zDb, |
| 12473 | + const char *zUri |
| 12474 | +); |
| 12475 | +sqlite3_recover *sqlite3_recover_init_sql( |
| 12476 | + sqlite3* db, |
| 12477 | + const char *zDb, |
| 12478 | + int (*xSql)(void*, const char*), |
| 12479 | + void *pCtx |
| 12480 | +); |
| 12481 | + |
| 12482 | +/* |
| 12483 | +** Configure an sqlite3_recover object that has just been created using |
| 12484 | +** sqlite3_recover_init() or sqlite3_recover_init_sql(). This function |
| 12485 | +** may only be called before the first call to sqlite3_recover_step() |
| 12486 | +** or sqlite3_recover_run() on the object. |
| 12487 | +** |
| 12488 | +** The second argument passed to this function must be one of the |
| 12489 | +** SQLITE_RECOVER_* symbols defined below. Valid values for the third argument |
| 12490 | +** depend on the specific SQLITE_RECOVER_* symbol in use. |
| 12491 | +** |
| 12492 | +** SQLITE_OK is returned if the configuration operation was successful, |
| 12493 | +** or an SQLite error code otherwise. |
| 12494 | +*/ |
| 12495 | +int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); |
| 12496 | + |
| 12497 | +/* |
| 12498 | +** SQLITE_RECOVER_LOST_AND_FOUND: |
| 12499 | +** The pArg argument points to a string buffer containing the name |
| 12500 | +** of a "lost-and-found" table in the output database, or NULL. If |
| 12501 | +** the argument is non-NULL and the database contains seemingly |
| 12502 | +** valid pages that cannot be associated with any table in the |
| 12503 | +** recovered part of the schema, data is extracted from these |
| 12504 | +** pages to add to the lost-and-found table. |
| 12505 | +** |
| 12506 | +** SQLITE_RECOVER_FREELIST_CORRUPT: |
| 12507 | +** The pArg value must actually be a pointer to a value of type |
| 12508 | +** int containing value 0 or 1 cast as a (void*). If this option is set |
| 12509 | +** (argument is 1) and a lost-and-found table has been configured using |
| 12510 | +** SQLITE_RECOVER_LOST_AND_FOUND, then is assumed that the freelist is |
| 12511 | +** corrupt and an attempt is made to recover records from pages that |
| 12512 | +** appear to be linked into the freelist. Otherwise, pages on the freelist |
| 12513 | +** are ignored. Setting this option can recover more data from the |
| 12514 | +** database, but often ends up "recovering" deleted records. The default |
| 12515 | +** value is 0 (clear). |
| 12516 | +** |
| 12517 | +** SQLITE_RECOVER_ROWIDS: |
| 12518 | +** The pArg value must actually be a pointer to a value of type |
| 12519 | +** int containing value 0 or 1 cast as a (void*). If this option is set |
| 12520 | +** (argument is 1), then an attempt is made to recover rowid values |
| 12521 | +** that are not also INTEGER PRIMARY KEY values. If this option is |
| 12522 | +** clear, then new rowids are assigned to all recovered rows. The |
| 12523 | +** default value is 1 (set). |
| 12524 | +** |
| 12525 | +** SQLITE_RECOVER_SLOWINDEXES: |
| 12526 | +** The pArg value must actually be a pointer to a value of type |
| 12527 | +** int containing value 0 or 1 cast as a (void*). If this option is clear |
| 12528 | +** (argument is 0), then when creating an output database, the recover |
| 12529 | +** module creates and populates non-UNIQUE indexes right at the end of the |
| 12530 | +** recovery operation - after all recoverable data has been inserted |
| 12531 | +** into the new database. This is faster overall, but means that the |
| 12532 | +** final call to sqlite3_recover_step() for a recovery operation may |
| 12533 | +** be need to create a large number of indexes, which may be very slow. |
| 12534 | +** |
| 12535 | +** Or, if this option is set (argument is 1), then non-UNIQUE indexes |
| 12536 | +** are created in the output database before it is populated with |
| 12537 | +** recovered data. This is slower overall, but avoids the slow call |
| 12538 | +** to sqlite3_recover_step() at the end of the recovery operation. |
| 12539 | +** |
| 12540 | +** The default option value is 0. |
| 12541 | +*/ |
| 12542 | +#define SQLITE_RECOVER_LOST_AND_FOUND 1 |
| 12543 | +#define SQLITE_RECOVER_FREELIST_CORRUPT 2 |
| 12544 | +#define SQLITE_RECOVER_ROWIDS 3 |
| 12545 | +#define SQLITE_RECOVER_SLOWINDEXES 4 |
| 12546 | + |
| 12547 | +/* |
| 12548 | +** Perform a unit of work towards the recovery operation. This function |
| 12549 | +** must normally be called multiple times to complete database recovery. |
| 12550 | +** |
| 12551 | +** If no error occurs but the recovery operation is not completed, this |
| 12552 | +** function returns SQLITE_OK. If recovery has been completed successfully |
| 12553 | +** then SQLITE_DONE is returned. If an error has occurred, then an SQLite |
| 12554 | +** error code (e.g. SQLITE_IOERR or SQLITE_NOMEM) is returned. It is not |
| 12555 | +** considered an error if some or all of the data cannot be recovered |
| 12556 | +** due to database corruption. |
| 12557 | +** |
| 12558 | +** Once sqlite3_recover_step() has returned a value other than SQLITE_OK, |
| 12559 | +** all further such calls on the same recover handle are no-ops that return |
| 12560 | +** the same non-SQLITE_OK value. |
| 12561 | +*/ |
| 12562 | +int sqlite3_recover_step(sqlite3_recover*); |
| 12563 | + |
| 12564 | +/* |
| 12565 | +** Run the recovery operation to completion. Return SQLITE_OK if successful, |
| 12566 | +** or an SQLite error code otherwise. Calling this function is the same |
| 12567 | +** as executing: |
| 12568 | +** |
| 12569 | +** while( SQLITE_OK==sqlite3_recover_step(p) ); |
| 12570 | +** return sqlite3_recover_errcode(p); |
| 12571 | +*/ |
| 12572 | +int sqlite3_recover_run(sqlite3_recover*); |
| 12573 | + |
| 12574 | +/* |
| 12575 | +** If an error has been encountered during a prior call to |
| 12576 | +** sqlite3_recover_step(), then this function attempts to return a |
| 12577 | +** pointer to a buffer containing an English language explanation of |
| 12578 | +** the error. If no error message is available, or if an out-of memory |
| 12579 | +** error occurs while attempting to allocate a buffer in which to format |
| 12580 | +** the error message, NULL is returned. |
| 12581 | +** |
| 12582 | +** The returned buffer remains valid until the sqlite3_recover handle is |
| 12583 | +** destroyed using sqlite3_recover_finish(). |
| 12584 | +*/ |
| 12585 | +const char *sqlite3_recover_errmsg(sqlite3_recover*); |
| 12586 | + |
| 12587 | +/* |
| 12588 | +** If this function is called on an sqlite3_recover handle after |
| 12589 | +** an error occurs, an SQLite error code is returned. Otherwise, SQLITE_OK. |
| 12590 | +*/ |
| 12591 | +int sqlite3_recover_errcode(sqlite3_recover*); |
| 12592 | + |
| 12593 | +/* |
| 12594 | +** Clean up a recovery object created by a call to sqlite3_recover_init(). |
| 12595 | +** The results of using a recovery object with any API after it has been |
| 12596 | +** passed to this function are undefined. |
| 12597 | +** |
| 12598 | +** This function returns the same value as sqlite3_recover_errcode(). |
| 12599 | +*/ |
| 12600 | +int sqlite3_recover_finish(sqlite3_recover*); |
| 12601 | + |
| 12602 | + |
| 12603 | +#ifdef __cplusplus |
| 12604 | +} /* end of the 'extern "C"' block */ |
| 12605 | +#endif |
| 12606 | + |
| 12607 | +#endif /* ifndef _SQLITE_RECOVER_H */ |
| 12608 | + |
| 12609 | +/************************* End ../ext/recover/sqlite3recover.h ********************/ |
| 12610 | +/************************* Begin ../ext/recover/sqlite3recover.c ******************/ |
| 12611 | +/* |
| 12612 | +** 2022-08-27 |
| 12613 | +** |
| 12614 | +** The author disclaims copyright to this source code. In place of |
| 12615 | +** a legal notice, here is a blessing: |
| 12616 | +** |
| 12617 | +** May you do good and not evil. |
| 12618 | +** May you find forgiveness for yourself and forgive others. |
| 12619 | +** May you share freely, never taking more than you give. |
| 12620 | +** |
| 12621 | +************************************************************************* |
| 12622 | +** |
| 12623 | +*/ |
| 12624 | + |
| 12625 | + |
| 12626 | +/* #include "sqlite3recover.h" */ |
| 12627 | +#include <assert.h> |
| 12628 | +#include <string.h> |
| 12629 | + |
| 12630 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 12631 | + |
| 12632 | +/* |
| 12633 | +** Declaration for public API function in file dbdata.c. This may be called |
| 12634 | +** with NULL as the final two arguments to register the sqlite_dbptr and |
| 12635 | +** sqlite_dbdata virtual tables with a database handle. |
| 12636 | +*/ |
| 12637 | +#ifdef _WIN32 |
| 12638 | + |
| 12639 | +#endif |
| 12640 | +int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); |
| 12641 | + |
| 12642 | +/* typedef unsigned int u32; */ |
| 12643 | +/* typedef unsigned char u8; */ |
| 12644 | +/* typedef sqlite3_int64 i64; */ |
| 12645 | + |
| 12646 | +typedef struct RecoverTable RecoverTable; |
| 12647 | +typedef struct RecoverColumn RecoverColumn; |
| 12648 | + |
| 12649 | +/* |
| 12650 | +** When recovering rows of data that can be associated with table |
| 12651 | +** definitions recovered from the sqlite_schema table, each table is |
| 12652 | +** represented by an instance of the following object. |
| 12653 | +** |
| 12654 | +** iRoot: |
| 12655 | +** The root page in the original database. Not necessarily (and usually |
| 12656 | +** not) the same in the recovered database. |
| 12657 | +** |
| 12658 | +** zTab: |
| 12659 | +** Name of the table. |
| 12660 | +** |
| 12661 | +** nCol/aCol[]: |
| 12662 | +** aCol[] is an array of nCol columns. In the order in which they appear |
| 12663 | +** in the table. |
| 12664 | +** |
| 12665 | +** bIntkey: |
| 12666 | +** Set to true for intkey tables, false for WITHOUT ROWID. |
| 12667 | +** |
| 12668 | +** iRowidBind: |
| 12669 | +** Each column in the aCol[] array has associated with it the index of |
| 12670 | +** the bind parameter its values will be bound to in the INSERT statement |
| 12671 | +** used to construct the output database. If the table does has a rowid |
| 12672 | +** but not an INTEGER PRIMARY KEY column, then iRowidBind contains the |
| 12673 | +** index of the bind paramater to which the rowid value should be bound. |
| 12674 | +** Otherwise, it contains -1. If the table does contain an INTEGER PRIMARY |
| 12675 | +** KEY column, then the rowid value should be bound to the index associated |
| 12676 | +** with the column. |
| 12677 | +** |
| 12678 | +** pNext: |
| 12679 | +** All RecoverTable objects used by the recovery operation are allocated |
| 12680 | +** and populated as part of creating the recovered database schema in |
| 12681 | +** the output database, before any non-schema data are recovered. They |
| 12682 | +** are then stored in a singly-linked list linked by this variable beginning |
| 12683 | +** at sqlite3_recover.pTblList. |
| 12684 | +*/ |
| 12685 | +struct RecoverTable { |
| 12686 | + u32 iRoot; /* Root page in original database */ |
| 12687 | + char *zTab; /* Name of table */ |
| 12688 | + int nCol; /* Number of columns in table */ |
| 12689 | + RecoverColumn *aCol; /* Array of columns */ |
| 12690 | + int bIntkey; /* True for intkey, false for without rowid */ |
| 12691 | + int iRowidBind; /* If >0, bind rowid to INSERT here */ |
| 12692 | + RecoverTable *pNext; |
| 12693 | +}; |
| 12694 | + |
| 12695 | +/* |
| 12696 | +** Each database column is represented by an instance of the following object |
| 12697 | +** stored in the RecoverTable.aCol[] array of the associated table. |
| 12698 | +** |
| 12699 | +** iField: |
| 12700 | +** The index of the associated field within database records. Or -1 if |
| 12701 | +** there is no associated field (e.g. for virtual generated columns). |
| 12702 | +** |
| 12703 | +** iBind: |
| 12704 | +** The bind index of the INSERT statement to bind this columns values |
| 12705 | +** to. Or 0 if there is no such index (iff (iField<0)). |
| 12706 | +** |
| 12707 | +** bIPK: |
| 12708 | +** True if this is the INTEGER PRIMARY KEY column. |
| 12709 | +** |
| 12710 | +** zCol: |
| 12711 | +** Name of column. |
| 12712 | +** |
| 12713 | +** eHidden: |
| 12714 | +** A RECOVER_EHIDDEN_* constant value (see below for interpretation of each). |
| 12715 | +*/ |
| 12716 | +struct RecoverColumn { |
| 12717 | + int iField; /* Field in record on disk */ |
| 12718 | + int iBind; /* Binding to use in INSERT */ |
| 12719 | + int bIPK; /* True for IPK column */ |
| 12720 | + char *zCol; |
| 12721 | + int eHidden; |
| 12722 | +}; |
| 12723 | + |
| 12724 | +#define RECOVER_EHIDDEN_NONE 0 /* Normal database column */ |
| 12725 | +#define RECOVER_EHIDDEN_HIDDEN 1 /* Column is __HIDDEN__ */ |
| 12726 | +#define RECOVER_EHIDDEN_VIRTUAL 2 /* Virtual generated column */ |
| 12727 | +#define RECOVER_EHIDDEN_STORED 3 /* Stored generated column */ |
| 12728 | + |
| 12729 | +/* |
| 12730 | +** Bitmap object used to track pages in the input database. Allocated |
| 12731 | +** and manipulated only by the following functions: |
| 12732 | +** |
| 12733 | +** recoverBitmapAlloc() |
| 12734 | +** recoverBitmapFree() |
| 12735 | +** recoverBitmapSet() |
| 12736 | +** recoverBitmapQuery() |
| 12737 | +** |
| 12738 | +** nPg: |
| 12739 | +** Largest page number that may be stored in the bitmap. The range |
| 12740 | +** of valid keys is 1 to nPg, inclusive. |
| 12741 | +** |
| 12742 | +** aElem[]: |
| 12743 | +** Array large enough to contain a bit for each key. For key value |
| 12744 | +** iKey, the associated bit is the bit (iKey%32) of aElem[iKey/32]. |
| 12745 | +** In other words, the following is true if bit iKey is set, or |
| 12746 | +** false if it is clear: |
| 12747 | +** |
| 12748 | +** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 12749 | +*/ |
| 12750 | +typedef struct RecoverBitmap RecoverBitmap; |
| 12751 | +struct RecoverBitmap { |
| 12752 | + i64 nPg; /* Size of bitmap */ |
| 12753 | + u32 aElem[1]; /* Array of 32-bit bitmasks */ |
| 12754 | +}; |
| 12755 | + |
| 12756 | +/* |
| 12757 | +** State variables (part of the sqlite3_recover structure) used while |
| 12758 | +** recovering data for tables identified in the recovered schema (state |
| 12759 | +** RECOVER_STATE_WRITING). |
| 12760 | +*/ |
| 12761 | +typedef struct RecoverStateW1 RecoverStateW1; |
| 12762 | +struct RecoverStateW1 { |
| 12763 | + sqlite3_stmt *pTbls; |
| 12764 | + sqlite3_stmt *pSel; |
| 12765 | + sqlite3_stmt *pInsert; |
| 12766 | + int nInsert; |
| 12767 | + |
| 12768 | + RecoverTable *pTab; /* Table currently being written */ |
| 12769 | + int nMax; /* Max column count in any schema table */ |
| 12770 | + sqlite3_value **apVal; /* Array of nMax values */ |
| 12771 | + int nVal; /* Number of valid entries in apVal[] */ |
| 12772 | + int bHaveRowid; |
| 12773 | + i64 iRowid; |
| 12774 | + i64 iPrevPage; |
| 12775 | + int iPrevCell; |
| 12776 | +}; |
| 12777 | + |
| 12778 | +/* |
| 12779 | +** State variables (part of the sqlite3_recover structure) used while |
| 12780 | +** recovering data destined for the lost and found table (states |
| 12781 | +** RECOVER_STATE_LOSTANDFOUND[123]). |
| 12782 | +*/ |
| 12783 | +typedef struct RecoverStateLAF RecoverStateLAF; |
| 12784 | +struct RecoverStateLAF { |
| 12785 | + RecoverBitmap *pUsed; |
| 12786 | + i64 nPg; /* Size of db in pages */ |
| 12787 | + sqlite3_stmt *pAllAndParent; |
| 12788 | + sqlite3_stmt *pMapInsert; |
| 12789 | + sqlite3_stmt *pMaxField; |
| 12790 | + sqlite3_stmt *pUsedPages; |
| 12791 | + sqlite3_stmt *pFindRoot; |
| 12792 | + sqlite3_stmt *pInsert; /* INSERT INTO lost_and_found ... */ |
| 12793 | + sqlite3_stmt *pAllPage; |
| 12794 | + sqlite3_stmt *pPageData; |
| 12795 | + sqlite3_value **apVal; |
| 12796 | + int nMaxField; |
| 12797 | +}; |
| 12798 | + |
| 12799 | +/* |
| 12800 | +** Main recover handle structure. |
| 12801 | +*/ |
| 12802 | +struct sqlite3_recover { |
| 12803 | + /* Copies of sqlite3_recover_init[_sql]() parameters */ |
| 12804 | + sqlite3 *dbIn; /* Input database */ |
| 12805 | + char *zDb; /* Name of input db ("main" etc.) */ |
| 12806 | + char *zUri; /* URI for output database */ |
| 12807 | + void *pSqlCtx; /* SQL callback context */ |
| 12808 | + int (*xSql)(void*,const char*); /* Pointer to SQL callback function */ |
| 12809 | + |
| 12810 | + /* Values configured by sqlite3_recover_config() */ |
| 12811 | + char *zStateDb; /* State database to use (or NULL) */ |
| 12812 | + char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ |
| 12813 | + int bFreelistCorrupt; /* SQLITE_RECOVER_FREELIST_CORRUPT setting */ |
| 12814 | + int bRecoverRowid; /* SQLITE_RECOVER_ROWIDS setting */ |
| 12815 | + int bSlowIndexes; /* SQLITE_RECOVER_SLOWINDEXES setting */ |
| 12816 | + |
| 12817 | + int pgsz; |
| 12818 | + int detected_pgsz; |
| 12819 | + int nReserve; |
| 12820 | + u8 *pPage1Disk; |
| 12821 | + u8 *pPage1Cache; |
| 12822 | + |
| 12823 | + /* Error code and error message */ |
| 12824 | + int errCode; /* For sqlite3_recover_errcode() */ |
| 12825 | + char *zErrMsg; /* For sqlite3_recover_errmsg() */ |
| 12826 | + |
| 12827 | + int eState; |
| 12828 | + int bCloseTransaction; |
| 12829 | + |
| 12830 | + /* Variables used with eState==RECOVER_STATE_WRITING */ |
| 12831 | + RecoverStateW1 w1; |
| 12832 | + |
| 12833 | + /* Variables used with states RECOVER_STATE_LOSTANDFOUND[123] */ |
| 12834 | + RecoverStateLAF laf; |
| 12835 | + |
| 12836 | + /* Fields used within sqlite3_recover_run() */ |
| 12837 | + sqlite3 *dbOut; /* Output database */ |
| 12838 | + sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ |
| 12839 | + RecoverTable *pTblList; /* List of tables recovered from schema */ |
| 12840 | +}; |
| 12841 | + |
| 12842 | +/* |
| 12843 | +** The various states in which an sqlite3_recover object may exist: |
| 12844 | +** |
| 12845 | +** RECOVER_STATE_INIT: |
| 12846 | +** The object is initially created in this state. sqlite3_recover_step() |
| 12847 | +** has yet to be called. This is the only state in which it is permitted |
| 12848 | +** to call sqlite3_recover_config(). |
| 12849 | +** |
| 12850 | +** RECOVER_STATE_WRITING: |
| 12851 | +** |
| 12852 | +** RECOVER_STATE_LOSTANDFOUND1: |
| 12853 | +** State to populate the bitmap of pages used by other tables or the |
| 12854 | +** database freelist. |
| 12855 | +** |
| 12856 | +** RECOVER_STATE_LOSTANDFOUND2: |
| 12857 | +** Populate the recovery.map table - used to figure out a "root" page |
| 12858 | +** for each lost page from in the database from which records are |
| 12859 | +** extracted. |
| 12860 | +** |
| 12861 | +** RECOVER_STATE_LOSTANDFOUND3: |
| 12862 | +** Populate the lost-and-found table itself. |
| 12863 | +*/ |
| 12864 | +#define RECOVER_STATE_INIT 0 |
| 12865 | +#define RECOVER_STATE_WRITING 1 |
| 12866 | +#define RECOVER_STATE_LOSTANDFOUND1 2 |
| 12867 | +#define RECOVER_STATE_LOSTANDFOUND2 3 |
| 12868 | +#define RECOVER_STATE_LOSTANDFOUND3 4 |
| 12869 | +#define RECOVER_STATE_SCHEMA2 5 |
| 12870 | +#define RECOVER_STATE_DONE 6 |
| 12871 | + |
| 12872 | + |
| 12873 | +/* |
| 12874 | +** Global variables used by this extension. |
| 12875 | +*/ |
| 12876 | +typedef struct RecoverGlobal RecoverGlobal; |
| 12877 | +struct RecoverGlobal { |
| 12878 | + const sqlite3_io_methods *pMethods; |
| 12879 | + sqlite3_recover *p; |
| 12880 | +}; |
| 12881 | +static RecoverGlobal recover_g; |
| 12882 | + |
| 12883 | +/* |
| 12884 | +** Use this static SQLite mutex to protect the globals during the |
| 12885 | +** first call to sqlite3_recover_step(). |
| 12886 | +*/ |
| 12887 | +#define RECOVER_MUTEX_ID SQLITE_MUTEX_STATIC_APP2 |
| 12888 | + |
| 12889 | + |
| 12890 | +/* |
| 12891 | +** Default value for SQLITE_RECOVER_ROWIDS (sqlite3_recover.bRecoverRowid). |
| 12892 | +*/ |
| 12893 | +#define RECOVER_ROWID_DEFAULT 1 |
| 12894 | + |
| 12895 | +#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 |
| 12896 | +# define recoverEnterMutex() |
| 12897 | +# define recoverLeaveMutex() |
| 12898 | +# define recoverAssertMutexHeld() |
| 12899 | +#else |
| 12900 | +static void recoverEnterMutex(void){ |
| 12901 | + sqlite3_mutex_enter(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)); |
| 12902 | +} |
| 12903 | +static void recoverLeaveMutex(void){ |
| 12904 | + sqlite3_mutex_leave(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)); |
| 12905 | +} |
| 12906 | +static void recoverAssertMutexHeld(void){ |
| 12907 | + assert( sqlite3_mutex_held(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)) ); |
| 12908 | +} |
| 12909 | +#endif |
| 12910 | + |
| 12911 | + |
| 12912 | +/* |
| 12913 | +** Like strlen(). But handles NULL pointer arguments. |
| 12914 | +*/ |
| 12915 | +static int recoverStrlen(const char *zStr){ |
| 12916 | + int nRet = 0; |
| 12917 | + if( zStr ){ |
| 12918 | + while( zStr[nRet] ) nRet++; |
| 12919 | + } |
| 12920 | + return nRet; |
| 12921 | +} |
| 12922 | + |
| 12923 | +/* |
| 12924 | +** This function is a no-op if the recover handle passed as the first |
| 12925 | +** argument already contains an error (if p->errCode!=SQLITE_OK). |
| 12926 | +** |
| 12927 | +** Otherwise, an attempt is made to allocate, zero and return a buffer nByte |
| 12928 | +** bytes in size. If successful, a pointer to the new buffer is returned. Or, |
| 12929 | +** if an OOM error occurs, NULL is returned and the handle error code |
| 12930 | +** (p->errCode) set to SQLITE_NOMEM. |
| 12931 | +*/ |
| 12932 | +static void *recoverMalloc(sqlite3_recover *p, i64 nByte){ |
| 12933 | + void *pRet = 0; |
| 12934 | + assert( nByte>0 ); |
| 12935 | + if( p->errCode==SQLITE_OK ){ |
| 12936 | + pRet = sqlite3_malloc64(nByte); |
| 12937 | + if( pRet ){ |
| 12938 | + memset(pRet, 0, nByte); |
| 12939 | + }else{ |
| 12940 | + p->errCode = SQLITE_NOMEM; |
| 12941 | + } |
| 12942 | + } |
| 12943 | + return pRet; |
| 12944 | +} |
| 12945 | + |
| 12946 | +/* |
| 12947 | +** Set the error code and error message for the recover handle passed as |
| 12948 | +** the first argument. The error code is set to the value of parameter |
| 12949 | +** errCode. |
| 12950 | +** |
| 12951 | +** Parameter zFmt must be a printf() style formatting string. The handle |
| 12952 | +** error message is set to the result of using any trailing arguments for |
| 12953 | +** parameter substitutions in the formatting string. |
| 12954 | +** |
| 12955 | +** For example: |
| 12956 | +** |
| 12957 | +** recoverError(p, SQLITE_ERROR, "no such table: %s", zTablename); |
| 12958 | +*/ |
| 12959 | +static int recoverError( |
| 12960 | + sqlite3_recover *p, |
| 12961 | + int errCode, |
| 12962 | + const char *zFmt, ... |
| 12963 | +){ |
| 12964 | + char *z = 0; |
| 12965 | + va_list ap; |
| 12966 | + va_start(ap, zFmt); |
| 12967 | + if( zFmt ){ |
| 12968 | + z = sqlite3_vmprintf(zFmt, ap); |
| 12969 | + va_end(ap); |
| 12970 | + } |
| 12971 | + sqlite3_free(p->zErrMsg); |
| 12972 | + p->zErrMsg = z; |
| 12973 | + p->errCode = errCode; |
| 12974 | + return errCode; |
| 12975 | +} |
| 12976 | + |
| 12977 | + |
| 12978 | +/* |
| 12979 | +** This function is a no-op if p->errCode is initially other than SQLITE_OK. |
| 12980 | +** In this case it returns NULL. |
| 12981 | +** |
| 12982 | +** Otherwise, an attempt is made to allocate and return a bitmap object |
| 12983 | +** large enough to store a bit for all page numbers between 1 and nPg, |
| 12984 | +** inclusive. The bitmap is initially zeroed. |
| 12985 | +*/ |
| 12986 | +static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 12987 | + int nElem = (nPg+1+31) / 32; |
| 12988 | + int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); |
| 12989 | + RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 12990 | + |
| 12991 | + if( pRet ){ |
| 12992 | + pRet->nPg = nPg; |
| 12993 | + } |
| 12994 | + return pRet; |
| 12995 | +} |
| 12996 | + |
| 12997 | +/* |
| 12998 | +** Free a bitmap object allocated by recoverBitmapAlloc(). |
| 12999 | +*/ |
| 13000 | +static void recoverBitmapFree(RecoverBitmap *pMap){ |
| 13001 | + sqlite3_free(pMap); |
| 13002 | +} |
| 13003 | + |
| 13004 | +/* |
| 13005 | +** Set the bit associated with page iPg in bitvec pMap. |
| 13006 | +*/ |
| 13007 | +static void recoverBitmapSet(RecoverBitmap *pMap, i64 iPg){ |
| 13008 | + if( iPg<=pMap->nPg ){ |
| 13009 | + int iElem = (iPg / 32); |
| 13010 | + int iBit = (iPg % 32); |
| 13011 | + pMap->aElem[iElem] |= (((u32)1) << iBit); |
| 13012 | + } |
| 13013 | +} |
| 13014 | + |
| 13015 | +/* |
| 13016 | +** Query bitmap object pMap for the state of the bit associated with page |
| 13017 | +** iPg. Return 1 if it is set, or 0 otherwise. |
| 13018 | +*/ |
| 13019 | +static int recoverBitmapQuery(RecoverBitmap *pMap, i64 iPg){ |
| 13020 | + int ret = 1; |
| 13021 | + if( iPg<=pMap->nPg && iPg>0 ){ |
| 13022 | + int iElem = (iPg / 32); |
| 13023 | + int iBit = (iPg % 32); |
| 13024 | + ret = (pMap->aElem[iElem] & (((u32)1) << iBit)) ? 1 : 0; |
| 13025 | + } |
| 13026 | + return ret; |
| 13027 | +} |
| 13028 | + |
| 13029 | +/* |
| 13030 | +** Set the recover handle error to the error code and message returned by |
| 13031 | +** calling sqlite3_errcode() and sqlite3_errmsg(), respectively, on database |
| 13032 | +** handle db. |
| 13033 | +*/ |
| 13034 | +static int recoverDbError(sqlite3_recover *p, sqlite3 *db){ |
| 13035 | + return recoverError(p, sqlite3_errcode(db), "%s", sqlite3_errmsg(db)); |
| 13036 | +} |
| 13037 | + |
| 13038 | +/* |
| 13039 | +** This function is a no-op if recover handle p already contains an error |
| 13040 | +** (if p->errCode!=SQLITE_OK). |
| 13041 | +** |
| 13042 | +** Otherwise, it attempts to prepare the SQL statement in zSql against |
| 13043 | +** database handle db. If successful, the statement handle is returned. |
| 13044 | +** Or, if an error occurs, NULL is returned and an error left in the |
| 13045 | +** recover handle. |
| 13046 | +*/ |
| 13047 | +static sqlite3_stmt *recoverPrepare( |
| 13048 | + sqlite3_recover *p, |
| 13049 | + sqlite3 *db, |
| 13050 | + const char *zSql |
| 13051 | +){ |
| 13052 | + sqlite3_stmt *pStmt = 0; |
| 13053 | + if( p->errCode==SQLITE_OK ){ |
| 13054 | + if( sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) ){ |
| 13055 | + recoverDbError(p, db); |
| 13056 | + } |
| 13057 | + } |
| 13058 | + return pStmt; |
| 13059 | +} |
| 13060 | + |
| 13061 | +/* |
| 13062 | +** This function is a no-op if recover handle p already contains an error |
| 13063 | +** (if p->errCode!=SQLITE_OK). |
| 13064 | +** |
| 13065 | +** Otherwise, argument zFmt is used as a printf() style format string, |
| 13066 | +** along with any trailing arguments, to create an SQL statement. This |
| 13067 | +** SQL statement is prepared against database handle db and, if successful, |
| 13068 | +** the statment handle returned. Or, if an error occurs - either during |
| 13069 | +** the printf() formatting or when preparing the resulting SQL - an |
| 13070 | +** error code and message are left in the recover handle. |
| 13071 | +*/ |
| 13072 | +static sqlite3_stmt *recoverPreparePrintf( |
| 13073 | + sqlite3_recover *p, |
| 13074 | + sqlite3 *db, |
| 13075 | + const char *zFmt, ... |
| 13076 | +){ |
| 13077 | + sqlite3_stmt *pStmt = 0; |
| 13078 | + if( p->errCode==SQLITE_OK ){ |
| 13079 | + va_list ap; |
| 13080 | + char *z; |
| 13081 | + va_start(ap, zFmt); |
| 13082 | + z = sqlite3_vmprintf(zFmt, ap); |
| 13083 | + va_end(ap); |
| 13084 | + if( z==0 ){ |
| 13085 | + p->errCode = SQLITE_NOMEM; |
| 13086 | + }else{ |
| 13087 | + pStmt = recoverPrepare(p, db, z); |
| 13088 | + sqlite3_free(z); |
| 13089 | + } |
| 13090 | + } |
| 13091 | + return pStmt; |
| 13092 | +} |
| 13093 | + |
| 13094 | +/* |
| 13095 | +** Reset SQLite statement handle pStmt. If the call to sqlite3_reset() |
| 13096 | +** indicates that an error occurred, and there is not already an error |
| 13097 | +** in the recover handle passed as the first argument, set the error |
| 13098 | +** code and error message appropriately. |
| 13099 | +** |
| 13100 | +** This function returns a copy of the statement handle pointer passed |
| 13101 | +** as the second argument. |
| 13102 | +*/ |
| 13103 | +static sqlite3_stmt *recoverReset(sqlite3_recover *p, sqlite3_stmt *pStmt){ |
| 13104 | + int rc = sqlite3_reset(pStmt); |
| 13105 | + if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT && p->errCode==SQLITE_OK ){ |
| 13106 | + recoverDbError(p, sqlite3_db_handle(pStmt)); |
| 13107 | + } |
| 13108 | + return pStmt; |
| 13109 | +} |
| 13110 | + |
| 13111 | +/* |
| 13112 | +** Finalize SQLite statement handle pStmt. If the call to sqlite3_reset() |
| 13113 | +** indicates that an error occurred, and there is not already an error |
| 13114 | +** in the recover handle passed as the first argument, set the error |
| 13115 | +** code and error message appropriately. |
| 13116 | +*/ |
| 13117 | +static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){ |
| 13118 | + sqlite3 *db = sqlite3_db_handle(pStmt); |
| 13119 | + int rc = sqlite3_finalize(pStmt); |
| 13120 | + if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){ |
| 13121 | + recoverDbError(p, db); |
| 13122 | + } |
| 13123 | +} |
| 13124 | + |
| 13125 | +/* |
| 13126 | +** This function is a no-op if recover handle p already contains an error |
| 13127 | +** (if p->errCode!=SQLITE_OK). A copy of p->errCode is returned in this |
| 13128 | +** case. |
| 13129 | +** |
| 13130 | +** Otherwise, execute SQL script zSql. If successful, return SQLITE_OK. |
| 13131 | +** Or, if an error occurs, leave an error code and message in the recover |
| 13132 | +** handle and return a copy of the error code. |
| 13133 | +*/ |
| 13134 | +static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ |
| 13135 | + if( p->errCode==SQLITE_OK ){ |
| 13136 | + int rc = sqlite3_exec(db, zSql, 0, 0, 0); |
| 13137 | + if( rc ){ |
| 13138 | + recoverDbError(p, db); |
| 13139 | + } |
| 13140 | + } |
| 13141 | + return p->errCode; |
| 13142 | +} |
| 13143 | + |
| 13144 | +/* |
| 13145 | +** Bind the value pVal to parameter iBind of statement pStmt. Leave an |
| 13146 | +** error in the recover handle passed as the first argument if an error |
| 13147 | +** (e.g. an OOM) occurs. |
| 13148 | +*/ |
| 13149 | +static void recoverBindValue( |
| 13150 | + sqlite3_recover *p, |
| 13151 | + sqlite3_stmt *pStmt, |
| 13152 | + int iBind, |
| 13153 | + sqlite3_value *pVal |
| 13154 | +){ |
| 13155 | + if( p->errCode==SQLITE_OK ){ |
| 13156 | + int rc = sqlite3_bind_value(pStmt, iBind, pVal); |
| 13157 | + if( rc ) recoverError(p, rc, 0); |
| 13158 | + } |
| 13159 | +} |
| 13160 | + |
| 13161 | +/* |
| 13162 | +** This function is a no-op if recover handle p already contains an error |
| 13163 | +** (if p->errCode!=SQLITE_OK). NULL is returned in this case. |
| 13164 | +** |
| 13165 | +** Otherwise, an attempt is made to interpret zFmt as a printf() style |
| 13166 | +** formatting string and the result of using the trailing arguments for |
| 13167 | +** parameter substitution with it written into a buffer obtained from |
| 13168 | +** sqlite3_malloc(). If successful, a pointer to the buffer is returned. |
| 13169 | +** It is the responsibility of the caller to eventually free the buffer |
| 13170 | +** using sqlite3_free(). |
| 13171 | +** |
| 13172 | +** Or, if an error occurs, an error code and message is left in the recover |
| 13173 | +** handle and NULL returned. |
| 13174 | +*/ |
| 13175 | +static char *recoverMPrintf(sqlite3_recover *p, const char *zFmt, ...){ |
| 13176 | + va_list ap; |
| 13177 | + char *z; |
| 13178 | + va_start(ap, zFmt); |
| 13179 | + z = sqlite3_vmprintf(zFmt, ap); |
| 13180 | + va_end(ap); |
| 13181 | + if( p->errCode==SQLITE_OK ){ |
| 13182 | + if( z==0 ) p->errCode = SQLITE_NOMEM; |
| 13183 | + }else{ |
| 13184 | + sqlite3_free(z); |
| 13185 | + z = 0; |
| 13186 | + } |
| 13187 | + return z; |
| 13188 | +} |
| 13189 | + |
| 13190 | +/* |
| 13191 | +** This function is a no-op if recover handle p already contains an error |
| 13192 | +** (if p->errCode!=SQLITE_OK). Zero is returned in this case. |
| 13193 | +** |
| 13194 | +** Otherwise, execute "PRAGMA page_count" against the input database. If |
| 13195 | +** successful, return the integer result. Or, if an error occurs, leave an |
| 13196 | +** error code and error message in the sqlite3_recover handle and return |
| 13197 | +** zero. |
| 13198 | +*/ |
| 13199 | +static i64 recoverPageCount(sqlite3_recover *p){ |
| 13200 | + i64 nPg = 0; |
| 13201 | + if( p->errCode==SQLITE_OK ){ |
| 13202 | + sqlite3_stmt *pStmt = 0; |
| 13203 | + pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); |
| 13204 | + if( pStmt ){ |
| 13205 | + sqlite3_step(pStmt); |
| 13206 | + nPg = sqlite3_column_int64(pStmt, 0); |
| 13207 | + } |
| 13208 | + recoverFinalize(p, pStmt); |
| 13209 | + } |
| 13210 | + return nPg; |
| 13211 | +} |
| 13212 | + |
| 13213 | +/* |
| 13214 | +** Implementation of SQL scalar function "read_i32". The first argument to |
| 13215 | +** this function must be a blob. The second a non-negative integer. This |
| 13216 | +** function reads and returns a 32-bit big-endian integer from byte |
| 13217 | +** offset (4*<arg2>) of the blob. |
| 13218 | +** |
| 13219 | +** SELECT read_i32(<blob>, <idx>) |
| 13220 | +*/ |
| 13221 | +static void recoverReadI32( |
| 13222 | + sqlite3_context *context, |
| 13223 | + int argc, |
| 13224 | + sqlite3_value **argv |
| 13225 | +){ |
| 13226 | + const unsigned char *pBlob; |
| 13227 | + int nBlob; |
| 13228 | + int iInt; |
| 13229 | + |
| 13230 | + assert( argc==2 ); |
| 13231 | + nBlob = sqlite3_value_bytes(argv[0]); |
| 13232 | + pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); |
| 13233 | + iInt = sqlite3_value_int(argv[1]) & 0xFFFF; |
| 13234 | + |
| 13235 | + if( (iInt+1)*4<=nBlob ){ |
| 13236 | + const unsigned char *a = &pBlob[iInt*4]; |
| 13237 | + i64 iVal = ((i64)a[0]<<24) |
| 13238 | + + ((i64)a[1]<<16) |
| 13239 | + + ((i64)a[2]<< 8) |
| 13240 | + + ((i64)a[3]<< 0); |
| 13241 | + sqlite3_result_int64(context, iVal); |
| 13242 | + } |
| 13243 | +} |
| 13244 | + |
| 13245 | +/* |
| 13246 | +** Implementation of SQL scalar function "page_is_used". This function |
| 13247 | +** is used as part of the procedure for locating orphan rows for the |
| 13248 | +** lost-and-found table, and it depends on those routines having populated |
| 13249 | +** the sqlite3_recover.laf.pUsed variable. |
| 13250 | +** |
| 13251 | +** The only argument to this function is a page-number. It returns true |
| 13252 | +** if the page has already been used somehow during data recovery, or false |
| 13253 | +** otherwise. |
| 13254 | +** |
| 13255 | +** SELECT page_is_used(<pgno>); |
| 13256 | +*/ |
| 13257 | +static void recoverPageIsUsed( |
| 13258 | + sqlite3_context *pCtx, |
| 13259 | + int nArg, |
| 13260 | + sqlite3_value **apArg |
| 13261 | +){ |
| 13262 | + sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); |
| 13263 | + i64 pgno = sqlite3_value_int64(apArg[0]); |
| 13264 | + assert( nArg==1 ); |
| 13265 | + sqlite3_result_int(pCtx, recoverBitmapQuery(p->laf.pUsed, pgno)); |
| 13266 | +} |
| 13267 | + |
| 13268 | +/* |
| 13269 | +** The implementation of a user-defined SQL function invoked by the |
| 13270 | +** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages |
| 13271 | +** of the database being recovered. |
| 13272 | +** |
| 13273 | +** This function always takes a single integer argument. If the argument |
| 13274 | +** is zero, then the value returned is the number of pages in the db being |
| 13275 | +** recovered. If the argument is greater than zero, it is a page number. |
| 13276 | +** The value returned in this case is an SQL blob containing the data for |
| 13277 | +** the identified page of the db being recovered. e.g. |
| 13278 | +** |
| 13279 | +** SELECT getpage(0); -- return number of pages in db |
| 13280 | +** SELECT getpage(4); -- return page 4 of db as a blob of data |
| 13281 | +*/ |
| 13282 | +static void recoverGetPage( |
| 13283 | + sqlite3_context *pCtx, |
| 13284 | + int nArg, |
| 13285 | + sqlite3_value **apArg |
| 13286 | +){ |
| 13287 | + sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); |
| 13288 | + i64 pgno = sqlite3_value_int64(apArg[0]); |
| 13289 | + sqlite3_stmt *pStmt = 0; |
| 13290 | + |
| 13291 | + assert( nArg==1 ); |
| 13292 | + if( pgno==0 ){ |
| 13293 | + i64 nPg = recoverPageCount(p); |
| 13294 | + sqlite3_result_int64(pCtx, nPg); |
| 13295 | + return; |
| 13296 | + }else{ |
| 13297 | + if( p->pGetPage==0 ){ |
| 13298 | + pStmt = p->pGetPage = recoverPreparePrintf( |
| 13299 | + p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb |
| 13300 | + ); |
| 13301 | + }else if( p->errCode==SQLITE_OK ){ |
| 13302 | + pStmt = p->pGetPage; |
| 13303 | + } |
| 13304 | + |
| 13305 | + if( pStmt ){ |
| 13306 | + sqlite3_bind_int64(pStmt, 1, pgno); |
| 13307 | + if( SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 13308 | + const u8 *aPg; |
| 13309 | + int nPg; |
| 13310 | + assert( p->errCode==SQLITE_OK ); |
| 13311 | + aPg = sqlite3_column_blob(pStmt, 0); |
| 13312 | + nPg = sqlite3_column_bytes(pStmt, 0); |
| 13313 | + if( pgno==1 && nPg==p->pgsz && 0==memcmp(p->pPage1Cache, aPg, nPg) ){ |
| 13314 | + aPg = p->pPage1Disk; |
| 13315 | + } |
| 13316 | + sqlite3_result_blob(pCtx, aPg, nPg-p->nReserve, SQLITE_TRANSIENT); |
| 13317 | + } |
| 13318 | + recoverReset(p, pStmt); |
| 13319 | + } |
| 13320 | + } |
| 13321 | + |
| 13322 | + if( p->errCode ){ |
| 13323 | + if( p->zErrMsg ) sqlite3_result_error(pCtx, p->zErrMsg, -1); |
| 13324 | + sqlite3_result_error_code(pCtx, p->errCode); |
| 13325 | + } |
| 13326 | +} |
| 13327 | + |
| 13328 | +/* |
| 13329 | +** Find a string that is not found anywhere in z[]. Return a pointer |
| 13330 | +** to that string. |
| 13331 | +** |
| 13332 | +** Try to use zA and zB first. If both of those are already found in z[] |
| 13333 | +** then make up some string and store it in the buffer zBuf. |
| 13334 | +*/ |
| 13335 | +static const char *recoverUnusedString( |
| 13336 | + const char *z, /* Result must not appear anywhere in z */ |
| 13337 | + const char *zA, const char *zB, /* Try these first */ |
| 13338 | + char *zBuf /* Space to store a generated string */ |
| 13339 | +){ |
| 13340 | + unsigned i = 0; |
| 13341 | + if( strstr(z, zA)==0 ) return zA; |
| 13342 | + if( strstr(z, zB)==0 ) return zB; |
| 13343 | + do{ |
| 13344 | + sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); |
| 13345 | + }while( strstr(z,zBuf)!=0 ); |
| 13346 | + return zBuf; |
| 13347 | +} |
| 13348 | + |
| 13349 | +/* |
| 13350 | +** Implementation of scalar SQL function "escape_crnl". The argument passed to |
| 13351 | +** this function is the output of built-in function quote(). If the first |
| 13352 | +** character of the input is "'", indicating that the value passed to quote() |
| 13353 | +** was a text value, then this function searches the input for "\n" and "\r" |
| 13354 | +** characters and adds a wrapper similar to the following: |
| 13355 | +** |
| 13356 | +** replace(replace(<input>, '\n', char(10), '\r', char(13)); |
| 13357 | +** |
| 13358 | +** Or, if the first character of the input is not "'", then a copy of the input |
| 13359 | +** is returned. |
| 13360 | +*/ |
| 13361 | +static void recoverEscapeCrnl( |
| 13362 | + sqlite3_context *context, |
| 13363 | + int argc, |
| 13364 | + sqlite3_value **argv |
| 13365 | +){ |
| 13366 | + const char *zText = (const char*)sqlite3_value_text(argv[0]); |
| 13367 | + if( zText && zText[0]=='\'' ){ |
| 13368 | + int nText = sqlite3_value_bytes(argv[0]); |
| 13369 | + int i; |
| 13370 | + char zBuf1[20]; |
| 13371 | + char zBuf2[20]; |
| 13372 | + const char *zNL = 0; |
| 13373 | + const char *zCR = 0; |
| 13374 | + int nCR = 0; |
| 13375 | + int nNL = 0; |
| 13376 | + |
| 13377 | + for(i=0; zText[i]; i++){ |
| 13378 | + if( zNL==0 && zText[i]=='\n' ){ |
| 13379 | + zNL = recoverUnusedString(zText, "\\n", "\\012", zBuf1); |
| 13380 | + nNL = (int)strlen(zNL); |
| 13381 | + } |
| 13382 | + if( zCR==0 && zText[i]=='\r' ){ |
| 13383 | + zCR = recoverUnusedString(zText, "\\r", "\\015", zBuf2); |
| 13384 | + nCR = (int)strlen(zCR); |
| 13385 | + } |
| 13386 | + } |
| 13387 | + |
| 13388 | + if( zNL || zCR ){ |
| 13389 | + int iOut = 0; |
| 13390 | + i64 nMax = (nNL > nCR) ? nNL : nCR; |
| 13391 | + i64 nAlloc = nMax * nText + (nMax+64)*2; |
| 13392 | + char *zOut = (char*)sqlite3_malloc64(nAlloc); |
| 13393 | + if( zOut==0 ){ |
| 13394 | + sqlite3_result_error_nomem(context); |
| 13395 | + return; |
| 13396 | + } |
| 13397 | + |
| 13398 | + if( zNL && zCR ){ |
| 13399 | + memcpy(&zOut[iOut], "replace(replace(", 16); |
| 13400 | + iOut += 16; |
| 13401 | + }else{ |
| 13402 | + memcpy(&zOut[iOut], "replace(", 8); |
| 13403 | + iOut += 8; |
| 13404 | + } |
| 13405 | + for(i=0; zText[i]; i++){ |
| 13406 | + if( zText[i]=='\n' ){ |
| 13407 | + memcpy(&zOut[iOut], zNL, nNL); |
| 13408 | + iOut += nNL; |
| 13409 | + }else if( zText[i]=='\r' ){ |
| 13410 | + memcpy(&zOut[iOut], zCR, nCR); |
| 13411 | + iOut += nCR; |
| 13412 | + }else{ |
| 13413 | + zOut[iOut] = zText[i]; |
| 13414 | + iOut++; |
| 13415 | + } |
| 13416 | + } |
| 13417 | + |
| 13418 | + if( zNL ){ |
| 13419 | + memcpy(&zOut[iOut], ",'", 2); iOut += 2; |
| 13420 | + memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; |
| 13421 | + memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12; |
| 13422 | + } |
| 13423 | + if( zCR ){ |
| 13424 | + memcpy(&zOut[iOut], ",'", 2); iOut += 2; |
| 13425 | + memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; |
| 13426 | + memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12; |
| 13427 | + } |
| 13428 | + |
| 13429 | + sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT); |
| 13430 | + sqlite3_free(zOut); |
| 13431 | + return; |
| 13432 | + } |
| 13433 | + } |
| 13434 | + |
| 13435 | + sqlite3_result_value(context, argv[0]); |
| 13436 | +} |
| 13437 | + |
| 13438 | +/* |
| 13439 | +** This function is a no-op if recover handle p already contains an error |
| 13440 | +** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in |
| 13441 | +** this case. |
| 13442 | +** |
| 13443 | +** Otherwise, attempt to populate temporary table "recovery.schema" with the |
| 13444 | +** parts of the database schema that can be extracted from the input database. |
| 13445 | +** |
| 13446 | +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code |
| 13447 | +** and error message are left in the recover handle and a copy of the |
| 13448 | +** error code returned. It is not considered an error if part of all of |
| 13449 | +** the database schema cannot be recovered due to corruption. |
| 13450 | +*/ |
| 13451 | +static int recoverCacheSchema(sqlite3_recover *p){ |
| 13452 | + return recoverExec(p, p->dbOut, |
| 13453 | + "WITH RECURSIVE pages(p) AS (" |
| 13454 | + " SELECT 1" |
| 13455 | + " UNION" |
| 13456 | + " SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p" |
| 13457 | + ")" |
| 13458 | + "INSERT INTO recovery.schema SELECT" |
| 13459 | + " max(CASE WHEN field=0 THEN value ELSE NULL END)," |
| 13460 | + " max(CASE WHEN field=1 THEN value ELSE NULL END)," |
| 13461 | + " max(CASE WHEN field=2 THEN value ELSE NULL END)," |
| 13462 | + " max(CASE WHEN field=3 THEN value ELSE NULL END)," |
| 13463 | + " max(CASE WHEN field=4 THEN value ELSE NULL END)" |
| 13464 | + "FROM sqlite_dbdata('getpage()') WHERE pgno IN (" |
| 13465 | + " SELECT p FROM pages" |
| 13466 | + ") GROUP BY pgno, cell" |
| 13467 | + ); |
| 13468 | +} |
| 13469 | + |
| 13470 | +/* |
| 13471 | +** If this recover handle is not in SQL callback mode (i.e. was not created |
| 13472 | +** using sqlite3_recover_init_sql()) of if an error has already occurred, |
| 13473 | +** this function is a no-op. Otherwise, issue a callback with SQL statement |
| 13474 | +** zSql as the parameter. |
| 13475 | +** |
| 13476 | +** If the callback returns non-zero, set the recover handle error code to |
| 13477 | +** the value returned (so that the caller will abandon processing). |
| 13478 | +*/ |
| 13479 | +static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ |
| 13480 | + if( p->errCode==SQLITE_OK && p->xSql ){ |
| 13481 | + int res = p->xSql(p->pSqlCtx, zSql); |
| 13482 | + if( res ){ |
| 13483 | + recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res); |
| 13484 | + } |
| 13485 | + } |
| 13486 | +} |
| 13487 | + |
| 13488 | +/* |
| 13489 | +** Transfer the following settings from the input database to the output |
| 13490 | +** database: |
| 13491 | +** |
| 13492 | +** + page-size, |
| 13493 | +** + auto-vacuum settings, |
| 13494 | +** + database encoding, |
| 13495 | +** + user-version (PRAGMA user_version), and |
| 13496 | +** + application-id (PRAGMA application_id), and |
| 13497 | +*/ |
| 13498 | +static void recoverTransferSettings(sqlite3_recover *p){ |
| 13499 | + const char *aPragma[] = { |
| 13500 | + "encoding", |
| 13501 | + "page_size", |
| 13502 | + "auto_vacuum", |
| 13503 | + "user_version", |
| 13504 | + "application_id" |
| 13505 | + }; |
| 13506 | + int ii; |
| 13507 | + |
| 13508 | + /* Truncate the output database to 0 pages in size. This is done by |
| 13509 | + ** opening a new, empty, temp db, then using the backup API to clobber |
| 13510 | + ** any existing output db with a copy of it. */ |
| 13511 | + if( p->errCode==SQLITE_OK ){ |
| 13512 | + sqlite3 *db2 = 0; |
| 13513 | + int rc = sqlite3_open("", &db2); |
| 13514 | + if( rc!=SQLITE_OK ){ |
| 13515 | + recoverDbError(p, db2); |
| 13516 | + return; |
| 13517 | + } |
| 13518 | + |
| 13519 | + for(ii=0; ii<sizeof(aPragma)/sizeof(aPragma[0]); ii++){ |
| 13520 | + const char *zPrag = aPragma[ii]; |
| 13521 | + sqlite3_stmt *p1 = 0; |
| 13522 | + p1 = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.%s", p->zDb, zPrag); |
| 13523 | + if( p->errCode==SQLITE_OK && sqlite3_step(p1)==SQLITE_ROW ){ |
| 13524 | + const char *zArg = (const char*)sqlite3_column_text(p1, 0); |
| 13525 | + char *z2 = recoverMPrintf(p, "PRAGMA %s = %Q", zPrag, zArg); |
| 13526 | + recoverSqlCallback(p, z2); |
| 13527 | + recoverExec(p, db2, z2); |
| 13528 | + sqlite3_free(z2); |
| 13529 | + if( zArg==0 ){ |
| 13530 | + recoverError(p, SQLITE_NOMEM, 0); |
| 13531 | + } |
| 13532 | + } |
| 13533 | + recoverFinalize(p, p1); |
| 13534 | + } |
| 13535 | + recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;"); |
| 13536 | + |
| 13537 | + if( p->errCode==SQLITE_OK ){ |
| 13538 | + sqlite3 *db = p->dbOut; |
| 13539 | + sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); |
| 13540 | + if( pBackup ){ |
| 13541 | + sqlite3_backup_step(pBackup, -1); |
| 13542 | + p->errCode = sqlite3_backup_finish(pBackup); |
| 13543 | + }else{ |
| 13544 | + recoverDbError(p, db); |
| 13545 | + } |
| 13546 | + } |
| 13547 | + |
| 13548 | + sqlite3_close(db2); |
| 13549 | + } |
| 13550 | +} |
| 13551 | + |
| 13552 | +/* |
| 13553 | +** This function is a no-op if recover handle p already contains an error |
| 13554 | +** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in |
| 13555 | +** this case. |
| 13556 | +** |
| 13557 | +** Otherwise, an attempt is made to open the output database, attach |
| 13558 | +** and create the schema of the temporary database used to store |
| 13559 | +** intermediate data, and to register all required user functions and |
| 13560 | +** virtual table modules with the output handle. |
| 13561 | +** |
| 13562 | +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code |
| 13563 | +** and error message are left in the recover handle and a copy of the |
| 13564 | +** error code returned. |
| 13565 | +*/ |
| 13566 | +static int recoverOpenOutput(sqlite3_recover *p){ |
| 13567 | + struct Func { |
| 13568 | + const char *zName; |
| 13569 | + int nArg; |
| 13570 | + void (*xFunc)(sqlite3_context*,int,sqlite3_value **); |
| 13571 | + } aFunc[] = { |
| 13572 | + { "getpage", 1, recoverGetPage }, |
| 13573 | + { "page_is_used", 1, recoverPageIsUsed }, |
| 13574 | + { "read_i32", 2, recoverReadI32 }, |
| 13575 | + { "escape_crnl", 1, recoverEscapeCrnl }, |
| 13576 | + }; |
| 13577 | + |
| 13578 | + const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; |
| 13579 | + sqlite3 *db = 0; /* New database handle */ |
| 13580 | + int ii; /* For iterating through aFunc[] */ |
| 13581 | + |
| 13582 | + assert( p->dbOut==0 ); |
| 13583 | + |
| 13584 | + if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ |
| 13585 | + recoverDbError(p, db); |
| 13586 | + } |
| 13587 | + |
| 13588 | + /* Register the sqlite_dbdata and sqlite_dbptr virtual table modules. |
| 13589 | + ** These two are registered with the output database handle - this |
| 13590 | + ** module depends on the input handle supporting the sqlite_dbpage |
| 13591 | + ** virtual table only. */ |
| 13592 | + if( p->errCode==SQLITE_OK ){ |
| 13593 | + p->errCode = sqlite3_dbdata_init(db, 0, 0); |
| 13594 | + } |
| 13595 | + |
| 13596 | + /* Register the custom user-functions with the output handle. */ |
| 13597 | + for(ii=0; p->errCode==SQLITE_OK && ii<sizeof(aFunc)/sizeof(aFunc[0]); ii++){ |
| 13598 | + p->errCode = sqlite3_create_function(db, aFunc[ii].zName, |
| 13599 | + aFunc[ii].nArg, SQLITE_UTF8, (void*)p, aFunc[ii].xFunc, 0, 0 |
| 13600 | + ); |
| 13601 | + } |
| 13602 | + |
| 13603 | + p->dbOut = db; |
| 13604 | + return p->errCode; |
| 13605 | +} |
| 13606 | + |
| 13607 | +/* |
| 13608 | +** Attach the auxiliary database 'recovery' to the output database handle. |
| 13609 | +** This temporary database is used during the recovery process and then |
| 13610 | +** discarded. |
| 13611 | +*/ |
| 13612 | +static void recoverOpenRecovery(sqlite3_recover *p){ |
| 13613 | + char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); |
| 13614 | + recoverExec(p, p->dbOut, zSql); |
| 13615 | + recoverExec(p, p->dbOut, |
| 13616 | + "PRAGMA writable_schema = 1;" |
| 13617 | + "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);" |
| 13618 | + "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" |
| 13619 | + ); |
| 13620 | + sqlite3_free(zSql); |
| 13621 | +} |
| 13622 | + |
| 13623 | + |
| 13624 | +/* |
| 13625 | +** This function is a no-op if recover handle p already contains an error |
| 13626 | +** (if p->errCode!=SQLITE_OK). |
| 13627 | +** |
| 13628 | +** Otherwise, argument zName must be the name of a table that has just been |
| 13629 | +** created in the output database. This function queries the output db |
| 13630 | +** for the schema of said table, and creates a RecoverTable object to |
| 13631 | +** store the schema in memory. The new RecoverTable object is linked into |
| 13632 | +** the list at sqlite3_recover.pTblList. |
| 13633 | +** |
| 13634 | +** Parameter iRoot must be the root page of table zName in the INPUT |
| 13635 | +** database. |
| 13636 | +*/ |
| 13637 | +static void recoverAddTable( |
| 13638 | + sqlite3_recover *p, |
| 13639 | + const char *zName, /* Name of table created in output db */ |
| 13640 | + i64 iRoot /* Root page of same table in INPUT db */ |
| 13641 | +){ |
| 13642 | + sqlite3_stmt *pStmt = recoverPreparePrintf(p, p->dbOut, |
| 13643 | + "PRAGMA table_xinfo(%Q)", zName |
| 13644 | + ); |
| 13645 | + |
| 13646 | + if( pStmt ){ |
| 13647 | + int iPk = -1; |
| 13648 | + int iBind = 1; |
| 13649 | + RecoverTable *pNew = 0; |
| 13650 | + int nCol = 0; |
| 13651 | + int nName = recoverStrlen(zName); |
| 13652 | + int nByte = 0; |
| 13653 | + while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 13654 | + nCol++; |
| 13655 | + nByte += (sqlite3_column_bytes(pStmt, 1)+1); |
| 13656 | + } |
| 13657 | + nByte += sizeof(RecoverTable) + nCol*sizeof(RecoverColumn) + nName+1; |
| 13658 | + recoverReset(p, pStmt); |
| 13659 | + |
| 13660 | + pNew = recoverMalloc(p, nByte); |
| 13661 | + if( pNew ){ |
| 13662 | + int i = 0; |
| 13663 | + int iField = 0; |
| 13664 | + char *csr = 0; |
| 13665 | + pNew->aCol = (RecoverColumn*)&pNew[1]; |
| 13666 | + pNew->zTab = csr = (char*)&pNew->aCol[nCol]; |
| 13667 | + pNew->nCol = nCol; |
| 13668 | + pNew->iRoot = iRoot; |
| 13669 | + memcpy(csr, zName, nName); |
| 13670 | + csr += nName+1; |
| 13671 | + |
| 13672 | + for(i=0; sqlite3_step(pStmt)==SQLITE_ROW; i++){ |
| 13673 | + int iPKF = sqlite3_column_int(pStmt, 5); |
| 13674 | + int n = sqlite3_column_bytes(pStmt, 1); |
| 13675 | + const char *z = (const char*)sqlite3_column_text(pStmt, 1); |
| 13676 | + const char *zType = (const char*)sqlite3_column_text(pStmt, 2); |
| 13677 | + int eHidden = sqlite3_column_int(pStmt, 6); |
| 13678 | + |
| 13679 | + if( iPk==-1 && iPKF==1 && !sqlite3_stricmp("integer", zType) ) iPk = i; |
| 13680 | + if( iPKF>1 ) iPk = -2; |
| 13681 | + pNew->aCol[i].zCol = csr; |
| 13682 | + pNew->aCol[i].eHidden = eHidden; |
| 13683 | + if( eHidden==RECOVER_EHIDDEN_VIRTUAL ){ |
| 13684 | + pNew->aCol[i].iField = -1; |
| 13685 | + }else{ |
| 13686 | + pNew->aCol[i].iField = iField++; |
| 13687 | + } |
| 13688 | + if( eHidden!=RECOVER_EHIDDEN_VIRTUAL |
| 13689 | + && eHidden!=RECOVER_EHIDDEN_STORED |
| 13690 | + ){ |
| 13691 | + pNew->aCol[i].iBind = iBind++; |
| 13692 | + } |
| 13693 | + memcpy(csr, z, n); |
| 13694 | + csr += (n+1); |
| 13695 | + } |
| 13696 | + |
| 13697 | + pNew->pNext = p->pTblList; |
| 13698 | + p->pTblList = pNew; |
| 13699 | + pNew->bIntkey = 1; |
| 13700 | + } |
| 13701 | + |
| 13702 | + recoverFinalize(p, pStmt); |
| 13703 | + |
| 13704 | + pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); |
| 13705 | + while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 13706 | + int iField = sqlite3_column_int(pStmt, 0); |
| 13707 | + int iCol = sqlite3_column_int(pStmt, 1); |
| 13708 | + |
| 13709 | + assert( iField<pNew->nCol && iCol<pNew->nCol ); |
| 13710 | + pNew->aCol[iCol].iField = iField; |
| 13711 | + |
| 13712 | + pNew->bIntkey = 0; |
| 13713 | + iPk = -2; |
| 13714 | + } |
| 13715 | + recoverFinalize(p, pStmt); |
| 13716 | + |
| 13717 | + if( p->errCode==SQLITE_OK ){ |
| 13718 | + if( iPk>=0 ){ |
| 13719 | + pNew->aCol[iPk].bIPK = 1; |
| 13720 | + }else if( pNew->bIntkey ){ |
| 13721 | + pNew->iRowidBind = iBind++; |
| 13722 | + } |
| 13723 | + } |
| 13724 | + } |
| 13725 | +} |
| 13726 | + |
| 13727 | +/* |
| 13728 | +** This function is called after recoverCacheSchema() has cached those parts |
| 13729 | +** of the input database schema that could be recovered in temporary table |
| 13730 | +** "recovery.schema". This function creates in the output database copies |
| 13731 | +** of all parts of that schema that must be created before the tables can |
| 13732 | +** be populated. Specifically, this means: |
| 13733 | +** |
| 13734 | +** * all tables that are not VIRTUAL, and |
| 13735 | +** * UNIQUE indexes. |
| 13736 | +** |
| 13737 | +** If the recovery handle uses SQL callbacks, then callbacks containing |
| 13738 | +** the associated "CREATE TABLE" and "CREATE INDEX" statements are made. |
| 13739 | +** |
| 13740 | +** Additionally, records are added to the sqlite_schema table of the |
| 13741 | +** output database for any VIRTUAL tables. The CREATE VIRTUAL TABLE |
| 13742 | +** records are written directly to sqlite_schema, not actually executed. |
| 13743 | +** If the handle is in SQL callback mode, then callbacks are invoked |
| 13744 | +** with equivalent SQL statements. |
| 13745 | +*/ |
| 13746 | +static int recoverWriteSchema1(sqlite3_recover *p){ |
| 13747 | + sqlite3_stmt *pSelect = 0; |
| 13748 | + sqlite3_stmt *pTblname = 0; |
| 13749 | + |
| 13750 | + pSelect = recoverPrepare(p, p->dbOut, |
| 13751 | + "WITH dbschema(rootpage, name, sql, tbl, isVirtual, isIndex) AS (" |
| 13752 | + " SELECT rootpage, name, sql, " |
| 13753 | + " type='table', " |
| 13754 | + " sql LIKE 'create virtual%'," |
| 13755 | + " (type='index' AND (sql LIKE '%unique%' OR ?1))" |
| 13756 | + " FROM recovery.schema" |
| 13757 | + ")" |
| 13758 | + "SELECT rootpage, tbl, isVirtual, name, sql" |
| 13759 | + " FROM dbschema " |
| 13760 | + " WHERE tbl OR isIndex" |
| 13761 | + " ORDER BY tbl DESC, name=='sqlite_sequence' DESC" |
| 13762 | + ); |
| 13763 | + |
| 13764 | + pTblname = recoverPrepare(p, p->dbOut, |
| 13765 | + "SELECT name FROM sqlite_schema " |
| 13766 | + "WHERE type='table' ORDER BY rowid DESC LIMIT 1" |
| 13767 | + ); |
| 13768 | + |
| 13769 | + if( pSelect ){ |
| 13770 | + sqlite3_bind_int(pSelect, 1, p->bSlowIndexes); |
| 13771 | + while( sqlite3_step(pSelect)==SQLITE_ROW ){ |
| 13772 | + i64 iRoot = sqlite3_column_int64(pSelect, 0); |
| 13773 | + int bTable = sqlite3_column_int(pSelect, 1); |
| 13774 | + int bVirtual = sqlite3_column_int(pSelect, 2); |
| 13775 | + const char *zName = (const char*)sqlite3_column_text(pSelect, 3); |
| 13776 | + const char *zSql = (const char*)sqlite3_column_text(pSelect, 4); |
| 13777 | + char *zFree = 0; |
| 13778 | + int rc = SQLITE_OK; |
| 13779 | + |
| 13780 | + if( bVirtual ){ |
| 13781 | + zSql = (const char*)(zFree = recoverMPrintf(p, |
| 13782 | + "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", |
| 13783 | + zName, zName, zSql |
| 13784 | + )); |
| 13785 | + } |
| 13786 | + rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); |
| 13787 | + if( rc==SQLITE_OK ){ |
| 13788 | + recoverSqlCallback(p, zSql); |
| 13789 | + if( bTable && !bVirtual ){ |
| 13790 | + if( SQLITE_ROW==sqlite3_step(pTblname) ){ |
| 13791 | + const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0); |
| 13792 | + recoverAddTable(p, zTbl, iRoot); |
| 13793 | + } |
| 13794 | + recoverReset(p, pTblname); |
| 13795 | + } |
| 13796 | + }else if( rc!=SQLITE_ERROR ){ |
| 13797 | + recoverDbError(p, p->dbOut); |
| 13798 | + } |
| 13799 | + sqlite3_free(zFree); |
| 13800 | + } |
| 13801 | + } |
| 13802 | + recoverFinalize(p, pSelect); |
| 13803 | + recoverFinalize(p, pTblname); |
| 13804 | + |
| 13805 | + return p->errCode; |
| 13806 | +} |
| 13807 | + |
| 13808 | +/* |
| 13809 | +** This function is called after the output database has been populated. It |
| 13810 | +** adds all recovered schema elements that were not created in the output |
| 13811 | +** database by recoverWriteSchema1() - everything except for tables and |
| 13812 | +** UNIQUE indexes. Specifically: |
| 13813 | +** |
| 13814 | +** * views, |
| 13815 | +** * triggers, |
| 13816 | +** * non-UNIQUE indexes. |
| 13817 | +** |
| 13818 | +** If the recover handle is in SQL callback mode, then equivalent callbacks |
| 13819 | +** are issued to create the schema elements. |
| 13820 | +*/ |
| 13821 | +static int recoverWriteSchema2(sqlite3_recover *p){ |
| 13822 | + sqlite3_stmt *pSelect = 0; |
| 13823 | + |
| 13824 | + pSelect = recoverPrepare(p, p->dbOut, |
| 13825 | + p->bSlowIndexes ? |
| 13826 | + "SELECT rootpage, sql FROM recovery.schema " |
| 13827 | + " WHERE type!='table' AND type!='index'" |
| 13828 | + : |
| 13829 | + "SELECT rootpage, sql FROM recovery.schema " |
| 13830 | + " WHERE type!='table' AND (type!='index' OR sql NOT LIKE '%unique%')" |
| 13831 | + ); |
| 13832 | + |
| 13833 | + if( pSelect ){ |
| 13834 | + while( sqlite3_step(pSelect)==SQLITE_ROW ){ |
| 13835 | + const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); |
| 13836 | + int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); |
| 13837 | + if( rc==SQLITE_OK ){ |
| 13838 | + recoverSqlCallback(p, zSql); |
| 13839 | + }else if( rc!=SQLITE_ERROR ){ |
| 13840 | + recoverDbError(p, p->dbOut); |
| 13841 | + } |
| 13842 | + } |
| 13843 | + } |
| 13844 | + recoverFinalize(p, pSelect); |
| 13845 | + |
| 13846 | + return p->errCode; |
| 13847 | +} |
| 13848 | + |
| 13849 | +/* |
| 13850 | +** This function is a no-op if recover handle p already contains an error |
| 13851 | +** (if p->errCode!=SQLITE_OK). In this case it returns NULL. |
| 13852 | +** |
| 13853 | +** Otherwise, if the recover handle is configured to create an output |
| 13854 | +** database (was created by sqlite3_recover_init()), then this function |
| 13855 | +** prepares and returns an SQL statement to INSERT a new record into table |
| 13856 | +** pTab, assuming the first nField fields of a record extracted from disk |
| 13857 | +** are valid. |
| 13858 | +** |
| 13859 | +** For example, if table pTab is: |
| 13860 | +** |
| 13861 | +** CREATE TABLE name(a, b GENERATED ALWAYS AS (a+1) STORED, c, d, e); |
| 13862 | +** |
| 13863 | +** And nField is 4, then the SQL statement prepared and returned is: |
| 13864 | +** |
| 13865 | +** INSERT INTO (a, c, d) VALUES (?1, ?2, ?3); |
| 13866 | +** |
| 13867 | +** In this case even though 4 values were extracted from the input db, |
| 13868 | +** only 3 are written to the output, as the generated STORED column |
| 13869 | +** cannot be written. |
| 13870 | +** |
| 13871 | +** If the recover handle is in SQL callback mode, then the SQL statement |
| 13872 | +** prepared is such that evaluating it returns a single row containing |
| 13873 | +** a single text value - itself an SQL statement similar to the above, |
| 13874 | +** except with SQL literals in place of the variables. For example: |
| 13875 | +** |
| 13876 | +** SELECT 'INSERT INTO (a, c, d) VALUES (' |
| 13877 | +** || quote(?1) || ', ' |
| 13878 | +** || quote(?2) || ', ' |
| 13879 | +** || quote(?3) || ')'; |
| 13880 | +** |
| 13881 | +** In either case, it is the responsibility of the caller to eventually |
| 13882 | +** free the statement handle using sqlite3_finalize(). |
| 13883 | +*/ |
| 13884 | +static sqlite3_stmt *recoverInsertStmt( |
| 13885 | + sqlite3_recover *p, |
| 13886 | + RecoverTable *pTab, |
| 13887 | + int nField |
| 13888 | +){ |
| 13889 | + sqlite3_stmt *pRet = 0; |
| 13890 | + const char *zSep = ""; |
| 13891 | + const char *zSqlSep = ""; |
| 13892 | + char *zSql = 0; |
| 13893 | + char *zFinal = 0; |
| 13894 | + char *zBind = 0; |
| 13895 | + int ii; |
| 13896 | + int bSql = p->xSql ? 1 : 0; |
| 13897 | + |
| 13898 | + if( nField<=0 ) return 0; |
| 13899 | + |
| 13900 | + assert( nField<=pTab->nCol ); |
| 13901 | + |
| 13902 | + zSql = recoverMPrintf(p, "INSERT OR IGNORE INTO %Q(", pTab->zTab); |
| 13903 | + |
| 13904 | + if( pTab->iRowidBind ){ |
| 13905 | + assert( pTab->bIntkey ); |
| 13906 | + zSql = recoverMPrintf(p, "%z_rowid_", zSql); |
| 13907 | + if( bSql ){ |
| 13908 | + zBind = recoverMPrintf(p, "%zquote(?%d)", zBind, pTab->iRowidBind); |
| 13909 | + }else{ |
| 13910 | + zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind); |
| 13911 | + } |
| 13912 | + zSqlSep = "||', '||"; |
| 13913 | + zSep = ", "; |
| 13914 | + } |
| 13915 | + |
| 13916 | + for(ii=0; ii<nField; ii++){ |
| 13917 | + int eHidden = pTab->aCol[ii].eHidden; |
| 13918 | + if( eHidden!=RECOVER_EHIDDEN_VIRTUAL |
| 13919 | + && eHidden!=RECOVER_EHIDDEN_STORED |
| 13920 | + ){ |
| 13921 | + assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); |
| 13922 | + zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); |
| 13923 | + |
| 13924 | + if( bSql ){ |
| 13925 | + zBind = recoverMPrintf(p, |
| 13926 | + "%z%sescape_crnl(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind |
| 13927 | + ); |
| 13928 | + zSqlSep = "||', '||"; |
| 13929 | + }else{ |
| 13930 | + zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); |
| 13931 | + } |
| 13932 | + zSep = ", "; |
| 13933 | + } |
| 13934 | + } |
| 13935 | + |
| 13936 | + if( bSql ){ |
| 13937 | + zFinal = recoverMPrintf(p, "SELECT %Q || ') VALUES (' || %s || ')'", |
| 13938 | + zSql, zBind |
| 13939 | + ); |
| 13940 | + }else{ |
| 13941 | + zFinal = recoverMPrintf(p, "%s) VALUES (%s)", zSql, zBind); |
| 13942 | + } |
| 13943 | + |
| 13944 | + pRet = recoverPrepare(p, p->dbOut, zFinal); |
| 13945 | + sqlite3_free(zSql); |
| 13946 | + sqlite3_free(zBind); |
| 13947 | + sqlite3_free(zFinal); |
| 13948 | + |
| 13949 | + return pRet; |
| 13950 | +} |
| 13951 | + |
| 13952 | + |
| 13953 | +/* |
| 13954 | +** Search the list of RecoverTable objects at p->pTblList for one that |
| 13955 | +** has root page iRoot in the input database. If such an object is found, |
| 13956 | +** return a pointer to it. Otherwise, return NULL. |
| 13957 | +*/ |
| 13958 | +static RecoverTable *recoverFindTable(sqlite3_recover *p, u32 iRoot){ |
| 13959 | + RecoverTable *pRet = 0; |
| 13960 | + for(pRet=p->pTblList; pRet && pRet->iRoot!=iRoot; pRet=pRet->pNext); |
| 13961 | + return pRet; |
| 13962 | +} |
| 13963 | + |
| 13964 | +/* |
| 13965 | +** This function attempts to create a lost and found table within the |
| 13966 | +** output db. If successful, it returns a pointer to a buffer containing |
| 13967 | +** the name of the new table. It is the responsibility of the caller to |
| 13968 | +** eventually free this buffer using sqlite3_free(). |
| 13969 | +** |
| 13970 | +** If an error occurs, NULL is returned and an error code and error |
| 13971 | +** message left in the recover handle. |
| 13972 | +*/ |
| 13973 | +static char *recoverLostAndFoundCreate( |
| 13974 | + sqlite3_recover *p, /* Recover object */ |
| 13975 | + int nField /* Number of column fields in new table */ |
| 13976 | +){ |
| 13977 | + char *zTbl = 0; |
| 13978 | + sqlite3_stmt *pProbe = 0; |
| 13979 | + int ii = 0; |
| 13980 | + |
| 13981 | + pProbe = recoverPrepare(p, p->dbOut, |
| 13982 | + "SELECT 1 FROM sqlite_schema WHERE name=?" |
| 13983 | + ); |
| 13984 | + for(ii=-1; zTbl==0 && p->errCode==SQLITE_OK && ii<1000; ii++){ |
| 13985 | + int bFail = 0; |
| 13986 | + if( ii<0 ){ |
| 13987 | + zTbl = recoverMPrintf(p, "%s", p->zLostAndFound); |
| 13988 | + }else{ |
| 13989 | + zTbl = recoverMPrintf(p, "%s_%d", p->zLostAndFound, ii); |
| 13990 | + } |
| 13991 | + |
| 13992 | + if( p->errCode==SQLITE_OK ){ |
| 13993 | + sqlite3_bind_text(pProbe, 1, zTbl, -1, SQLITE_STATIC); |
| 13994 | + if( SQLITE_ROW==sqlite3_step(pProbe) ){ |
| 13995 | + bFail = 1; |
| 13996 | + } |
| 13997 | + recoverReset(p, pProbe); |
| 13998 | + } |
| 13999 | + |
| 14000 | + if( bFail ){ |
| 14001 | + sqlite3_clear_bindings(pProbe); |
| 14002 | + sqlite3_free(zTbl); |
| 14003 | + zTbl = 0; |
| 14004 | + } |
| 14005 | + } |
| 14006 | + recoverFinalize(p, pProbe); |
| 14007 | + |
| 14008 | + if( zTbl ){ |
| 14009 | + const char *zSep = 0; |
| 14010 | + char *zField = 0; |
| 14011 | + char *zSql = 0; |
| 14012 | + |
| 14013 | + zSep = "rootpgno INTEGER, pgno INTEGER, nfield INTEGER, id INTEGER, "; |
| 14014 | + for(ii=0; p->errCode==SQLITE_OK && ii<nField; ii++){ |
| 14015 | + zField = recoverMPrintf(p, "%z%sc%d", zField, zSep, ii); |
| 14016 | + zSep = ", "; |
| 14017 | + } |
| 14018 | + |
| 14019 | + zSql = recoverMPrintf(p, "CREATE TABLE %s(%s)", zTbl, zField); |
| 14020 | + sqlite3_free(zField); |
| 14021 | + |
| 14022 | + recoverExec(p, p->dbOut, zSql); |
| 14023 | + recoverSqlCallback(p, zSql); |
| 14024 | + sqlite3_free(zSql); |
| 14025 | + }else if( p->errCode==SQLITE_OK ){ |
| 14026 | + recoverError( |
| 14027 | + p, SQLITE_ERROR, "failed to create %s output table", p->zLostAndFound |
| 14028 | + ); |
| 14029 | + } |
| 14030 | + |
| 14031 | + return zTbl; |
| 14032 | +} |
| 14033 | + |
| 14034 | +/* |
| 14035 | +** Synthesize and prepare an INSERT statement to write to the lost_and_found |
| 14036 | +** table in the output database. The name of the table is zTab, and it has |
| 14037 | +** nField c* fields. |
| 14038 | +*/ |
| 14039 | +static sqlite3_stmt *recoverLostAndFoundInsert( |
| 14040 | + sqlite3_recover *p, |
| 14041 | + const char *zTab, |
| 14042 | + int nField |
| 14043 | +){ |
| 14044 | + int nTotal = nField + 4; |
| 14045 | + int ii; |
| 14046 | + char *zBind = 0; |
| 14047 | + sqlite3_stmt *pRet = 0; |
| 14048 | + |
| 14049 | + if( p->xSql==0 ){ |
| 14050 | + for(ii=0; ii<nTotal; ii++){ |
| 14051 | + zBind = recoverMPrintf(p, "%z%s?", zBind, zBind?", ":"", ii); |
| 14052 | + } |
| 14053 | + pRet = recoverPreparePrintf( |
| 14054 | + p, p->dbOut, "INSERT INTO %s VALUES(%s)", zTab, zBind |
| 14055 | + ); |
| 14056 | + }else{ |
| 14057 | + const char *zSep = ""; |
| 14058 | + for(ii=0; ii<nTotal; ii++){ |
| 14059 | + zBind = recoverMPrintf(p, "%z%squote(?)", zBind, zSep); |
| 14060 | + zSep = "|| ', ' ||"; |
| 14061 | + } |
| 14062 | + pRet = recoverPreparePrintf( |
| 14063 | + p, p->dbOut, "SELECT 'INSERT INTO %s VALUES(' || %s || ')'", zTab, zBind |
| 14064 | + ); |
| 14065 | + } |
| 14066 | + |
| 14067 | + sqlite3_free(zBind); |
| 14068 | + return pRet; |
| 14069 | +} |
| 14070 | + |
| 14071 | +/* |
| 14072 | +** Input database page iPg contains data that will be written to the |
| 14073 | +** lost-and-found table of the output database. This function attempts |
| 14074 | +** to identify the root page of the tree that page iPg belonged to. |
| 14075 | +** If successful, it sets output variable (*piRoot) to the page number |
| 14076 | +** of the root page and returns SQLITE_OK. Otherwise, if an error occurs, |
| 14077 | +** an SQLite error code is returned and the final value of *piRoot |
| 14078 | +** undefined. |
| 14079 | +*/ |
| 14080 | +static int recoverLostAndFoundFindRoot( |
| 14081 | + sqlite3_recover *p, |
| 14082 | + i64 iPg, |
| 14083 | + i64 *piRoot |
| 14084 | +){ |
| 14085 | + RecoverStateLAF *pLaf = &p->laf; |
| 14086 | + |
| 14087 | + if( pLaf->pFindRoot==0 ){ |
| 14088 | + pLaf->pFindRoot = recoverPrepare(p, p->dbOut, |
| 14089 | + "WITH RECURSIVE p(pgno) AS (" |
| 14090 | + " SELECT ?" |
| 14091 | + " UNION" |
| 14092 | + " SELECT parent FROM recovery.map AS m, p WHERE m.pgno=p.pgno" |
| 14093 | + ") " |
| 14094 | + "SELECT p.pgno FROM p, recovery.map m WHERE m.pgno=p.pgno " |
| 14095 | + " AND m.parent IS NULL" |
| 14096 | + ); |
| 14097 | + } |
| 14098 | + if( p->errCode==SQLITE_OK ){ |
| 14099 | + sqlite3_bind_int64(pLaf->pFindRoot, 1, iPg); |
| 14100 | + if( sqlite3_step(pLaf->pFindRoot)==SQLITE_ROW ){ |
| 14101 | + *piRoot = sqlite3_column_int64(pLaf->pFindRoot, 0); |
| 14102 | + }else{ |
| 14103 | + *piRoot = iPg; |
| 14104 | + } |
| 14105 | + recoverReset(p, pLaf->pFindRoot); |
| 14106 | + } |
| 14107 | + return p->errCode; |
| 14108 | +} |
| 14109 | + |
| 14110 | +/* |
| 14111 | +** Recover data from page iPage of the input database and write it to |
| 14112 | +** the lost-and-found table in the output database. |
| 14113 | +*/ |
| 14114 | +static void recoverLostAndFoundOnePage(sqlite3_recover *p, i64 iPage){ |
| 14115 | + RecoverStateLAF *pLaf = &p->laf; |
| 14116 | + sqlite3_value **apVal = pLaf->apVal; |
| 14117 | + sqlite3_stmt *pPageData = pLaf->pPageData; |
| 14118 | + sqlite3_stmt *pInsert = pLaf->pInsert; |
| 14119 | + |
| 14120 | + int nVal = -1; |
| 14121 | + int iPrevCell = 0; |
| 14122 | + i64 iRoot = 0; |
| 14123 | + int bHaveRowid = 0; |
| 14124 | + i64 iRowid = 0; |
| 14125 | + int ii = 0; |
| 14126 | + |
| 14127 | + if( recoverLostAndFoundFindRoot(p, iPage, &iRoot) ) return; |
| 14128 | + sqlite3_bind_int64(pPageData, 1, iPage); |
| 14129 | + while( p->errCode==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPageData) ){ |
| 14130 | + int iCell = sqlite3_column_int64(pPageData, 0); |
| 14131 | + int iField = sqlite3_column_int64(pPageData, 1); |
| 14132 | + |
| 14133 | + if( iPrevCell!=iCell && nVal>=0 ){ |
| 14134 | + /* Insert the new row */ |
| 14135 | + sqlite3_bind_int64(pInsert, 1, iRoot); /* rootpgno */ |
| 14136 | + sqlite3_bind_int64(pInsert, 2, iPage); /* pgno */ |
| 14137 | + sqlite3_bind_int(pInsert, 3, nVal); /* nfield */ |
| 14138 | + if( bHaveRowid ){ |
| 14139 | + sqlite3_bind_int64(pInsert, 4, iRowid); /* id */ |
| 14140 | + } |
| 14141 | + for(ii=0; ii<nVal; ii++){ |
| 14142 | + recoverBindValue(p, pInsert, 5+ii, apVal[ii]); |
| 14143 | + } |
| 14144 | + if( sqlite3_step(pInsert)==SQLITE_ROW ){ |
| 14145 | + recoverSqlCallback(p, (const char*)sqlite3_column_text(pInsert, 0)); |
| 14146 | + } |
| 14147 | + recoverReset(p, pInsert); |
| 14148 | + |
| 14149 | + /* Discard the accumulated row data */ |
| 14150 | + for(ii=0; ii<nVal; ii++){ |
| 14151 | + sqlite3_value_free(apVal[ii]); |
| 14152 | + apVal[ii] = 0; |
| 14153 | + } |
| 14154 | + sqlite3_clear_bindings(pInsert); |
| 14155 | + bHaveRowid = 0; |
| 14156 | + nVal = -1; |
| 14157 | + } |
| 14158 | + |
| 14159 | + if( iCell<0 ) break; |
| 14160 | + |
| 14161 | + if( iField<0 ){ |
| 14162 | + assert( nVal==-1 ); |
| 14163 | + iRowid = sqlite3_column_int64(pPageData, 2); |
| 14164 | + bHaveRowid = 1; |
| 14165 | + nVal = 0; |
| 14166 | + }else if( iField<pLaf->nMaxField ){ |
| 14167 | + sqlite3_value *pVal = sqlite3_column_value(pPageData, 2); |
| 14168 | + apVal[iField] = sqlite3_value_dup(pVal); |
| 14169 | + assert( iField==nVal || (nVal==-1 && iField==0) ); |
| 14170 | + nVal = iField+1; |
| 14171 | + if( apVal[iField]==0 ){ |
| 14172 | + recoverError(p, SQLITE_NOMEM, 0); |
| 14173 | + } |
| 14174 | + } |
| 14175 | + |
| 14176 | + iPrevCell = iCell; |
| 14177 | + } |
| 14178 | + recoverReset(p, pPageData); |
| 14179 | + |
| 14180 | + for(ii=0; ii<nVal; ii++){ |
| 14181 | + sqlite3_value_free(apVal[ii]); |
| 14182 | + apVal[ii] = 0; |
| 14183 | + } |
| 14184 | +} |
| 14185 | + |
| 14186 | +/* |
| 14187 | +** Perform one step (sqlite3_recover_step()) of work for the connection |
| 14188 | +** passed as the only argument, which is guaranteed to be in |
| 14189 | +** RECOVER_STATE_LOSTANDFOUND3 state - during which the lost-and-found |
| 14190 | +** table of the output database is populated with recovered data that can |
| 14191 | +** not be assigned to any recovered schema object. |
| 14192 | +*/ |
| 14193 | +static int recoverLostAndFound3Step(sqlite3_recover *p){ |
| 14194 | + RecoverStateLAF *pLaf = &p->laf; |
| 14195 | + if( p->errCode==SQLITE_OK ){ |
| 14196 | + if( pLaf->pInsert==0 ){ |
| 14197 | + return SQLITE_DONE; |
| 14198 | + }else{ |
| 14199 | + if( p->errCode==SQLITE_OK ){ |
| 14200 | + int res = sqlite3_step(pLaf->pAllPage); |
| 14201 | + if( res==SQLITE_ROW ){ |
| 14202 | + i64 iPage = sqlite3_column_int64(pLaf->pAllPage, 0); |
| 14203 | + if( recoverBitmapQuery(pLaf->pUsed, iPage)==0 ){ |
| 14204 | + recoverLostAndFoundOnePage(p, iPage); |
| 14205 | + } |
| 14206 | + }else{ |
| 14207 | + recoverReset(p, pLaf->pAllPage); |
| 14208 | + return SQLITE_DONE; |
| 14209 | + } |
| 14210 | + } |
| 14211 | + } |
| 14212 | + } |
| 14213 | + return SQLITE_OK; |
| 14214 | +} |
| 14215 | + |
| 14216 | +/* |
| 14217 | +** Initialize resources required in RECOVER_STATE_LOSTANDFOUND3 |
| 14218 | +** state - during which the lost-and-found table of the output database |
| 14219 | +** is populated with recovered data that can not be assigned to any |
| 14220 | +** recovered schema object. |
| 14221 | +*/ |
| 14222 | +static void recoverLostAndFound3Init(sqlite3_recover *p){ |
| 14223 | + RecoverStateLAF *pLaf = &p->laf; |
| 14224 | + |
| 14225 | + if( pLaf->nMaxField>0 ){ |
| 14226 | + char *zTab = 0; /* Name of lost_and_found table */ |
| 14227 | + |
| 14228 | + zTab = recoverLostAndFoundCreate(p, pLaf->nMaxField); |
| 14229 | + pLaf->pInsert = recoverLostAndFoundInsert(p, zTab, pLaf->nMaxField); |
| 14230 | + sqlite3_free(zTab); |
| 14231 | + |
| 14232 | + pLaf->pAllPage = recoverPreparePrintf(p, p->dbOut, |
| 14233 | + "WITH RECURSIVE seq(ii) AS (" |
| 14234 | + " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" |
| 14235 | + ")" |
| 14236 | + "SELECT ii FROM seq" , p->laf.nPg |
| 14237 | + ); |
| 14238 | + pLaf->pPageData = recoverPrepare(p, p->dbOut, |
| 14239 | + "SELECT cell, field, value " |
| 14240 | + "FROM sqlite_dbdata('getpage()') d WHERE d.pgno=? " |
| 14241 | + "UNION ALL " |
| 14242 | + "SELECT -1, -1, -1" |
| 14243 | + ); |
| 14244 | + |
| 14245 | + pLaf->apVal = (sqlite3_value**)recoverMalloc(p, |
| 14246 | + pLaf->nMaxField*sizeof(sqlite3_value*) |
| 14247 | + ); |
| 14248 | + } |
| 14249 | +} |
| 14250 | + |
| 14251 | +/* |
| 14252 | +** Initialize resources required in RECOVER_STATE_WRITING state - during which |
| 14253 | +** tables recovered from the schema of the input database are populated with |
| 14254 | +** recovered data. |
| 14255 | +*/ |
| 14256 | +static int recoverWriteDataInit(sqlite3_recover *p){ |
| 14257 | + RecoverStateW1 *p1 = &p->w1; |
| 14258 | + RecoverTable *pTbl = 0; |
| 14259 | + int nByte = 0; |
| 14260 | + |
| 14261 | + /* Figure out the maximum number of columns for any table in the schema */ |
| 14262 | + assert( p1->nMax==0 ); |
| 14263 | + for(pTbl=p->pTblList; pTbl; pTbl=pTbl->pNext){ |
| 14264 | + if( pTbl->nCol>p1->nMax ) p1->nMax = pTbl->nCol; |
| 14265 | + } |
| 14266 | + |
| 14267 | + /* Allocate an array of (sqlite3_value*) in which to accumulate the values |
| 14268 | + ** that will be written to the output database in a single row. */ |
| 14269 | + nByte = sizeof(sqlite3_value*) * (p1->nMax+1); |
| 14270 | + p1->apVal = (sqlite3_value**)recoverMalloc(p, nByte); |
| 14271 | + if( p1->apVal==0 ) return p->errCode; |
| 14272 | + |
| 14273 | + /* Prepare the SELECT to loop through schema tables (pTbls) and the SELECT |
| 14274 | + ** to loop through cells that appear to belong to a single table (pSel). */ |
| 14275 | + p1->pTbls = recoverPrepare(p, p->dbOut, |
| 14276 | + "SELECT rootpage FROM recovery.schema " |
| 14277 | + " WHERE type='table' AND (sql NOT LIKE 'create virtual%')" |
| 14278 | + " ORDER BY (tbl_name='sqlite_sequence') ASC" |
| 14279 | + ); |
| 14280 | + p1->pSel = recoverPrepare(p, p->dbOut, |
| 14281 | + "WITH RECURSIVE pages(page) AS (" |
| 14282 | + " SELECT ?1" |
| 14283 | + " UNION" |
| 14284 | + " SELECT child FROM sqlite_dbptr('getpage()'), pages " |
| 14285 | + " WHERE pgno=page" |
| 14286 | + ") " |
| 14287 | + "SELECT page, cell, field, value " |
| 14288 | + "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " |
| 14289 | + "UNION ALL " |
| 14290 | + "SELECT 0, 0, 0, 0" |
| 14291 | + ); |
| 14292 | + |
| 14293 | + return p->errCode; |
| 14294 | +} |
| 14295 | + |
| 14296 | +/* |
| 14297 | +** Clean up resources allocated by recoverWriteDataInit() (stuff in |
| 14298 | +** sqlite3_recover.w1). |
| 14299 | +*/ |
| 14300 | +static void recoverWriteDataCleanup(sqlite3_recover *p){ |
| 14301 | + RecoverStateW1 *p1 = &p->w1; |
| 14302 | + int ii; |
| 14303 | + for(ii=0; ii<p1->nVal; ii++){ |
| 14304 | + sqlite3_value_free(p1->apVal[ii]); |
| 14305 | + } |
| 14306 | + sqlite3_free(p1->apVal); |
| 14307 | + recoverFinalize(p, p1->pInsert); |
| 14308 | + recoverFinalize(p, p1->pTbls); |
| 14309 | + recoverFinalize(p, p1->pSel); |
| 14310 | + memset(p1, 0, sizeof(*p1)); |
| 14311 | +} |
| 14312 | + |
| 14313 | +/* |
| 14314 | +** Perform one step (sqlite3_recover_step()) of work for the connection |
| 14315 | +** passed as the only argument, which is guaranteed to be in |
| 14316 | +** RECOVER_STATE_WRITING state - during which tables recovered from the |
| 14317 | +** schema of the input database are populated with recovered data. |
| 14318 | +*/ |
| 14319 | +static int recoverWriteDataStep(sqlite3_recover *p){ |
| 14320 | + RecoverStateW1 *p1 = &p->w1; |
| 14321 | + sqlite3_stmt *pSel = p1->pSel; |
| 14322 | + sqlite3_value **apVal = p1->apVal; |
| 14323 | + |
| 14324 | + if( p->errCode==SQLITE_OK && p1->pTab==0 ){ |
| 14325 | + if( sqlite3_step(p1->pTbls)==SQLITE_ROW ){ |
| 14326 | + i64 iRoot = sqlite3_column_int64(p1->pTbls, 0); |
| 14327 | + p1->pTab = recoverFindTable(p, iRoot); |
| 14328 | + |
| 14329 | + recoverFinalize(p, p1->pInsert); |
| 14330 | + p1->pInsert = 0; |
| 14331 | + |
| 14332 | + /* If this table is unknown, return early. The caller will invoke this |
| 14333 | + ** function again and it will move on to the next table. */ |
| 14334 | + if( p1->pTab==0 ) return p->errCode; |
| 14335 | + |
| 14336 | + /* If this is the sqlite_sequence table, delete any rows added by |
| 14337 | + ** earlier INSERT statements on tables with AUTOINCREMENT primary |
| 14338 | + ** keys before recovering its contents. The p1->pTbls SELECT statement |
| 14339 | + ** is rigged to deliver "sqlite_sequence" last of all, so we don't |
| 14340 | + ** worry about it being modified after it is recovered. */ |
| 14341 | + if( sqlite3_stricmp("sqlite_sequence", p1->pTab->zTab)==0 ){ |
| 14342 | + recoverExec(p, p->dbOut, "DELETE FROM sqlite_sequence"); |
| 14343 | + recoverSqlCallback(p, "DELETE FROM sqlite_sequence"); |
| 14344 | + } |
| 14345 | + |
| 14346 | + /* Bind the root page of this table within the original database to |
| 14347 | + ** SELECT statement p1->pSel. The SELECT statement will then iterate |
| 14348 | + ** through cells that look like they belong to table pTab. */ |
| 14349 | + sqlite3_bind_int64(pSel, 1, iRoot); |
| 14350 | + |
| 14351 | + p1->nVal = 0; |
| 14352 | + p1->bHaveRowid = 0; |
| 14353 | + p1->iPrevPage = -1; |
| 14354 | + p1->iPrevCell = -1; |
| 14355 | + }else{ |
| 14356 | + return SQLITE_DONE; |
| 14357 | + } |
| 14358 | + } |
| 14359 | + assert( p->errCode!=SQLITE_OK || p1->pTab ); |
| 14360 | + |
| 14361 | + if( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ |
| 14362 | + RecoverTable *pTab = p1->pTab; |
| 14363 | + |
| 14364 | + i64 iPage = sqlite3_column_int64(pSel, 0); |
| 14365 | + int iCell = sqlite3_column_int(pSel, 1); |
| 14366 | + int iField = sqlite3_column_int(pSel, 2); |
| 14367 | + sqlite3_value *pVal = sqlite3_column_value(pSel, 3); |
| 14368 | + int bNewCell = (p1->iPrevPage!=iPage || p1->iPrevCell!=iCell); |
| 14369 | + |
| 14370 | + assert( bNewCell==0 || (iField==-1 || iField==0) ); |
| 14371 | + assert( bNewCell || iField==p1->nVal || p1->nVal==pTab->nCol ); |
| 14372 | + |
| 14373 | + if( bNewCell ){ |
| 14374 | + int ii = 0; |
| 14375 | + if( p1->nVal>=0 ){ |
| 14376 | + if( p1->pInsert==0 || p1->nVal!=p1->nInsert ){ |
| 14377 | + recoverFinalize(p, p1->pInsert); |
| 14378 | + p1->pInsert = recoverInsertStmt(p, pTab, p1->nVal); |
| 14379 | + p1->nInsert = p1->nVal; |
| 14380 | + } |
| 14381 | + if( p1->nVal>0 ){ |
| 14382 | + sqlite3_stmt *pInsert = p1->pInsert; |
| 14383 | + for(ii=0; ii<pTab->nCol; ii++){ |
| 14384 | + RecoverColumn *pCol = &pTab->aCol[ii]; |
| 14385 | + int iBind = pCol->iBind; |
| 14386 | + if( iBind>0 ){ |
| 14387 | + if( pCol->bIPK ){ |
| 14388 | + sqlite3_bind_int64(pInsert, iBind, p1->iRowid); |
| 14389 | + }else if( pCol->iField<p1->nVal ){ |
| 14390 | + recoverBindValue(p, pInsert, iBind, apVal[pCol->iField]); |
| 14391 | + } |
| 14392 | + } |
| 14393 | + } |
| 14394 | + if( p->bRecoverRowid && pTab->iRowidBind>0 && p1->bHaveRowid ){ |
| 14395 | + sqlite3_bind_int64(pInsert, pTab->iRowidBind, p1->iRowid); |
| 14396 | + } |
| 14397 | + if( SQLITE_ROW==sqlite3_step(pInsert) ){ |
| 14398 | + const char *z = (const char*)sqlite3_column_text(pInsert, 0); |
| 14399 | + recoverSqlCallback(p, z); |
| 14400 | + } |
| 14401 | + recoverReset(p, pInsert); |
| 14402 | + assert( p->errCode || pInsert ); |
| 14403 | + if( pInsert ) sqlite3_clear_bindings(pInsert); |
| 14404 | + } |
| 14405 | + } |
| 14406 | + |
| 14407 | + for(ii=0; ii<p1->nVal; ii++){ |
| 14408 | + sqlite3_value_free(apVal[ii]); |
| 14409 | + apVal[ii] = 0; |
| 14410 | + } |
| 14411 | + p1->nVal = -1; |
| 14412 | + p1->bHaveRowid = 0; |
| 14413 | + } |
| 14414 | + |
| 14415 | + if( iPage!=0 ){ |
| 14416 | + if( iField<0 ){ |
| 14417 | + p1->iRowid = sqlite3_column_int64(pSel, 3); |
| 14418 | + assert( p1->nVal==-1 ); |
| 14419 | + p1->nVal = 0; |
| 14420 | + p1->bHaveRowid = 1; |
| 14421 | + }else if( iField<pTab->nCol ){ |
| 14422 | + assert( apVal[iField]==0 ); |
| 14423 | + apVal[iField] = sqlite3_value_dup( pVal ); |
| 14424 | + if( apVal[iField]==0 ){ |
| 14425 | + recoverError(p, SQLITE_NOMEM, 0); |
| 14426 | + } |
| 14427 | + p1->nVal = iField+1; |
| 14428 | + } |
| 14429 | + p1->iPrevCell = iCell; |
| 14430 | + p1->iPrevPage = iPage; |
| 14431 | + } |
| 14432 | + }else{ |
| 14433 | + recoverReset(p, pSel); |
| 14434 | + p1->pTab = 0; |
| 14435 | + } |
| 14436 | + |
| 14437 | + return p->errCode; |
| 14438 | +} |
| 14439 | + |
| 14440 | +/* |
| 14441 | +** Initialize resources required by sqlite3_recover_step() in |
| 14442 | +** RECOVER_STATE_LOSTANDFOUND1 state - during which the set of pages not |
| 14443 | +** already allocated to a recovered schema element is determined. |
| 14444 | +*/ |
| 14445 | +static void recoverLostAndFound1Init(sqlite3_recover *p){ |
| 14446 | + RecoverStateLAF *pLaf = &p->laf; |
| 14447 | + sqlite3_stmt *pStmt = 0; |
| 14448 | + |
| 14449 | + assert( p->laf.pUsed==0 ); |
| 14450 | + pLaf->nPg = recoverPageCount(p); |
| 14451 | + pLaf->pUsed = recoverBitmapAlloc(p, pLaf->nPg); |
| 14452 | + |
| 14453 | + /* Prepare a statement to iterate through all pages that are part of any tree |
| 14454 | + ** in the recoverable part of the input database schema to the bitmap. And, |
| 14455 | + ** if !p->bFreelistCorrupt, add all pages that appear to be part of the |
| 14456 | + ** freelist. */ |
| 14457 | + pStmt = recoverPrepare( |
| 14458 | + p, p->dbOut, |
| 14459 | + "WITH trunk(pgno) AS (" |
| 14460 | + " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" |
| 14461 | + " UNION" |
| 14462 | + " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" |
| 14463 | + ")," |
| 14464 | + "trunkdata(pgno, data) AS (" |
| 14465 | + " SELECT pgno, getpage(pgno) FROM trunk" |
| 14466 | + ")," |
| 14467 | + "freelist(data, n, freepgno) AS (" |
| 14468 | + " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" |
| 14469 | + " UNION ALL" |
| 14470 | + " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" |
| 14471 | + ")," |
| 14472 | + "" |
| 14473 | + "roots(r) AS (" |
| 14474 | + " SELECT 1 UNION ALL" |
| 14475 | + " SELECT rootpage FROM recovery.schema WHERE rootpage>0" |
| 14476 | + ")," |
| 14477 | + "used(page) AS (" |
| 14478 | + " SELECT r FROM roots" |
| 14479 | + " UNION" |
| 14480 | + " SELECT child FROM sqlite_dbptr('getpage()'), used " |
| 14481 | + " WHERE pgno=page" |
| 14482 | + ") " |
| 14483 | + "SELECT page FROM used" |
| 14484 | + " UNION ALL " |
| 14485 | + "SELECT freepgno FROM freelist WHERE NOT ?" |
| 14486 | + ); |
| 14487 | + if( pStmt ) sqlite3_bind_int(pStmt, 1, p->bFreelistCorrupt); |
| 14488 | + pLaf->pUsedPages = pStmt; |
| 14489 | +} |
| 14490 | + |
| 14491 | +/* |
| 14492 | +** Perform one step (sqlite3_recover_step()) of work for the connection |
| 14493 | +** passed as the only argument, which is guaranteed to be in |
| 14494 | +** RECOVER_STATE_LOSTANDFOUND1 state - during which the set of pages not |
| 14495 | +** already allocated to a recovered schema element is determined. |
| 14496 | +*/ |
| 14497 | +static int recoverLostAndFound1Step(sqlite3_recover *p){ |
| 14498 | + RecoverStateLAF *pLaf = &p->laf; |
| 14499 | + int rc = p->errCode; |
| 14500 | + if( rc==SQLITE_OK ){ |
| 14501 | + rc = sqlite3_step(pLaf->pUsedPages); |
| 14502 | + if( rc==SQLITE_ROW ){ |
| 14503 | + i64 iPg = sqlite3_column_int64(pLaf->pUsedPages, 0); |
| 14504 | + recoverBitmapSet(pLaf->pUsed, iPg); |
| 14505 | + rc = SQLITE_OK; |
| 14506 | + }else{ |
| 14507 | + recoverFinalize(p, pLaf->pUsedPages); |
| 14508 | + pLaf->pUsedPages = 0; |
| 14509 | + } |
| 14510 | + } |
| 14511 | + return rc; |
| 14512 | +} |
| 14513 | + |
| 14514 | +/* |
| 14515 | +** Initialize resources required by RECOVER_STATE_LOSTANDFOUND2 |
| 14516 | +** state - during which the pages identified in RECOVER_STATE_LOSTANDFOUND1 |
| 14517 | +** are sorted into sets that likely belonged to the same database tree. |
| 14518 | +*/ |
| 14519 | +static void recoverLostAndFound2Init(sqlite3_recover *p){ |
| 14520 | + RecoverStateLAF *pLaf = &p->laf; |
| 14521 | + |
| 14522 | + assert( p->laf.pAllAndParent==0 ); |
| 14523 | + assert( p->laf.pMapInsert==0 ); |
| 14524 | + assert( p->laf.pMaxField==0 ); |
| 14525 | + assert( p->laf.nMaxField==0 ); |
| 14526 | + |
| 14527 | + pLaf->pMapInsert = recoverPrepare(p, p->dbOut, |
| 14528 | + "INSERT OR IGNORE INTO recovery.map(pgno, parent) VALUES(?, ?)" |
| 14529 | + ); |
| 14530 | + pLaf->pAllAndParent = recoverPreparePrintf(p, p->dbOut, |
| 14531 | + "WITH RECURSIVE seq(ii) AS (" |
| 14532 | + " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" |
| 14533 | + ")" |
| 14534 | + "SELECT pgno, child FROM sqlite_dbptr('getpage()') " |
| 14535 | + " UNION ALL " |
| 14536 | + "SELECT NULL, ii FROM seq", p->laf.nPg |
| 14537 | + ); |
| 14538 | + pLaf->pMaxField = recoverPreparePrintf(p, p->dbOut, |
| 14539 | + "SELECT max(field)+1 FROM sqlite_dbdata('getpage') WHERE pgno = ?" |
| 14540 | + ); |
| 14541 | +} |
| 14542 | + |
| 14543 | +/* |
| 14544 | +** Perform one step (sqlite3_recover_step()) of work for the connection |
| 14545 | +** passed as the only argument, which is guaranteed to be in |
| 14546 | +** RECOVER_STATE_LOSTANDFOUND2 state - during which the pages identified |
| 14547 | +** in RECOVER_STATE_LOSTANDFOUND1 are sorted into sets that likely belonged |
| 14548 | +** to the same database tree. |
| 14549 | +*/ |
| 14550 | +static int recoverLostAndFound2Step(sqlite3_recover *p){ |
| 14551 | + RecoverStateLAF *pLaf = &p->laf; |
| 14552 | + if( p->errCode==SQLITE_OK ){ |
| 14553 | + int res = sqlite3_step(pLaf->pAllAndParent); |
| 14554 | + if( res==SQLITE_ROW ){ |
| 14555 | + i64 iChild = sqlite3_column_int(pLaf->pAllAndParent, 1); |
| 14556 | + if( recoverBitmapQuery(pLaf->pUsed, iChild)==0 ){ |
| 14557 | + sqlite3_bind_int64(pLaf->pMapInsert, 1, iChild); |
| 14558 | + sqlite3_bind_value(pLaf->pMapInsert, 2, |
| 14559 | + sqlite3_column_value(pLaf->pAllAndParent, 0) |
| 14560 | + ); |
| 14561 | + sqlite3_step(pLaf->pMapInsert); |
| 14562 | + recoverReset(p, pLaf->pMapInsert); |
| 14563 | + sqlite3_bind_int64(pLaf->pMaxField, 1, iChild); |
| 14564 | + if( SQLITE_ROW==sqlite3_step(pLaf->pMaxField) ){ |
| 14565 | + int nMax = sqlite3_column_int(pLaf->pMaxField, 0); |
| 14566 | + if( nMax>pLaf->nMaxField ) pLaf->nMaxField = nMax; |
| 14567 | + } |
| 14568 | + recoverReset(p, pLaf->pMaxField); |
| 14569 | + } |
| 14570 | + }else{ |
| 14571 | + recoverFinalize(p, pLaf->pAllAndParent); |
| 14572 | + pLaf->pAllAndParent =0; |
| 14573 | + return SQLITE_DONE; |
| 14574 | + } |
| 14575 | + } |
| 14576 | + return p->errCode; |
| 14577 | +} |
| 14578 | + |
| 14579 | +/* |
| 14580 | +** Free all resources allocated as part of sqlite3_recover_step() calls |
| 14581 | +** in one of the RECOVER_STATE_LOSTANDFOUND[123] states. |
| 14582 | +*/ |
| 14583 | +static void recoverLostAndFoundCleanup(sqlite3_recover *p){ |
| 14584 | + recoverBitmapFree(p->laf.pUsed); |
| 14585 | + p->laf.pUsed = 0; |
| 14586 | + sqlite3_finalize(p->laf.pUsedPages); |
| 14587 | + sqlite3_finalize(p->laf.pAllAndParent); |
| 14588 | + sqlite3_finalize(p->laf.pMapInsert); |
| 14589 | + sqlite3_finalize(p->laf.pMaxField); |
| 14590 | + sqlite3_finalize(p->laf.pFindRoot); |
| 14591 | + sqlite3_finalize(p->laf.pInsert); |
| 14592 | + sqlite3_finalize(p->laf.pAllPage); |
| 14593 | + sqlite3_finalize(p->laf.pPageData); |
| 14594 | + p->laf.pUsedPages = 0; |
| 14595 | + p->laf.pAllAndParent = 0; |
| 14596 | + p->laf.pMapInsert = 0; |
| 14597 | + p->laf.pMaxField = 0; |
| 14598 | + p->laf.pFindRoot = 0; |
| 14599 | + p->laf.pInsert = 0; |
| 14600 | + p->laf.pAllPage = 0; |
| 14601 | + p->laf.pPageData = 0; |
| 14602 | + sqlite3_free(p->laf.apVal); |
| 14603 | + p->laf.apVal = 0; |
| 14604 | +} |
| 14605 | + |
| 14606 | +/* |
| 14607 | +** Free all resources allocated as part of sqlite3_recover_step() calls. |
| 14608 | +*/ |
| 14609 | +static void recoverFinalCleanup(sqlite3_recover *p){ |
| 14610 | + RecoverTable *pTab = 0; |
| 14611 | + RecoverTable *pNext = 0; |
| 14612 | + |
| 14613 | + recoverWriteDataCleanup(p); |
| 14614 | + recoverLostAndFoundCleanup(p); |
| 14615 | + |
| 14616 | + for(pTab=p->pTblList; pTab; pTab=pNext){ |
| 14617 | + pNext = pTab->pNext; |
| 14618 | + sqlite3_free(pTab); |
| 14619 | + } |
| 14620 | + p->pTblList = 0; |
| 14621 | + sqlite3_finalize(p->pGetPage); |
| 14622 | + p->pGetPage = 0; |
| 14623 | + |
| 14624 | + { |
| 14625 | +#ifndef NDEBUG |
| 14626 | + int res = |
| 14627 | +#endif |
| 14628 | + sqlite3_close(p->dbOut); |
| 14629 | + assert( res==SQLITE_OK ); |
| 14630 | + } |
| 14631 | + p->dbOut = 0; |
| 14632 | +} |
| 14633 | + |
| 14634 | +/* |
| 14635 | +** Decode and return an unsigned 16-bit big-endian integer value from |
| 14636 | +** buffer a[]. |
| 14637 | +*/ |
| 14638 | +static u32 recoverGetU16(const u8 *a){ |
| 14639 | + return (((u32)a[0])<<8) + ((u32)a[1]); |
| 14640 | +} |
| 14641 | + |
| 14642 | +/* |
| 14643 | +** Decode and return an unsigned 32-bit big-endian integer value from |
| 14644 | +** buffer a[]. |
| 14645 | +*/ |
| 14646 | +static u32 recoverGetU32(const u8 *a){ |
| 14647 | + return (((u32)a[0])<<24) + (((u32)a[1])<<16) + (((u32)a[2])<<8) + ((u32)a[3]); |
| 14648 | +} |
| 14649 | + |
| 14650 | +/* |
| 14651 | +** Decode an SQLite varint from buffer a[]. Write the decoded value to (*pVal) |
| 14652 | +** and return the number of bytes consumed. |
| 14653 | +*/ |
| 14654 | +static int recoverGetVarint(const u8 *a, i64 *pVal){ |
| 14655 | + sqlite3_uint64 u = 0; |
| 14656 | + int i; |
| 14657 | + for(i=0; i<8; i++){ |
| 14658 | + u = (u<<7) + (a[i]&0x7f); |
| 14659 | + if( (a[i]&0x80)==0 ){ *pVal = (sqlite3_int64)u; return i+1; } |
| 14660 | + } |
| 14661 | + u = (u<<8) + (a[i]&0xff); |
| 14662 | + *pVal = (sqlite3_int64)u; |
| 14663 | + return 9; |
| 14664 | +} |
| 14665 | + |
| 14666 | +/* |
| 14667 | +** The second argument points to a buffer n bytes in size. If this buffer |
| 14668 | +** or a prefix thereof appears to contain a well-formed SQLite b-tree page, |
| 14669 | +** return the page-size in bytes. Otherwise, if the buffer does not |
| 14670 | +** appear to contain a well-formed b-tree page, return 0. |
| 14671 | +*/ |
| 14672 | +static int recoverIsValidPage(u8 *aTmp, const u8 *a, int n){ |
| 14673 | + u8 *aUsed = aTmp; |
| 14674 | + int nFrag = 0; |
| 14675 | + int nActual = 0; |
| 14676 | + int iFree = 0; |
| 14677 | + int nCell = 0; /* Number of cells on page */ |
| 14678 | + int iCellOff = 0; /* Offset of cell array in page */ |
| 14679 | + int iContent = 0; |
| 14680 | + int eType = 0; |
| 14681 | + int ii = 0; |
| 14682 | + |
| 14683 | + eType = (int)a[0]; |
| 14684 | + if( eType!=0x02 && eType!=0x05 && eType!=0x0A && eType!=0x0D ) return 0; |
| 14685 | + |
| 14686 | + iFree = (int)recoverGetU16(&a[1]); |
| 14687 | + nCell = (int)recoverGetU16(&a[3]); |
| 14688 | + iContent = (int)recoverGetU16(&a[5]); |
| 14689 | + if( iContent==0 ) iContent = 65536; |
| 14690 | + nFrag = (int)a[7]; |
| 14691 | + |
| 14692 | + if( iContent>n ) return 0; |
| 14693 | + |
| 14694 | + memset(aUsed, 0, n); |
| 14695 | + memset(aUsed, 0xFF, iContent); |
| 14696 | + |
| 14697 | + /* Follow the free-list. This is the same format for all b-tree pages. */ |
| 14698 | + if( iFree && iFree<=iContent ) return 0; |
| 14699 | + while( iFree ){ |
| 14700 | + int iNext = 0; |
| 14701 | + int nByte = 0; |
| 14702 | + if( iFree>(n-4) ) return 0; |
| 14703 | + iNext = recoverGetU16(&a[iFree]); |
| 14704 | + nByte = recoverGetU16(&a[iFree+2]); |
| 14705 | + if( iFree+nByte>n ) return 0; |
| 14706 | + if( iNext && iNext<iFree+nByte ) return 0; |
| 14707 | + memset(&aUsed[iFree], 0xFF, nByte); |
| 14708 | + iFree = iNext; |
| 14709 | + } |
| 14710 | + |
| 14711 | + /* Run through the cells */ |
| 14712 | + if( eType==0x02 || eType==0x05 ){ |
| 14713 | + iCellOff = 12; |
| 14714 | + }else{ |
| 14715 | + iCellOff = 8; |
| 14716 | + } |
| 14717 | + if( (iCellOff + 2*nCell)>iContent ) return 0; |
| 14718 | + for(ii=0; ii<nCell; ii++){ |
| 14719 | + int iByte; |
| 14720 | + i64 nPayload = 0; |
| 14721 | + int nByte = 0; |
| 14722 | + int iOff = recoverGetU16(&a[iCellOff + 2*ii]); |
| 14723 | + if( iOff<iContent || iOff>n ){ |
| 14724 | + return 0; |
| 14725 | + } |
| 14726 | + if( eType==0x05 || eType==0x02 ) nByte += 4; |
| 14727 | + nByte += recoverGetVarint(&a[iOff+nByte], &nPayload); |
| 14728 | + if( eType==0x0D ){ |
| 14729 | + i64 dummy = 0; |
| 14730 | + nByte += recoverGetVarint(&a[iOff+nByte], &dummy); |
| 14731 | + } |
| 14732 | + if( eType!=0x05 ){ |
| 14733 | + int X = (eType==0x0D) ? n-35 : (((n-12)*64/255)-23); |
| 14734 | + int M = ((n-12)*32/255)-23; |
| 14735 | + int K = M+((nPayload-M)%(n-4)); |
| 14736 | + |
| 14737 | + if( nPayload<X ){ |
| 14738 | + nByte += nPayload; |
| 14739 | + }else if( K<=X ){ |
| 14740 | + nByte += K+4; |
| 14741 | + }else{ |
| 14742 | + nByte += M+4; |
| 14743 | + } |
| 14744 | + } |
| 14745 | + |
| 14746 | + if( iOff+nByte>n ){ |
| 14747 | + return 0; |
| 14748 | + } |
| 14749 | + for(iByte=iOff; iByte<(iOff+nByte); iByte++){ |
| 14750 | + if( aUsed[iByte]!=0 ){ |
| 14751 | + return 0; |
| 14752 | + } |
| 14753 | + aUsed[iByte] = 0xFF; |
| 14754 | + } |
| 14755 | + } |
| 14756 | + |
| 14757 | + nActual = 0; |
| 14758 | + for(ii=0; ii<n; ii++){ |
| 14759 | + if( aUsed[ii]==0 ) nActual++; |
| 14760 | + } |
| 14761 | + return (nActual==nFrag); |
| 14762 | +} |
| 14763 | + |
| 14764 | + |
| 14765 | +static int recoverVfsClose(sqlite3_file*); |
| 14766 | +static int recoverVfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); |
| 14767 | +static int recoverVfsWrite(sqlite3_file*, const void*, int, sqlite3_int64); |
| 14768 | +static int recoverVfsTruncate(sqlite3_file*, sqlite3_int64 size); |
| 14769 | +static int recoverVfsSync(sqlite3_file*, int flags); |
| 14770 | +static int recoverVfsFileSize(sqlite3_file*, sqlite3_int64 *pSize); |
| 14771 | +static int recoverVfsLock(sqlite3_file*, int); |
| 14772 | +static int recoverVfsUnlock(sqlite3_file*, int); |
| 14773 | +static int recoverVfsCheckReservedLock(sqlite3_file*, int *pResOut); |
| 14774 | +static int recoverVfsFileControl(sqlite3_file*, int op, void *pArg); |
| 14775 | +static int recoverVfsSectorSize(sqlite3_file*); |
| 14776 | +static int recoverVfsDeviceCharacteristics(sqlite3_file*); |
| 14777 | +static int recoverVfsShmMap(sqlite3_file*, int, int, int, void volatile**); |
| 14778 | +static int recoverVfsShmLock(sqlite3_file*, int offset, int n, int flags); |
| 14779 | +static void recoverVfsShmBarrier(sqlite3_file*); |
| 14780 | +static int recoverVfsShmUnmap(sqlite3_file*, int deleteFlag); |
| 14781 | +static int recoverVfsFetch(sqlite3_file*, sqlite3_int64, int, void**); |
| 14782 | +static int recoverVfsUnfetch(sqlite3_file *pFd, sqlite3_int64 iOff, void *p); |
| 14783 | + |
| 14784 | +static sqlite3_io_methods recover_methods = { |
| 14785 | + 2, /* iVersion */ |
| 14786 | + recoverVfsClose, |
| 14787 | + recoverVfsRead, |
| 14788 | + recoverVfsWrite, |
| 14789 | + recoverVfsTruncate, |
| 14790 | + recoverVfsSync, |
| 14791 | + recoverVfsFileSize, |
| 14792 | + recoverVfsLock, |
| 14793 | + recoverVfsUnlock, |
| 14794 | + recoverVfsCheckReservedLock, |
| 14795 | + recoverVfsFileControl, |
| 14796 | + recoverVfsSectorSize, |
| 14797 | + recoverVfsDeviceCharacteristics, |
| 14798 | + recoverVfsShmMap, |
| 14799 | + recoverVfsShmLock, |
| 14800 | + recoverVfsShmBarrier, |
| 14801 | + recoverVfsShmUnmap, |
| 14802 | + recoverVfsFetch, |
| 14803 | + recoverVfsUnfetch |
| 14804 | +}; |
| 14805 | + |
| 14806 | +static int recoverVfsClose(sqlite3_file *pFd){ |
| 14807 | + assert( pFd->pMethods!=&recover_methods ); |
| 14808 | + return pFd->pMethods->xClose(pFd); |
| 14809 | +} |
| 14810 | + |
| 14811 | +/* |
| 14812 | +** Write value v to buffer a[] as a 16-bit big-endian unsigned integer. |
| 14813 | +*/ |
| 14814 | +static void recoverPutU16(u8 *a, u32 v){ |
| 14815 | + a[0] = (v>>8) & 0x00FF; |
| 14816 | + a[1] = (v>>0) & 0x00FF; |
| 14817 | +} |
| 14818 | + |
| 14819 | +/* |
| 14820 | +** Write value v to buffer a[] as a 32-bit big-endian unsigned integer. |
| 14821 | +*/ |
| 14822 | +static void recoverPutU32(u8 *a, u32 v){ |
| 14823 | + a[0] = (v>>24) & 0x00FF; |
| 14824 | + a[1] = (v>>16) & 0x00FF; |
| 14825 | + a[2] = (v>>8) & 0x00FF; |
| 14826 | + a[3] = (v>>0) & 0x00FF; |
| 14827 | +} |
| 14828 | + |
| 14829 | +/* |
| 14830 | +** Detect the page-size of the database opened by file-handle pFd by |
| 14831 | +** searching the first part of the file for a well-formed SQLite b-tree |
| 14832 | +** page. If parameter nReserve is non-zero, then as well as searching for |
| 14833 | +** a b-tree page with zero reserved bytes, this function searches for one |
| 14834 | +** with nReserve reserved bytes at the end of it. |
| 14835 | +** |
| 14836 | +** If successful, set variable p->detected_pgsz to the detected page-size |
| 14837 | +** in bytes and return SQLITE_OK. Or, if no error occurs but no valid page |
| 14838 | +** can be found, return SQLITE_OK but leave p->detected_pgsz set to 0. Or, |
| 14839 | +** if an error occurs (e.g. an IO or OOM error), then an SQLite error code |
| 14840 | +** is returned. The final value of p->detected_pgsz is undefined in this |
| 14841 | +** case. |
| 14842 | +*/ |
| 14843 | +static int recoverVfsDetectPagesize( |
| 14844 | + sqlite3_recover *p, /* Recover handle */ |
| 14845 | + sqlite3_file *pFd, /* File-handle open on input database */ |
| 14846 | + u32 nReserve, /* Possible nReserve value */ |
| 14847 | + i64 nSz /* Size of database file in bytes */ |
| 14848 | +){ |
| 14849 | + int rc = SQLITE_OK; |
| 14850 | + const int nMin = 512; |
| 14851 | + const int nMax = 65536; |
| 14852 | + const int nMaxBlk = 4; |
| 14853 | + u32 pgsz = 0; |
| 14854 | + int iBlk = 0; |
| 14855 | + u8 *aPg = 0; |
| 14856 | + u8 *aTmp = 0; |
| 14857 | + int nBlk = 0; |
| 14858 | + |
| 14859 | + aPg = (u8*)sqlite3_malloc(2*nMax); |
| 14860 | + if( aPg==0 ) return SQLITE_NOMEM; |
| 14861 | + aTmp = &aPg[nMax]; |
| 14862 | + |
| 14863 | + nBlk = (nSz+nMax-1)/nMax; |
| 14864 | + if( nBlk>nMaxBlk ) nBlk = nMaxBlk; |
| 14865 | + |
| 14866 | + do { |
| 14867 | + for(iBlk=0; rc==SQLITE_OK && iBlk<nBlk; iBlk++){ |
| 14868 | + int nByte = (nSz>=((iBlk+1)*nMax)) ? nMax : (nSz % nMax); |
| 14869 | + memset(aPg, 0, nMax); |
| 14870 | + rc = pFd->pMethods->xRead(pFd, aPg, nByte, iBlk*nMax); |
| 14871 | + if( rc==SQLITE_OK ){ |
| 14872 | + int pgsz2; |
| 14873 | + for(pgsz2=(pgsz ? pgsz*2 : nMin); pgsz2<=nMax; pgsz2=pgsz2*2){ |
| 14874 | + int iOff; |
| 14875 | + for(iOff=0; iOff<nMax; iOff+=pgsz2){ |
| 14876 | + if( recoverIsValidPage(aTmp, &aPg[iOff], pgsz2-nReserve) ){ |
| 14877 | + pgsz = pgsz2; |
| 14878 | + break; |
| 14879 | + } |
| 14880 | + } |
| 14881 | + } |
| 14882 | + } |
| 14883 | + } |
| 14884 | + if( pgsz>(u32)p->detected_pgsz ){ |
| 14885 | + p->detected_pgsz = pgsz; |
| 14886 | + p->nReserve = nReserve; |
| 14887 | + } |
| 14888 | + if( nReserve==0 ) break; |
| 14889 | + nReserve = 0; |
| 14890 | + }while( 1 ); |
| 14891 | + |
| 14892 | + p->detected_pgsz = pgsz; |
| 14893 | + sqlite3_free(aPg); |
| 14894 | + return rc; |
| 14895 | +} |
| 14896 | + |
| 14897 | +/* |
| 14898 | +** The xRead() method of the wrapper VFS. This is used to intercept calls |
| 14899 | +** to read page 1 of the input database. |
| 14900 | +*/ |
| 14901 | +static int recoverVfsRead(sqlite3_file *pFd, void *aBuf, int nByte, i64 iOff){ |
| 14902 | + int rc = SQLITE_OK; |
| 14903 | + if( pFd->pMethods==&recover_methods ){ |
| 14904 | + pFd->pMethods = recover_g.pMethods; |
| 14905 | + rc = pFd->pMethods->xRead(pFd, aBuf, nByte, iOff); |
| 14906 | + if( nByte==16 ){ |
| 14907 | + sqlite3_randomness(16, aBuf); |
| 14908 | + }else |
| 14909 | + if( rc==SQLITE_OK && iOff==0 && nByte>=108 ){ |
| 14910 | + /* Ensure that the database has a valid header file. The only fields |
| 14911 | + ** that really matter to recovery are: |
| 14912 | + ** |
| 14913 | + ** + Database page size (16-bits at offset 16) |
| 14914 | + ** + Size of db in pages (32-bits at offset 28) |
| 14915 | + ** + Database encoding (32-bits at offset 56) |
| 14916 | + ** |
| 14917 | + ** Also preserved are: |
| 14918 | + ** |
| 14919 | + ** + first freelist page (32-bits at offset 32) |
| 14920 | + ** + size of freelist (32-bits at offset 36) |
| 14921 | + ** |
| 14922 | + ** We also try to preserve the auto-vacuum, incr-value, user-version |
| 14923 | + ** and application-id fields - all 32 bit quantities at offsets |
| 14924 | + ** 52, 60, 64 and 68. All other fields are set to known good values. |
| 14925 | + ** |
| 14926 | + ** Byte offset 105 should also contain the page-size as a 16-bit |
| 14927 | + ** integer. |
| 14928 | + */ |
| 14929 | + const int aPreserve[] = {32, 36, 52, 60, 64, 68}; |
| 14930 | + u8 aHdr[108] = { |
| 14931 | + 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, |
| 14932 | + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00, |
| 14933 | + 0xFF, 0xFF, 0x01, 0x01, 0x00, 0x40, 0x20, 0x20, |
| 14934 | + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, |
| 14935 | + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 14936 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, |
| 14937 | + 0x00, 0x00, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, |
| 14938 | + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 14939 | + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 14940 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 14941 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 14942 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 14943 | + 0x00, 0x2e, 0x5b, 0x30, |
| 14944 | + |
| 14945 | + 0x0D, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00 |
| 14946 | + }; |
| 14947 | + u8 *a = (u8*)aBuf; |
| 14948 | + |
| 14949 | + u32 pgsz = recoverGetU16(&a[16]); |
| 14950 | + u32 nReserve = a[20]; |
| 14951 | + u32 enc = recoverGetU32(&a[56]); |
| 14952 | + u32 dbsz = 0; |
| 14953 | + i64 dbFileSize = 0; |
| 14954 | + int ii; |
| 14955 | + sqlite3_recover *p = recover_g.p; |
| 14956 | + |
| 14957 | + if( pgsz==0x01 ) pgsz = 65536; |
| 14958 | + rc = pFd->pMethods->xFileSize(pFd, &dbFileSize); |
| 14959 | + |
| 14960 | + if( rc==SQLITE_OK && p->detected_pgsz==0 ){ |
| 14961 | + rc = recoverVfsDetectPagesize(p, pFd, nReserve, dbFileSize); |
| 14962 | + } |
| 14963 | + if( p->detected_pgsz ){ |
| 14964 | + pgsz = p->detected_pgsz; |
| 14965 | + nReserve = p->nReserve; |
| 14966 | + } |
| 14967 | + |
| 14968 | + if( pgsz ){ |
| 14969 | + dbsz = dbFileSize / pgsz; |
| 14970 | + } |
| 14971 | + if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF16BE && enc!=SQLITE_UTF16LE ){ |
| 14972 | + enc = SQLITE_UTF8; |
| 14973 | + } |
| 14974 | + |
| 14975 | + sqlite3_free(p->pPage1Cache); |
| 14976 | + p->pPage1Cache = 0; |
| 14977 | + p->pPage1Disk = 0; |
| 14978 | + |
| 14979 | + p->pgsz = nByte; |
| 14980 | + p->pPage1Cache = (u8*)recoverMalloc(p, nByte*2); |
| 14981 | + if( p->pPage1Cache ){ |
| 14982 | + p->pPage1Disk = &p->pPage1Cache[nByte]; |
| 14983 | + memcpy(p->pPage1Disk, aBuf, nByte); |
| 14984 | + |
| 14985 | + recoverPutU32(&aHdr[28], dbsz); |
| 14986 | + recoverPutU32(&aHdr[56], enc); |
| 14987 | + recoverPutU16(&aHdr[105], pgsz-nReserve); |
| 14988 | + if( pgsz==65536 ) pgsz = 1; |
| 14989 | + recoverPutU16(&aHdr[16], pgsz); |
| 14990 | + aHdr[20] = nReserve; |
| 14991 | + for(ii=0; ii<sizeof(aPreserve)/sizeof(aPreserve[0]); ii++){ |
| 14992 | + memcpy(&aHdr[aPreserve[ii]], &a[aPreserve[ii]], 4); |
| 14993 | + } |
| 14994 | + memcpy(aBuf, aHdr, sizeof(aHdr)); |
| 14995 | + memset(&((u8*)aBuf)[sizeof(aHdr)], 0, nByte-sizeof(aHdr)); |
| 14996 | + |
| 14997 | + memcpy(p->pPage1Cache, aBuf, nByte); |
| 14998 | + }else{ |
| 14999 | + rc = p->errCode; |
| 15000 | + } |
| 15001 | + |
| 15002 | + } |
| 15003 | + pFd->pMethods = &recover_methods; |
| 15004 | + }else{ |
| 15005 | + rc = pFd->pMethods->xRead(pFd, aBuf, nByte, iOff); |
| 15006 | + } |
| 15007 | + return rc; |
| 15008 | +} |
| 15009 | + |
| 15010 | +/* |
| 15011 | +** Used to make sqlite3_io_methods wrapper methods less verbose. |
| 15012 | +*/ |
| 15013 | +#define RECOVER_VFS_WRAPPER(code) \ |
| 15014 | + int rc = SQLITE_OK; \ |
| 15015 | + if( pFd->pMethods==&recover_methods ){ \ |
| 15016 | + pFd->pMethods = recover_g.pMethods; \ |
| 15017 | + rc = code; \ |
| 15018 | + pFd->pMethods = &recover_methods; \ |
| 15019 | + }else{ \ |
| 15020 | + rc = code; \ |
| 15021 | + } \ |
| 15022 | + return rc; |
| 15023 | + |
| 15024 | +/* |
| 15025 | +** Methods of the wrapper VFS. All methods except for xRead() and xClose() |
| 15026 | +** simply uninstall the sqlite3_io_methods wrapper, invoke the equivalent |
| 15027 | +** method on the lower level VFS, then reinstall the wrapper before returning. |
| 15028 | +** Those that return an integer value use the RECOVER_VFS_WRAPPER macro. |
| 15029 | +*/ |
| 15030 | +static int recoverVfsWrite( |
| 15031 | + sqlite3_file *pFd, const void *aBuf, int nByte, i64 iOff |
| 15032 | +){ |
| 15033 | + RECOVER_VFS_WRAPPER ( |
| 15034 | + pFd->pMethods->xWrite(pFd, aBuf, nByte, iOff) |
| 15035 | + ); |
| 15036 | +} |
| 15037 | +static int recoverVfsTruncate(sqlite3_file *pFd, sqlite3_int64 size){ |
| 15038 | + RECOVER_VFS_WRAPPER ( |
| 15039 | + pFd->pMethods->xTruncate(pFd, size) |
| 15040 | + ); |
| 15041 | +} |
| 15042 | +static int recoverVfsSync(sqlite3_file *pFd, int flags){ |
| 15043 | + RECOVER_VFS_WRAPPER ( |
| 15044 | + pFd->pMethods->xSync(pFd, flags) |
| 15045 | + ); |
| 15046 | +} |
| 15047 | +static int recoverVfsFileSize(sqlite3_file *pFd, sqlite3_int64 *pSize){ |
| 15048 | + RECOVER_VFS_WRAPPER ( |
| 15049 | + pFd->pMethods->xFileSize(pFd, pSize) |
| 15050 | + ); |
| 15051 | +} |
| 15052 | +static int recoverVfsLock(sqlite3_file *pFd, int eLock){ |
| 15053 | + RECOVER_VFS_WRAPPER ( |
| 15054 | + pFd->pMethods->xLock(pFd, eLock) |
| 15055 | + ); |
| 15056 | +} |
| 15057 | +static int recoverVfsUnlock(sqlite3_file *pFd, int eLock){ |
| 15058 | + RECOVER_VFS_WRAPPER ( |
| 15059 | + pFd->pMethods->xUnlock(pFd, eLock) |
| 15060 | + ); |
| 15061 | +} |
| 15062 | +static int recoverVfsCheckReservedLock(sqlite3_file *pFd, int *pResOut){ |
| 15063 | + RECOVER_VFS_WRAPPER ( |
| 15064 | + pFd->pMethods->xCheckReservedLock(pFd, pResOut) |
| 15065 | + ); |
| 15066 | +} |
| 15067 | +static int recoverVfsFileControl(sqlite3_file *pFd, int op, void *pArg){ |
| 15068 | + RECOVER_VFS_WRAPPER ( |
| 15069 | + (pFd->pMethods ? pFd->pMethods->xFileControl(pFd, op, pArg) : SQLITE_NOTFOUND) |
| 15070 | + ); |
| 15071 | +} |
| 15072 | +static int recoverVfsSectorSize(sqlite3_file *pFd){ |
| 15073 | + RECOVER_VFS_WRAPPER ( |
| 15074 | + pFd->pMethods->xSectorSize(pFd) |
| 15075 | + ); |
| 15076 | +} |
| 15077 | +static int recoverVfsDeviceCharacteristics(sqlite3_file *pFd){ |
| 15078 | + RECOVER_VFS_WRAPPER ( |
| 15079 | + pFd->pMethods->xDeviceCharacteristics(pFd) |
| 15080 | + ); |
| 15081 | +} |
| 15082 | +static int recoverVfsShmMap( |
| 15083 | + sqlite3_file *pFd, int iPg, int pgsz, int bExtend, void volatile **pp |
| 15084 | +){ |
| 15085 | + RECOVER_VFS_WRAPPER ( |
| 15086 | + pFd->pMethods->xShmMap(pFd, iPg, pgsz, bExtend, pp) |
| 15087 | + ); |
| 15088 | +} |
| 15089 | +static int recoverVfsShmLock(sqlite3_file *pFd, int offset, int n, int flags){ |
| 15090 | + RECOVER_VFS_WRAPPER ( |
| 15091 | + pFd->pMethods->xShmLock(pFd, offset, n, flags) |
| 15092 | + ); |
| 15093 | +} |
| 15094 | +static void recoverVfsShmBarrier(sqlite3_file *pFd){ |
| 15095 | + if( pFd->pMethods==&recover_methods ){ |
| 15096 | + pFd->pMethods = recover_g.pMethods; |
| 15097 | + pFd->pMethods->xShmBarrier(pFd); |
| 15098 | + pFd->pMethods = &recover_methods; |
| 15099 | + }else{ |
| 15100 | + pFd->pMethods->xShmBarrier(pFd); |
| 15101 | + } |
| 15102 | +} |
| 15103 | +static int recoverVfsShmUnmap(sqlite3_file *pFd, int deleteFlag){ |
| 15104 | + RECOVER_VFS_WRAPPER ( |
| 15105 | + pFd->pMethods->xShmUnmap(pFd, deleteFlag) |
| 15106 | + ); |
| 15107 | +} |
| 15108 | + |
| 15109 | +static int recoverVfsFetch( |
| 15110 | + sqlite3_file *pFd, |
| 15111 | + sqlite3_int64 iOff, |
| 15112 | + int iAmt, |
| 15113 | + void **pp |
| 15114 | +){ |
| 15115 | + *pp = 0; |
| 15116 | + return SQLITE_OK; |
| 15117 | +} |
| 15118 | +static int recoverVfsUnfetch(sqlite3_file *pFd, sqlite3_int64 iOff, void *p){ |
| 15119 | + return SQLITE_OK; |
| 15120 | +} |
| 15121 | + |
| 15122 | +/* |
| 15123 | +** Install the VFS wrapper around the file-descriptor open on the input |
| 15124 | +** database for recover handle p. Mutex RECOVER_MUTEX_ID must be held |
| 15125 | +** when this function is called. |
| 15126 | +*/ |
| 15127 | +static void recoverInstallWrapper(sqlite3_recover *p){ |
| 15128 | + sqlite3_file *pFd = 0; |
| 15129 | + assert( recover_g.pMethods==0 ); |
| 15130 | + recoverAssertMutexHeld(); |
| 15131 | + sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd); |
| 15132 | + assert( pFd==0 || pFd->pMethods!=&recover_methods ); |
| 15133 | + if( pFd && pFd->pMethods ){ |
| 15134 | + int iVersion = 1 + (pFd->pMethods->iVersion>1 && pFd->pMethods->xShmMap!=0); |
| 15135 | + recover_g.pMethods = pFd->pMethods; |
| 15136 | + recover_g.p = p; |
| 15137 | + recover_methods.iVersion = iVersion; |
| 15138 | + pFd->pMethods = &recover_methods; |
| 15139 | + } |
| 15140 | +} |
| 15141 | + |
| 15142 | +/* |
| 15143 | +** Uninstall the VFS wrapper that was installed around the file-descriptor open |
| 15144 | +** on the input database for recover handle p. Mutex RECOVER_MUTEX_ID must be |
| 15145 | +** held when this function is called. |
| 15146 | +*/ |
| 15147 | +static void recoverUninstallWrapper(sqlite3_recover *p){ |
| 15148 | + sqlite3_file *pFd = 0; |
| 15149 | + recoverAssertMutexHeld(); |
| 15150 | + sqlite3_file_control(p->dbIn, p->zDb,SQLITE_FCNTL_FILE_POINTER,(void*)&pFd); |
| 15151 | + if( pFd && pFd->pMethods ){ |
| 15152 | + pFd->pMethods = recover_g.pMethods; |
| 15153 | + recover_g.pMethods = 0; |
| 15154 | + recover_g.p = 0; |
| 15155 | + } |
| 15156 | +} |
| 15157 | + |
| 15158 | +/* |
| 15159 | +** This function does the work of a single sqlite3_recover_step() call. It |
| 15160 | +** is guaranteed that the handle is not in an error state when this |
| 15161 | +** function is called. |
| 15162 | +*/ |
| 15163 | +static void recoverStep(sqlite3_recover *p){ |
| 15164 | + assert( p && p->errCode==SQLITE_OK ); |
| 15165 | + switch( p->eState ){ |
| 15166 | + case RECOVER_STATE_INIT: |
| 15167 | + /* This is the very first call to sqlite3_recover_step() on this object. |
| 15168 | + */ |
| 15169 | + recoverSqlCallback(p, "BEGIN"); |
| 15170 | + recoverSqlCallback(p, "PRAGMA writable_schema = on"); |
| 15171 | + |
| 15172 | + recoverEnterMutex(); |
| 15173 | + recoverInstallWrapper(p); |
| 15174 | + |
| 15175 | + /* Open the output database. And register required virtual tables and |
| 15176 | + ** user functions with the new handle. */ |
| 15177 | + recoverOpenOutput(p); |
| 15178 | + |
| 15179 | + /* Open transactions on both the input and output databases. */ |
| 15180 | + recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); |
| 15181 | + recoverExec(p, p->dbIn, "BEGIN"); |
| 15182 | + if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; |
| 15183 | + recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); |
| 15184 | + recoverTransferSettings(p); |
| 15185 | + recoverOpenRecovery(p); |
| 15186 | + recoverCacheSchema(p); |
| 15187 | + |
| 15188 | + recoverUninstallWrapper(p); |
| 15189 | + recoverLeaveMutex(); |
| 15190 | + |
| 15191 | + recoverExec(p, p->dbOut, "BEGIN"); |
| 15192 | + |
| 15193 | + recoverWriteSchema1(p); |
| 15194 | + p->eState = RECOVER_STATE_WRITING; |
| 15195 | + break; |
| 15196 | + |
| 15197 | + case RECOVER_STATE_WRITING: { |
| 15198 | + if( p->w1.pTbls==0 ){ |
| 15199 | + recoverWriteDataInit(p); |
| 15200 | + } |
| 15201 | + if( SQLITE_DONE==recoverWriteDataStep(p) ){ |
| 15202 | + recoverWriteDataCleanup(p); |
| 15203 | + if( p->zLostAndFound ){ |
| 15204 | + p->eState = RECOVER_STATE_LOSTANDFOUND1; |
| 15205 | + }else{ |
| 15206 | + p->eState = RECOVER_STATE_SCHEMA2; |
| 15207 | + } |
| 15208 | + } |
| 15209 | + break; |
| 15210 | + } |
| 15211 | + |
| 15212 | + case RECOVER_STATE_LOSTANDFOUND1: { |
| 15213 | + if( p->laf.pUsed==0 ){ |
| 15214 | + recoverLostAndFound1Init(p); |
| 15215 | + } |
| 15216 | + if( SQLITE_DONE==recoverLostAndFound1Step(p) ){ |
| 15217 | + p->eState = RECOVER_STATE_LOSTANDFOUND2; |
| 15218 | + } |
| 15219 | + break; |
| 15220 | + } |
| 15221 | + case RECOVER_STATE_LOSTANDFOUND2: { |
| 15222 | + if( p->laf.pAllAndParent==0 ){ |
| 15223 | + recoverLostAndFound2Init(p); |
| 15224 | + } |
| 15225 | + if( SQLITE_DONE==recoverLostAndFound2Step(p) ){ |
| 15226 | + p->eState = RECOVER_STATE_LOSTANDFOUND3; |
| 15227 | + } |
| 15228 | + break; |
| 15229 | + } |
| 15230 | + |
| 15231 | + case RECOVER_STATE_LOSTANDFOUND3: { |
| 15232 | + if( p->laf.pInsert==0 ){ |
| 15233 | + recoverLostAndFound3Init(p); |
| 15234 | + } |
| 15235 | + if( SQLITE_DONE==recoverLostAndFound3Step(p) ){ |
| 15236 | + p->eState = RECOVER_STATE_SCHEMA2; |
| 15237 | + } |
| 15238 | + break; |
| 15239 | + } |
| 15240 | + |
| 15241 | + case RECOVER_STATE_SCHEMA2: { |
| 15242 | + int rc = SQLITE_OK; |
| 15243 | + |
| 15244 | + recoverWriteSchema2(p); |
| 15245 | + p->eState = RECOVER_STATE_DONE; |
| 15246 | + |
| 15247 | + /* If no error has occurred, commit the write transaction on the output |
| 15248 | + ** database. Regardless of whether or not an error has occurred, make |
| 15249 | + ** an attempt to end the read transaction on the input database. */ |
| 15250 | + recoverExec(p, p->dbOut, "COMMIT"); |
| 15251 | + rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0); |
| 15252 | + if( p->errCode==SQLITE_OK ) p->errCode = rc; |
| 15253 | + |
| 15254 | + recoverSqlCallback(p, "PRAGMA writable_schema = off"); |
| 15255 | + recoverSqlCallback(p, "COMMIT"); |
| 15256 | + p->eState = RECOVER_STATE_DONE; |
| 15257 | + recoverFinalCleanup(p); |
| 15258 | + break; |
| 15259 | + }; |
| 15260 | + |
| 15261 | + case RECOVER_STATE_DONE: { |
| 15262 | + /* no-op */ |
| 15263 | + break; |
| 15264 | + }; |
| 15265 | + } |
| 15266 | +} |
| 15267 | + |
| 15268 | + |
| 15269 | +/* |
| 15270 | +** This is a worker function that does the heavy lifting for both init |
| 15271 | +** functions: |
| 15272 | +** |
| 15273 | +** sqlite3_recover_init() |
| 15274 | +** sqlite3_recover_init_sql() |
| 15275 | +** |
| 15276 | +** All this function does is allocate space for the recover handle and |
| 15277 | +** take copies of the input parameters. All the real work is done within |
| 15278 | +** sqlite3_recover_run(). |
| 15279 | +*/ |
| 15280 | +sqlite3_recover *recoverInit( |
| 15281 | + sqlite3* db, |
| 15282 | + const char *zDb, |
| 15283 | + const char *zUri, /* Output URI for _recover_init() */ |
| 15284 | + int (*xSql)(void*, const char*),/* SQL callback for _recover_init_sql() */ |
| 15285 | + void *pSqlCtx /* Context arg for _recover_init_sql() */ |
| 15286 | +){ |
| 15287 | + sqlite3_recover *pRet = 0; |
| 15288 | + int nDb = 0; |
| 15289 | + int nUri = 0; |
| 15290 | + int nByte = 0; |
| 15291 | + |
| 15292 | + if( zDb==0 ){ zDb = "main"; } |
| 15293 | + |
| 15294 | + nDb = recoverStrlen(zDb); |
| 15295 | + nUri = recoverStrlen(zUri); |
| 15296 | + |
| 15297 | + nByte = sizeof(sqlite3_recover) + nDb+1 + nUri+1; |
| 15298 | + pRet = (sqlite3_recover*)sqlite3_malloc(nByte); |
| 15299 | + if( pRet ){ |
| 15300 | + memset(pRet, 0, nByte); |
| 15301 | + pRet->dbIn = db; |
| 15302 | + pRet->zDb = (char*)&pRet[1]; |
| 15303 | + pRet->zUri = &pRet->zDb[nDb+1]; |
| 15304 | + memcpy(pRet->zDb, zDb, nDb); |
| 15305 | + if( nUri>0 && zUri ) memcpy(pRet->zUri, zUri, nUri); |
| 15306 | + pRet->xSql = xSql; |
| 15307 | + pRet->pSqlCtx = pSqlCtx; |
| 15308 | + pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT; |
| 15309 | + } |
| 15310 | + |
| 15311 | + return pRet; |
| 15312 | +} |
| 15313 | + |
| 15314 | +/* |
| 15315 | +** Initialize a recovery handle that creates a new database containing |
| 15316 | +** the recovered data. |
| 15317 | +*/ |
| 15318 | +sqlite3_recover *sqlite3_recover_init( |
| 15319 | + sqlite3* db, |
| 15320 | + const char *zDb, |
| 15321 | + const char *zUri |
| 15322 | +){ |
| 15323 | + return recoverInit(db, zDb, zUri, 0, 0); |
| 15324 | +} |
| 15325 | + |
| 15326 | +/* |
| 15327 | +** Initialize a recovery handle that returns recovered data in the |
| 15328 | +** form of SQL statements via a callback. |
| 15329 | +*/ |
| 15330 | +sqlite3_recover *sqlite3_recover_init_sql( |
| 15331 | + sqlite3* db, |
| 15332 | + const char *zDb, |
| 15333 | + int (*xSql)(void*, const char*), |
| 15334 | + void *pSqlCtx |
| 15335 | +){ |
| 15336 | + return recoverInit(db, zDb, 0, xSql, pSqlCtx); |
| 15337 | +} |
| 15338 | + |
| 15339 | +/* |
| 15340 | +** Return the handle error message, if any. |
| 15341 | +*/ |
| 15342 | +const char *sqlite3_recover_errmsg(sqlite3_recover *p){ |
| 15343 | + return (p && p->errCode!=SQLITE_NOMEM) ? p->zErrMsg : "out of memory"; |
| 15344 | +} |
| 15345 | + |
| 15346 | +/* |
| 15347 | +** Return the handle error code. |
| 15348 | +*/ |
| 15349 | +int sqlite3_recover_errcode(sqlite3_recover *p){ |
| 15350 | + return p ? p->errCode : SQLITE_NOMEM; |
| 15351 | +} |
| 15352 | + |
| 15353 | +/* |
| 15354 | +** Configure the handle. |
| 15355 | +*/ |
| 15356 | +int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ |
| 15357 | + int rc = SQLITE_OK; |
| 15358 | + if( p==0 ){ |
| 15359 | + rc = SQLITE_NOMEM; |
| 15360 | + }else if( p->eState!=RECOVER_STATE_INIT ){ |
| 15361 | + rc = SQLITE_MISUSE; |
| 15362 | + }else{ |
| 15363 | + switch( op ){ |
| 15364 | + case 789: |
| 15365 | + /* This undocumented magic configuration option is used to set the |
| 15366 | + ** name of the auxiliary database that is ATTACH-ed to the database |
| 15367 | + ** connection and used to hold state information during the |
| 15368 | + ** recovery process. This option is for debugging use only and |
| 15369 | + ** is subject to change or removal at any time. */ |
| 15370 | + sqlite3_free(p->zStateDb); |
| 15371 | + p->zStateDb = recoverMPrintf(p, "%s", (char*)pArg); |
| 15372 | + break; |
| 15373 | + |
| 15374 | + case SQLITE_RECOVER_LOST_AND_FOUND: { |
| 15375 | + const char *zArg = (const char*)pArg; |
| 15376 | + sqlite3_free(p->zLostAndFound); |
| 15377 | + if( zArg ){ |
| 15378 | + p->zLostAndFound = recoverMPrintf(p, "%s", zArg); |
| 15379 | + }else{ |
| 15380 | + p->zLostAndFound = 0; |
| 15381 | + } |
| 15382 | + break; |
| 15383 | + } |
| 15384 | + |
| 15385 | + case SQLITE_RECOVER_FREELIST_CORRUPT: |
| 15386 | + p->bFreelistCorrupt = *(int*)pArg; |
| 15387 | + break; |
| 15388 | + |
| 15389 | + case SQLITE_RECOVER_ROWIDS: |
| 15390 | + p->bRecoverRowid = *(int*)pArg; |
| 15391 | + break; |
| 15392 | + |
| 15393 | + case SQLITE_RECOVER_SLOWINDEXES: |
| 15394 | + p->bSlowIndexes = *(int*)pArg; |
| 15395 | + break; |
| 15396 | + |
| 15397 | + default: |
| 15398 | + rc = SQLITE_NOTFOUND; |
| 15399 | + break; |
| 15400 | + } |
| 15401 | + } |
| 15402 | + |
| 15403 | + return rc; |
| 15404 | +} |
| 15405 | + |
| 15406 | +/* |
| 15407 | +** Do a unit of work towards the recovery job. Return SQLITE_OK if |
| 15408 | +** no error has occurred but database recovery is not finished, SQLITE_DONE |
| 15409 | +** if database recovery has been successfully completed, or an SQLite |
| 15410 | +** error code if an error has occurred. |
| 15411 | +*/ |
| 15412 | +int sqlite3_recover_step(sqlite3_recover *p){ |
| 15413 | + if( p==0 ) return SQLITE_NOMEM; |
| 15414 | + if( p->errCode==SQLITE_OK ) recoverStep(p); |
| 15415 | + if( p->eState==RECOVER_STATE_DONE && p->errCode==SQLITE_OK ){ |
| 15416 | + return SQLITE_DONE; |
| 15417 | + } |
| 15418 | + return p->errCode; |
| 15419 | +} |
| 15420 | + |
| 15421 | +/* |
| 15422 | +** Do the configured recovery operation. Return SQLITE_OK if successful, or |
| 15423 | +** else an SQLite error code. |
| 15424 | +*/ |
| 15425 | +int sqlite3_recover_run(sqlite3_recover *p){ |
| 15426 | + while( SQLITE_OK==sqlite3_recover_step(p) ); |
| 15427 | + return sqlite3_recover_errcode(p); |
| 15428 | +} |
| 15429 | + |
| 15430 | + |
| 15431 | +/* |
| 15432 | +** Free all resources associated with the recover handle passed as the only |
| 15433 | +** argument. The results of using a handle with any sqlite3_recover_** |
| 15434 | +** API function after it has been passed to this function are undefined. |
| 15435 | +** |
| 15436 | +** A copy of the value returned by the first call made to sqlite3_recover_run() |
| 15437 | +** on this handle is returned, or SQLITE_OK if sqlite3_recover_run() has |
| 15438 | +** not been called on this handle. |
| 15439 | +*/ |
| 15440 | +int sqlite3_recover_finish(sqlite3_recover *p){ |
| 15441 | + int rc; |
| 15442 | + if( p==0 ){ |
| 15443 | + rc = SQLITE_NOMEM; |
| 15444 | + }else{ |
| 15445 | + recoverFinalCleanup(p); |
| 15446 | + if( p->bCloseTransaction && sqlite3_get_autocommit(p->dbIn)==0 ){ |
| 15447 | + rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0); |
| 15448 | + if( p->errCode==SQLITE_OK ) p->errCode = rc; |
| 15449 | + } |
| 15450 | + rc = p->errCode; |
| 15451 | + sqlite3_free(p->zErrMsg); |
| 15452 | + sqlite3_free(p->zStateDb); |
| 15453 | + sqlite3_free(p->zLostAndFound); |
| 15454 | + sqlite3_free(p->pPage1Cache); |
| 15455 | + sqlite3_free(p); |
| 15456 | + } |
| 15457 | + return rc; |
| 15458 | +} |
| 15459 | + |
| 15460 | +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 15461 | + |
| 15462 | + |
| 15463 | +/************************* End ../ext/recover/sqlite3recover.c ********************/ |
| 12261 | 15464 | #endif |
| 12262 | 15465 | |
| 12263 | 15466 | #if defined(SQLITE_ENABLE_SESSION) |
| 12264 | 15467 | /* |
| 12265 | 15468 | ** State information for a single open session |
| | @@ -13853,10 +17056,11 @@ |
| 13853 | 17056 | if( len>78 ){ |
| 13854 | 17057 | len = 78; |
| 13855 | 17058 | while( (zSql[len]&0xc0)==0x80 ) len--; |
| 13856 | 17059 | } |
| 13857 | 17060 | zCode = sqlite3_mprintf("%.*s", len, zSql); |
| 17061 | + shell_check_oom(zCode); |
| 13858 | 17062 | for(i=0; zCode[i]; i++){ if( IsSpace(zSql[i]) ) zCode[i] = ' '; } |
| 13859 | 17063 | if( iOffset<25 ){ |
| 13860 | 17064 | zMsg = sqlite3_mprintf("\n %z\n %*s^--- error here", zCode, iOffset, ""); |
| 13861 | 17065 | }else{ |
| 13862 | 17066 | zMsg = sqlite3_mprintf("\n %z\n %*serror here ---^", zCode, iOffset-14, ""); |
| | @@ -15552,11 +18756,11 @@ |
| 15552 | 18756 | ".clone NEWDB Clone data into NEWDB from the existing database", |
| 15553 | 18757 | #endif |
| 15554 | 18758 | ".connection [close] [#] Open or close an auxiliary database connection", |
| 15555 | 18759 | ".databases List names and files of attached databases", |
| 15556 | 18760 | ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", |
| 15557 | | -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 18761 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 15558 | 18762 | ".dbinfo ?DB? Show status information about the database", |
| 15559 | 18763 | #endif |
| 15560 | 18764 | ".dump ?OBJECTS? Render database content as SQL", |
| 15561 | 18765 | " Options:", |
| 15562 | 18766 | " --data-only Output only INSERT statements", |
| | @@ -15700,14 +18904,13 @@ |
| 15700 | 18904 | #ifndef SQLITE_SHELL_FIDDLE |
| 15701 | 18905 | ".quit Exit this program", |
| 15702 | 18906 | ".read FILE Read input from FILE or command output", |
| 15703 | 18907 | " If FILE begins with \"|\", it is a command that generates the input.", |
| 15704 | 18908 | #endif |
| 15705 | | -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 18909 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 15706 | 18910 | ".recover Recover as much data as possible from corrupt db.", |
| 15707 | | - " --freelist-corrupt Assume the freelist is corrupt", |
| 15708 | | - " --recovery-db NAME Store recovery metadata in database file NAME", |
| 18911 | + " --ignore-freelist Ignore pages that appear to be on db freelist", |
| 15709 | 18912 | " --lost-and-found TABLE Alternative name for the lost-and-found table", |
| 15710 | 18913 | " --no-rowids Do not attempt to recover rowid values", |
| 15711 | 18914 | " that are not also INTEGER PRIMARY KEYs", |
| 15712 | 18915 | #endif |
| 15713 | 18916 | #ifndef SQLITE_SHELL_FIDDLE |
| | @@ -16315,11 +19518,11 @@ |
| 16315 | 19518 | sqlite3_series_init(p->db, 0, 0); |
| 16316 | 19519 | #ifndef SQLITE_SHELL_FIDDLE |
| 16317 | 19520 | sqlite3_fileio_init(p->db, 0, 0); |
| 16318 | 19521 | sqlite3_completion_init(p->db, 0, 0); |
| 16319 | 19522 | #endif |
| 16320 | | -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 19523 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 16321 | 19524 | sqlite3_dbdata_init(p->db, 0, 0); |
| 16322 | 19525 | #endif |
| 16323 | 19526 | #ifdef SQLITE_HAVE_ZLIB |
| 16324 | 19527 | if( !p->bSafeModePersist ){ |
| 16325 | 19528 | sqlite3_zipfile_init(p->db, 0, 0); |
| | @@ -17200,12 +20403,11 @@ |
| 17200 | 20403 | sqlite3_free(zSchemaTab); |
| 17201 | 20404 | sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); |
| 17202 | 20405 | utf8_printf(p->out, "%-20s %u\n", "data version", iDataVersion); |
| 17203 | 20406 | return 0; |
| 17204 | 20407 | } |
| 17205 | | -#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) |
| 17206 | | - && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 20408 | +#endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 17207 | 20409 | |
| 17208 | 20410 | /* |
| 17209 | 20411 | ** Print the current sqlite3_errmsg() value to stderr and return 1. |
| 17210 | 20412 | */ |
| 17211 | 20413 | static int shellDatabaseError(sqlite3 *db){ |
| | @@ -18474,403 +21676,56 @@ |
| 18474 | 21676 | } |
| 18475 | 21677 | /* End of the ".archive" or ".ar" command logic |
| 18476 | 21678 | *******************************************************************************/ |
| 18477 | 21679 | #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */ |
| 18478 | 21680 | |
| 18479 | | -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 18480 | | -/* |
| 18481 | | -** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op. |
| 18482 | | -** Otherwise, the SQL statement or statements in zSql are executed using |
| 18483 | | -** database connection db and the error code written to *pRc before |
| 18484 | | -** this function returns. |
| 18485 | | -*/ |
| 18486 | | -static void shellExec(sqlite3 *db, int *pRc, const char *zSql){ |
| 18487 | | - int rc = *pRc; |
| 18488 | | - if( rc==SQLITE_OK ){ |
| 18489 | | - char *zErr = 0; |
| 18490 | | - rc = sqlite3_exec(db, zSql, 0, 0, &zErr); |
| 18491 | | - if( rc!=SQLITE_OK ){ |
| 18492 | | - raw_printf(stderr, "SQL error: %s\n", zErr); |
| 18493 | | - } |
| 18494 | | - sqlite3_free(zErr); |
| 18495 | | - *pRc = rc; |
| 18496 | | - } |
| 18497 | | -} |
| 18498 | | - |
| 18499 | | -/* |
| 18500 | | -** Like shellExec(), except that zFmt is a printf() style format string. |
| 18501 | | -*/ |
| 18502 | | -static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){ |
| 18503 | | - char *z = 0; |
| 18504 | | - if( *pRc==SQLITE_OK ){ |
| 18505 | | - va_list ap; |
| 18506 | | - va_start(ap, zFmt); |
| 18507 | | - z = sqlite3_vmprintf(zFmt, ap); |
| 18508 | | - va_end(ap); |
| 18509 | | - if( z==0 ){ |
| 18510 | | - *pRc = SQLITE_NOMEM; |
| 18511 | | - }else{ |
| 18512 | | - shellExec(db, pRc, z); |
| 18513 | | - } |
| 18514 | | - sqlite3_free(z); |
| 18515 | | - } |
| 18516 | | -} |
| 18517 | | - |
| 18518 | | -/* |
| 18519 | | -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. |
| 18520 | | -** Otherwise, an attempt is made to allocate, zero and return a pointer |
| 18521 | | -** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set |
| 18522 | | -** to SQLITE_NOMEM and NULL returned. |
| 18523 | | -*/ |
| 18524 | | -static void *shellMalloc(int *pRc, sqlite3_int64 nByte){ |
| 18525 | | - void *pRet = 0; |
| 18526 | | - if( *pRc==SQLITE_OK ){ |
| 18527 | | - pRet = sqlite3_malloc64(nByte); |
| 18528 | | - if( pRet==0 ){ |
| 18529 | | - *pRc = SQLITE_NOMEM; |
| 18530 | | - }else{ |
| 18531 | | - memset(pRet, 0, nByte); |
| 18532 | | - } |
| 18533 | | - } |
| 18534 | | - return pRet; |
| 18535 | | -} |
| 18536 | | - |
| 18537 | | -/* |
| 18538 | | -** If *pRc is not SQLITE_OK when this function is called, it is a no-op. |
| 18539 | | -** Otherwise, zFmt is treated as a printf() style string. The result of |
| 18540 | | -** formatting it along with any trailing arguments is written into a |
| 18541 | | -** buffer obtained from sqlite3_malloc(), and pointer to which is returned. |
| 18542 | | -** It is the responsibility of the caller to eventually free this buffer |
| 18543 | | -** using a call to sqlite3_free(). |
| 18544 | | -** |
| 18545 | | -** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL |
| 18546 | | -** pointer returned. |
| 18547 | | -*/ |
| 18548 | | -static char *shellMPrintf(int *pRc, const char *zFmt, ...){ |
| 18549 | | - char *z = 0; |
| 18550 | | - if( *pRc==SQLITE_OK ){ |
| 18551 | | - va_list ap; |
| 18552 | | - va_start(ap, zFmt); |
| 18553 | | - z = sqlite3_vmprintf(zFmt, ap); |
| 18554 | | - va_end(ap); |
| 18555 | | - if( z==0 ){ |
| 18556 | | - *pRc = SQLITE_NOMEM; |
| 18557 | | - } |
| 18558 | | - } |
| 18559 | | - return z; |
| 18560 | | -} |
| 18561 | | - |
| 18562 | | - |
| 18563 | | -/* |
| 18564 | | -** When running the ".recover" command, each output table, and the special |
| 18565 | | -** orphaned row table if it is required, is represented by an instance |
| 18566 | | -** of the following struct. |
| 18567 | | -*/ |
| 18568 | | -typedef struct RecoverTable RecoverTable; |
| 18569 | | -struct RecoverTable { |
| 18570 | | - char *zQuoted; /* Quoted version of table name */ |
| 18571 | | - int nCol; /* Number of columns in table */ |
| 18572 | | - char **azlCol; /* Array of column lists */ |
| 18573 | | - int iPk; /* Index of IPK column */ |
| 18574 | | -}; |
| 18575 | | - |
| 18576 | | -/* |
| 18577 | | -** Free a RecoverTable object allocated by recoverFindTable() or |
| 18578 | | -** recoverOrphanTable(). |
| 18579 | | -*/ |
| 18580 | | -static void recoverFreeTable(RecoverTable *pTab){ |
| 18581 | | - if( pTab ){ |
| 18582 | | - sqlite3_free(pTab->zQuoted); |
| 18583 | | - if( pTab->azlCol ){ |
| 18584 | | - int i; |
| 18585 | | - for(i=0; i<=pTab->nCol; i++){ |
| 18586 | | - sqlite3_free(pTab->azlCol[i]); |
| 18587 | | - } |
| 18588 | | - sqlite3_free(pTab->azlCol); |
| 18589 | | - } |
| 18590 | | - sqlite3_free(pTab); |
| 18591 | | - } |
| 18592 | | -} |
| 18593 | | - |
| 18594 | | -/* |
| 18595 | | -** This function is a no-op if (*pRc) is not SQLITE_OK when it is called. |
| 18596 | | -** Otherwise, it allocates and returns a RecoverTable object based on the |
| 18597 | | -** final four arguments passed to this function. It is the responsibility |
| 18598 | | -** of the caller to eventually free the returned object using |
| 18599 | | -** recoverFreeTable(). |
| 18600 | | -*/ |
| 18601 | | -static RecoverTable *recoverNewTable( |
| 18602 | | - int *pRc, /* IN/OUT: Error code */ |
| 18603 | | - const char *zName, /* Name of table */ |
| 18604 | | - const char *zSql, /* CREATE TABLE statement */ |
| 18605 | | - int bIntkey, |
| 18606 | | - int nCol |
| 18607 | | -){ |
| 18608 | | - sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */ |
| 18609 | | - int rc = *pRc; |
| 18610 | | - RecoverTable *pTab = 0; |
| 18611 | | - |
| 18612 | | - pTab = (RecoverTable*)shellMalloc(&rc, sizeof(RecoverTable)); |
| 18613 | | - if( rc==SQLITE_OK ){ |
| 18614 | | - int nSqlCol = 0; |
| 18615 | | - int bSqlIntkey = 0; |
| 18616 | | - sqlite3_stmt *pStmt = 0; |
| 18617 | | - |
| 18618 | | - rc = sqlite3_open("", &dbtmp); |
| 18619 | | - if( rc==SQLITE_OK ){ |
| 18620 | | - sqlite3_create_function(dbtmp, "shell_idquote", 1, SQLITE_UTF8, 0, |
| 18621 | | - shellIdQuote, 0, 0); |
| 18622 | | - } |
| 18623 | | - if( rc==SQLITE_OK ){ |
| 18624 | | - rc = sqlite3_exec(dbtmp, "PRAGMA writable_schema = on", 0, 0, 0); |
| 18625 | | - } |
| 18626 | | - if( rc==SQLITE_OK ){ |
| 18627 | | - rc = sqlite3_exec(dbtmp, zSql, 0, 0, 0); |
| 18628 | | - if( rc==SQLITE_ERROR ){ |
| 18629 | | - rc = SQLITE_OK; |
| 18630 | | - goto finished; |
| 18631 | | - } |
| 18632 | | - } |
| 18633 | | - shellPreparePrintf(dbtmp, &rc, &pStmt, |
| 18634 | | - "SELECT count(*) FROM pragma_table_info(%Q)", zName |
| 18635 | | - ); |
| 18636 | | - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 18637 | | - nSqlCol = sqlite3_column_int(pStmt, 0); |
| 18638 | | - } |
| 18639 | | - shellFinalize(&rc, pStmt); |
| 18640 | | - |
| 18641 | | - if( rc!=SQLITE_OK || nSqlCol<nCol ){ |
| 18642 | | - goto finished; |
| 18643 | | - } |
| 18644 | | - |
| 18645 | | - shellPreparePrintf(dbtmp, &rc, &pStmt, |
| 18646 | | - "SELECT (" |
| 18647 | | - " SELECT substr(data,1,1)==X'0D' FROM sqlite_dbpage WHERE pgno=rootpage" |
| 18648 | | - ") FROM sqlite_schema WHERE name = %Q", zName |
| 18649 | | - ); |
| 18650 | | - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 18651 | | - bSqlIntkey = sqlite3_column_int(pStmt, 0); |
| 18652 | | - } |
| 18653 | | - shellFinalize(&rc, pStmt); |
| 18654 | | - |
| 18655 | | - if( bIntkey==bSqlIntkey ){ |
| 18656 | | - int i; |
| 18657 | | - const char *zPk = "_rowid_"; |
| 18658 | | - sqlite3_stmt *pPkFinder = 0; |
| 18659 | | - |
| 18660 | | - /* If this is an intkey table and there is an INTEGER PRIMARY KEY, |
| 18661 | | - ** set zPk to the name of the PK column, and pTab->iPk to the index |
| 18662 | | - ** of the column, where columns are 0-numbered from left to right. |
| 18663 | | - ** Or, if this is a WITHOUT ROWID table or if there is no IPK column, |
| 18664 | | - ** leave zPk as "_rowid_" and pTab->iPk at -2. */ |
| 18665 | | - pTab->iPk = -2; |
| 18666 | | - if( bIntkey ){ |
| 18667 | | - shellPreparePrintf(dbtmp, &rc, &pPkFinder, |
| 18668 | | - "SELECT cid, name FROM pragma_table_info(%Q) " |
| 18669 | | - " WHERE pk=1 AND type='integer' COLLATE nocase" |
| 18670 | | - " AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)" |
| 18671 | | - , zName, zName |
| 18672 | | - ); |
| 18673 | | - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPkFinder) ){ |
| 18674 | | - pTab->iPk = sqlite3_column_int(pPkFinder, 0); |
| 18675 | | - zPk = (const char*)sqlite3_column_text(pPkFinder, 1); |
| 18676 | | - if( zPk==0 ){ zPk = "_"; /* Defensive. Should never happen */ } |
| 18677 | | - } |
| 18678 | | - } |
| 18679 | | - |
| 18680 | | - pTab->zQuoted = shellMPrintf(&rc, "\"%w\"", zName); |
| 18681 | | - pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1)); |
| 18682 | | - pTab->nCol = nSqlCol; |
| 18683 | | - |
| 18684 | | - if( bIntkey ){ |
| 18685 | | - pTab->azlCol[0] = shellMPrintf(&rc, "\"%w\"", zPk); |
| 18686 | | - }else{ |
| 18687 | | - pTab->azlCol[0] = shellMPrintf(&rc, ""); |
| 18688 | | - } |
| 18689 | | - i = 1; |
| 18690 | | - shellPreparePrintf(dbtmp, &rc, &pStmt, |
| 18691 | | - "SELECT %Q || group_concat(shell_idquote(name), ', ') " |
| 18692 | | - " FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) " |
| 18693 | | - "FROM pragma_table_info(%Q)", |
| 18694 | | - bIntkey ? ", " : "", pTab->iPk, |
| 18695 | | - bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ", |
| 18696 | | - zName |
| 18697 | | - ); |
| 18698 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 18699 | | - const char *zText = (const char*)sqlite3_column_text(pStmt, 0); |
| 18700 | | - pTab->azlCol[i] = shellMPrintf(&rc, "%s%s", pTab->azlCol[0], zText); |
| 18701 | | - i++; |
| 18702 | | - } |
| 18703 | | - shellFinalize(&rc, pStmt); |
| 18704 | | - |
| 18705 | | - shellFinalize(&rc, pPkFinder); |
| 18706 | | - } |
| 18707 | | - } |
| 18708 | | - |
| 18709 | | - finished: |
| 18710 | | - sqlite3_close(dbtmp); |
| 18711 | | - *pRc = rc; |
| 18712 | | - if( rc!=SQLITE_OK || (pTab && pTab->zQuoted==0) ){ |
| 18713 | | - recoverFreeTable(pTab); |
| 18714 | | - pTab = 0; |
| 18715 | | - } |
| 18716 | | - return pTab; |
| 18717 | | -} |
| 18718 | | - |
| 18719 | | -/* |
| 18720 | | -** This function is called to search the schema recovered from the |
| 18721 | | -** sqlite_schema table of the (possibly) corrupt database as part |
| 18722 | | -** of a ".recover" command. Specifically, for a table with root page |
| 18723 | | -** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the |
| 18724 | | -** table must be a WITHOUT ROWID table, or if non-zero, not one of |
| 18725 | | -** those. |
| 18726 | | -** |
| 18727 | | -** If a table is found, a (RecoverTable*) object is returned. Or, if |
| 18728 | | -** no such table is found, but bIntkey is false and iRoot is the |
| 18729 | | -** root page of an index in the recovered schema, then (*pbNoop) is |
| 18730 | | -** set to true and NULL returned. Or, if there is no such table or |
| 18731 | | -** index, NULL is returned and (*pbNoop) set to 0, indicating that |
| 18732 | | -** the caller should write data to the orphans table. |
| 18733 | | -*/ |
| 18734 | | -static RecoverTable *recoverFindTable( |
| 18735 | | - ShellState *pState, /* Shell state object */ |
| 18736 | | - int *pRc, /* IN/OUT: Error code */ |
| 18737 | | - int iRoot, /* Root page of table */ |
| 18738 | | - int bIntkey, /* True for an intkey table */ |
| 18739 | | - int nCol, /* Number of columns in table */ |
| 18740 | | - int *pbNoop /* OUT: True if iRoot is root of index */ |
| 18741 | | -){ |
| 18742 | | - sqlite3_stmt *pStmt = 0; |
| 18743 | | - RecoverTable *pRet = 0; |
| 18744 | | - int bNoop = 0; |
| 18745 | | - const char *zSql = 0; |
| 18746 | | - const char *zName = 0; |
| 18747 | | - |
| 18748 | | - /* Search the recovered schema for an object with root page iRoot. */ |
| 18749 | | - shellPreparePrintf(pState->db, pRc, &pStmt, |
| 18750 | | - "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot |
| 18751 | | - ); |
| 18752 | | - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 18753 | | - const char *zType = (const char*)sqlite3_column_text(pStmt, 0); |
| 18754 | | - if( bIntkey==0 && sqlite3_stricmp(zType, "index")==0 ){ |
| 18755 | | - bNoop = 1; |
| 18756 | | - break; |
| 18757 | | - } |
| 18758 | | - if( sqlite3_stricmp(zType, "table")==0 ){ |
| 18759 | | - zName = (const char*)sqlite3_column_text(pStmt, 1); |
| 18760 | | - zSql = (const char*)sqlite3_column_text(pStmt, 2); |
| 18761 | | - if( zName!=0 && zSql!=0 ){ |
| 18762 | | - pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol); |
| 18763 | | - break; |
| 18764 | | - } |
| 18765 | | - } |
| 18766 | | - } |
| 18767 | | - |
| 18768 | | - shellFinalize(pRc, pStmt); |
| 18769 | | - *pbNoop = bNoop; |
| 18770 | | - return pRet; |
| 18771 | | -} |
| 18772 | | - |
| 18773 | | -/* |
| 18774 | | -** Return a RecoverTable object representing the orphans table. |
| 18775 | | -*/ |
| 18776 | | -static RecoverTable *recoverOrphanTable( |
| 18777 | | - ShellState *pState, /* Shell state object */ |
| 18778 | | - int *pRc, /* IN/OUT: Error code */ |
| 18779 | | - const char *zLostAndFound, /* Base name for orphans table */ |
| 18780 | | - int nCol /* Number of user data columns */ |
| 18781 | | -){ |
| 18782 | | - RecoverTable *pTab = 0; |
| 18783 | | - if( nCol>=0 && *pRc==SQLITE_OK ){ |
| 18784 | | - int i; |
| 18785 | | - |
| 18786 | | - /* This block determines the name of the orphan table. The prefered |
| 18787 | | - ** name is zLostAndFound. But if that clashes with another name |
| 18788 | | - ** in the recovered schema, try zLostAndFound_0, zLostAndFound_1 |
| 18789 | | - ** and so on until a non-clashing name is found. */ |
| 18790 | | - int iTab = 0; |
| 18791 | | - char *zTab = shellMPrintf(pRc, "%s", zLostAndFound); |
| 18792 | | - sqlite3_stmt *pTest = 0; |
| 18793 | | - shellPrepare(pState->db, pRc, |
| 18794 | | - "SELECT 1 FROM recovery.schema WHERE name=?", &pTest |
| 18795 | | - ); |
| 18796 | | - if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); |
| 18797 | | - while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){ |
| 18798 | | - shellReset(pRc, pTest); |
| 18799 | | - sqlite3_free(zTab); |
| 18800 | | - zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++); |
| 18801 | | - sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT); |
| 18802 | | - } |
| 18803 | | - shellFinalize(pRc, pTest); |
| 18804 | | - |
| 18805 | | - pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable)); |
| 18806 | | - if( pTab ){ |
| 18807 | | - pTab->zQuoted = shellMPrintf(pRc, "\"%w\"", zTab); |
| 18808 | | - pTab->nCol = nCol; |
| 18809 | | - pTab->iPk = -2; |
| 18810 | | - if( nCol>0 ){ |
| 18811 | | - pTab->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * (nCol+1)); |
| 18812 | | - if( pTab->azlCol ){ |
| 18813 | | - pTab->azlCol[nCol] = shellMPrintf(pRc, ""); |
| 18814 | | - for(i=nCol-1; i>=0; i--){ |
| 18815 | | - pTab->azlCol[i] = shellMPrintf(pRc, "%s, NULL", pTab->azlCol[i+1]); |
| 18816 | | - } |
| 18817 | | - } |
| 18818 | | - } |
| 18819 | | - |
| 18820 | | - if( *pRc!=SQLITE_OK ){ |
| 18821 | | - recoverFreeTable(pTab); |
| 18822 | | - pTab = 0; |
| 18823 | | - }else{ |
| 18824 | | - raw_printf(pState->out, |
| 18825 | | - "CREATE TABLE %s(rootpgno INTEGER, " |
| 18826 | | - "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted |
| 18827 | | - ); |
| 18828 | | - for(i=0; i<nCol; i++){ |
| 18829 | | - raw_printf(pState->out, ", c%d", i); |
| 18830 | | - } |
| 18831 | | - raw_printf(pState->out, ");\n"); |
| 18832 | | - } |
| 18833 | | - } |
| 18834 | | - sqlite3_free(zTab); |
| 18835 | | - } |
| 18836 | | - return pTab; |
| 21681 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 21682 | + |
| 21683 | +/* |
| 21684 | +** This function is used as a callback by the recover extension. Simply |
| 21685 | +** print the supplied SQL statement to stdout. |
| 21686 | +*/ |
| 21687 | +static int recoverSqlCb(void *pCtx, const char *zSql){ |
| 21688 | + ShellState *pState = (ShellState*)pCtx; |
| 21689 | + utf8_printf(pState->out, "%s;\n", zSql); |
| 21690 | + return SQLITE_OK; |
| 18837 | 21691 | } |
| 18838 | 21692 | |
| 18839 | 21693 | /* |
| 18840 | 21694 | ** This function is called to recover data from the database. A script |
| 18841 | 21695 | ** to construct a new database containing all recovered data is output |
| 18842 | 21696 | ** on stream pState->out. |
| 18843 | 21697 | */ |
| 18844 | 21698 | static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ |
| 18845 | 21699 | int rc = SQLITE_OK; |
| 18846 | | - sqlite3_stmt *pLoop = 0; /* Loop through all root pages */ |
| 18847 | | - sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */ |
| 18848 | | - sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */ |
| 18849 | | - const char *zRecoveryDb = ""; /* Name of "recovery" database */ |
| 18850 | | - const char *zLostAndFound = "lost_and_found"; |
| 18851 | | - int i; |
| 18852 | | - int nOrphan = -1; |
| 18853 | | - RecoverTable *pOrphan = 0; |
| 18854 | | - |
| 18855 | | - int bFreelist = 1; /* 0 if --freelist-corrupt is specified */ |
| 21700 | + const char *zRecoveryDb = ""; /* Name of "recovery" database. Debug only */ |
| 21701 | + const char *zLAF = "lost_and_found"; |
| 21702 | + int bFreelist = 1; /* 0 if --ignore-freelist is specified */ |
| 18856 | 21703 | int bRowids = 1; /* 0 if --no-rowids */ |
| 21704 | + sqlite3_recover *p = 0; |
| 21705 | + int i = 0; |
| 21706 | + |
| 18857 | 21707 | for(i=1; i<nArg; i++){ |
| 18858 | 21708 | char *z = azArg[i]; |
| 18859 | 21709 | int n; |
| 18860 | 21710 | if( z[0]=='-' && z[1]=='-' ) z++; |
| 18861 | 21711 | n = strlen30(z); |
| 18862 | | - if( n<=17 && memcmp("-freelist-corrupt", z, n)==0 ){ |
| 21712 | + if( n<=17 && memcmp("-ignore-freelist", z, n)==0 ){ |
| 18863 | 21713 | bFreelist = 0; |
| 18864 | 21714 | }else |
| 18865 | 21715 | if( n<=12 && memcmp("-recovery-db", z, n)==0 && i<(nArg-1) ){ |
| 21716 | + /* This option determines the name of the ATTACH-ed database used |
| 21717 | + ** internally by the recovery extension. The default is "" which |
| 21718 | + ** means to use a temporary database that is automatically deleted |
| 21719 | + ** when closed. This option is undocumented and might disappear at |
| 21720 | + ** any moment. */ |
| 18866 | 21721 | i++; |
| 18867 | 21722 | zRecoveryDb = azArg[i]; |
| 18868 | 21723 | }else |
| 18869 | 21724 | if( n<=15 && memcmp("-lost-and-found", z, n)==0 && i<(nArg-1) ){ |
| 18870 | 21725 | i++; |
| 18871 | | - zLostAndFound = azArg[i]; |
| 21726 | + zLAF = azArg[i]; |
| 18872 | 21727 | }else |
| 18873 | 21728 | if( n<=10 && memcmp("-no-rowids", z, n)==0 ){ |
| 18874 | 21729 | bRowids = 0; |
| 18875 | 21730 | } |
| 18876 | 21731 | else{ |
| | @@ -18878,285 +21733,29 @@ |
| 18878 | 21733 | showHelp(pState->out, azArg[0]); |
| 18879 | 21734 | return 1; |
| 18880 | 21735 | } |
| 18881 | 21736 | } |
| 18882 | 21737 | |
| 18883 | | - shellExecPrintf(pState->db, &rc, |
| 18884 | | - /* Attach an in-memory database named 'recovery'. Create an indexed |
| 18885 | | - ** cache of the sqlite_dbptr virtual table. */ |
| 18886 | | - "PRAGMA writable_schema = on;" |
| 18887 | | - "ATTACH %Q AS recovery;" |
| 18888 | | - "DROP TABLE IF EXISTS recovery.dbptr;" |
| 18889 | | - "DROP TABLE IF EXISTS recovery.freelist;" |
| 18890 | | - "DROP TABLE IF EXISTS recovery.map;" |
| 18891 | | - "DROP TABLE IF EXISTS recovery.schema;" |
| 18892 | | - "CREATE TABLE recovery.freelist(pgno INTEGER PRIMARY KEY);", zRecoveryDb |
| 18893 | | - ); |
| 18894 | | - |
| 18895 | | - if( bFreelist ){ |
| 18896 | | - shellExec(pState->db, &rc, |
| 18897 | | - "WITH trunk(pgno) AS (" |
| 18898 | | - " SELECT shell_int32(" |
| 18899 | | - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x " |
| 18900 | | - " WHERE x>0" |
| 18901 | | - " UNION" |
| 18902 | | - " SELECT shell_int32(" |
| 18903 | | - " (SELECT data FROM sqlite_dbpage WHERE pgno=trunk.pgno), 0) AS x " |
| 18904 | | - " FROM trunk WHERE x>0" |
| 18905 | | - ")," |
| 18906 | | - "freelist(data, n, freepgno) AS (" |
| 18907 | | - " SELECT data, min(16384, shell_int32(data, 1)-1), t.pgno " |
| 18908 | | - " FROM trunk t, sqlite_dbpage s WHERE s.pgno=t.pgno" |
| 18909 | | - " UNION ALL" |
| 18910 | | - " SELECT data, n-1, shell_int32(data, 2+n) " |
| 18911 | | - " FROM freelist WHERE n>=0" |
| 18912 | | - ")" |
| 18913 | | - "REPLACE INTO recovery.freelist SELECT freepgno FROM freelist;" |
| 18914 | | - ); |
| 18915 | | - } |
| 18916 | | - |
| 18917 | | - /* If this is an auto-vacuum database, add all pointer-map pages to |
| 18918 | | - ** the freelist table. Do this regardless of whether or not |
| 18919 | | - ** --freelist-corrupt was specified. */ |
| 18920 | | - shellExec(pState->db, &rc, |
| 18921 | | - "WITH ptrmap(pgno) AS (" |
| 18922 | | - " SELECT 2 WHERE shell_int32(" |
| 18923 | | - " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 13" |
| 18924 | | - " )" |
| 18925 | | - " UNION ALL " |
| 18926 | | - " SELECT pgno+1+(SELECT page_size FROM pragma_page_size)/5 AS pp " |
| 18927 | | - " FROM ptrmap WHERE pp<=(SELECT page_count FROM pragma_page_count)" |
| 18928 | | - ")" |
| 18929 | | - "REPLACE INTO recovery.freelist SELECT pgno FROM ptrmap" |
| 18930 | | - ); |
| 18931 | | - |
| 18932 | | - shellExec(pState->db, &rc, |
| 18933 | | - "CREATE TABLE recovery.dbptr(" |
| 18934 | | - " pgno, child, PRIMARY KEY(child, pgno)" |
| 18935 | | - ") WITHOUT ROWID;" |
| 18936 | | - "INSERT OR IGNORE INTO recovery.dbptr(pgno, child) " |
| 18937 | | - " SELECT * FROM sqlite_dbptr" |
| 18938 | | - " WHERE pgno NOT IN freelist AND child NOT IN freelist;" |
| 18939 | | - |
| 18940 | | - /* Delete any pointer to page 1. This ensures that page 1 is considered |
| 18941 | | - ** a root page, regardless of how corrupt the db is. */ |
| 18942 | | - "DELETE FROM recovery.dbptr WHERE child = 1;" |
| 18943 | | - |
| 18944 | | - /* Delete all pointers to any pages that have more than one pointer |
| 18945 | | - ** to them. Such pages will be treated as root pages when recovering |
| 18946 | | - ** data. */ |
| 18947 | | - "DELETE FROM recovery.dbptr WHERE child IN (" |
| 18948 | | - " SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1" |
| 18949 | | - ");" |
| 18950 | | - |
| 18951 | | - /* Create the "map" table that will (eventually) contain instructions |
| 18952 | | - ** for dealing with each page in the db that contains one or more |
| 18953 | | - ** records. */ |
| 18954 | | - "CREATE TABLE recovery.map(" |
| 18955 | | - "pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT" |
| 18956 | | - ");" |
| 18957 | | - |
| 18958 | | - /* Populate table [map]. If there are circular loops of pages in the |
| 18959 | | - ** database, the following adds all pages in such a loop to the map |
| 18960 | | - ** as individual root pages. This could be handled better. */ |
| 18961 | | - "WITH pages(i, maxlen) AS (" |
| 18962 | | - " SELECT page_count, (" |
| 18963 | | - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=page_count" |
| 18964 | | - " ) FROM pragma_page_count WHERE page_count>0" |
| 18965 | | - " UNION ALL" |
| 18966 | | - " SELECT i-1, (" |
| 18967 | | - " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=i-1" |
| 18968 | | - " ) FROM pages WHERE i>=2" |
| 18969 | | - ")" |
| 18970 | | - "INSERT INTO recovery.map(pgno, maxlen, intkey, root) " |
| 18971 | | - " SELECT i, maxlen, NULL, (" |
| 18972 | | - " WITH p(orig, pgno, parent) AS (" |
| 18973 | | - " SELECT 0, i, (SELECT pgno FROM recovery.dbptr WHERE child=i)" |
| 18974 | | - " UNION " |
| 18975 | | - " SELECT i, p.parent, " |
| 18976 | | - " (SELECT pgno FROM recovery.dbptr WHERE child=p.parent) FROM p" |
| 18977 | | - " )" |
| 18978 | | - " SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)" |
| 18979 | | - ") " |
| 18980 | | - "FROM pages WHERE maxlen IS NOT NULL AND i NOT IN freelist;" |
| 18981 | | - "UPDATE recovery.map AS o SET intkey = (" |
| 18982 | | - " SELECT substr(data, 1, 1)==X'0D' FROM sqlite_dbpage WHERE pgno=o.pgno" |
| 18983 | | - ");" |
| 18984 | | - |
| 18985 | | - /* Extract data from page 1 and any linked pages into table |
| 18986 | | - ** recovery.schema. With the same schema as an sqlite_schema table. */ |
| 18987 | | - "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" |
| 18988 | | - "INSERT INTO recovery.schema SELECT " |
| 18989 | | - " max(CASE WHEN field=0 THEN value ELSE NULL END)," |
| 18990 | | - " max(CASE WHEN field=1 THEN value ELSE NULL END)," |
| 18991 | | - " max(CASE WHEN field=2 THEN value ELSE NULL END)," |
| 18992 | | - " max(CASE WHEN field=3 THEN value ELSE NULL END)," |
| 18993 | | - " max(CASE WHEN field=4 THEN value ELSE NULL END)" |
| 18994 | | - "FROM sqlite_dbdata WHERE pgno IN (" |
| 18995 | | - " SELECT pgno FROM recovery.map WHERE root=1" |
| 18996 | | - ")" |
| 18997 | | - "GROUP BY pgno, cell;" |
| 18998 | | - "CREATE INDEX recovery.schema_rootpage ON schema(rootpage);" |
| 18999 | | - ); |
| 19000 | | - |
| 19001 | | - /* Open a transaction, then print out all non-virtual, non-"sqlite_%" |
| 19002 | | - ** CREATE TABLE statements that extracted from the existing schema. */ |
| 19003 | | - if( rc==SQLITE_OK ){ |
| 19004 | | - sqlite3_stmt *pStmt = 0; |
| 19005 | | - /* ".recover" might output content in an order which causes immediate |
| 19006 | | - ** foreign key constraints to be violated. So disable foreign-key |
| 19007 | | - ** constraint enforcement to prevent problems when running the output |
| 19008 | | - ** script. */ |
| 19009 | | - raw_printf(pState->out, "PRAGMA foreign_keys=OFF;\n"); |
| 19010 | | - raw_printf(pState->out, "BEGIN;\n"); |
| 19011 | | - raw_printf(pState->out, "PRAGMA writable_schema = on;\n"); |
| 19012 | | - shellPrepare(pState->db, &rc, |
| 19013 | | - "SELECT sql FROM recovery.schema " |
| 19014 | | - "WHERE type='table' AND sql LIKE 'create table%'", &pStmt |
| 19015 | | - ); |
| 19016 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 19017 | | - const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0); |
| 19018 | | - raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n", |
| 19019 | | - &zCreateTable[12] |
| 19020 | | - ); |
| 19021 | | - } |
| 19022 | | - shellFinalize(&rc, pStmt); |
| 19023 | | - } |
| 19024 | | - |
| 19025 | | - /* Figure out if an orphan table will be required. And if so, how many |
| 19026 | | - ** user columns it should contain */ |
| 19027 | | - shellPrepare(pState->db, &rc, |
| 19028 | | - "SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1" |
| 19029 | | - , &pLoop |
| 19030 | | - ); |
| 19031 | | - if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ |
| 19032 | | - nOrphan = sqlite3_column_int(pLoop, 0); |
| 19033 | | - } |
| 19034 | | - shellFinalize(&rc, pLoop); |
| 19035 | | - pLoop = 0; |
| 19036 | | - |
| 19037 | | - shellPrepare(pState->db, &rc, |
| 19038 | | - "SELECT pgno FROM recovery.map WHERE root=?", &pPages |
| 19039 | | - ); |
| 19040 | | - |
| 19041 | | - shellPrepare(pState->db, &rc, |
| 19042 | | - "SELECT max(field), group_concat(shell_escape_crnl(quote" |
| 19043 | | - "(case when (? AND field<0) then NULL else value end)" |
| 19044 | | - "), ', ')" |
| 19045 | | - ", min(field) " |
| 19046 | | - "FROM sqlite_dbdata WHERE pgno = ? AND field != ?" |
| 19047 | | - "GROUP BY cell", &pCells |
| 19048 | | - ); |
| 19049 | | - |
| 19050 | | - /* Loop through each root page. */ |
| 19051 | | - shellPrepare(pState->db, &rc, |
| 19052 | | - "SELECT root, intkey, max(maxlen) FROM recovery.map" |
| 19053 | | - " WHERE root>1 GROUP BY root, intkey ORDER BY root=(" |
| 19054 | | - " SELECT rootpage FROM recovery.schema WHERE name='sqlite_sequence'" |
| 19055 | | - ")", &pLoop |
| 19056 | | - ); |
| 19057 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ |
| 19058 | | - int iRoot = sqlite3_column_int(pLoop, 0); |
| 19059 | | - int bIntkey = sqlite3_column_int(pLoop, 1); |
| 19060 | | - int nCol = sqlite3_column_int(pLoop, 2); |
| 19061 | | - int bNoop = 0; |
| 19062 | | - RecoverTable *pTab; |
| 19063 | | - |
| 19064 | | - assert( bIntkey==0 || bIntkey==1 ); |
| 19065 | | - pTab = recoverFindTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop); |
| 19066 | | - if( bNoop || rc ) continue; |
| 19067 | | - if( pTab==0 ){ |
| 19068 | | - if( pOrphan==0 ){ |
| 19069 | | - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); |
| 19070 | | - } |
| 19071 | | - pTab = pOrphan; |
| 19072 | | - if( pTab==0 ) break; |
| 19073 | | - } |
| 19074 | | - |
| 19075 | | - if( 0==sqlite3_stricmp(pTab->zQuoted, "\"sqlite_sequence\"") ){ |
| 19076 | | - raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n"); |
| 19077 | | - } |
| 19078 | | - sqlite3_bind_int(pPages, 1, iRoot); |
| 19079 | | - if( bRowids==0 && pTab->iPk<0 ){ |
| 19080 | | - sqlite3_bind_int(pCells, 1, 1); |
| 19081 | | - }else{ |
| 19082 | | - sqlite3_bind_int(pCells, 1, 0); |
| 19083 | | - } |
| 19084 | | - sqlite3_bind_int(pCells, 3, pTab->iPk); |
| 19085 | | - |
| 19086 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPages) ){ |
| 19087 | | - int iPgno = sqlite3_column_int(pPages, 0); |
| 19088 | | - sqlite3_bind_int(pCells, 2, iPgno); |
| 19089 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pCells) ){ |
| 19090 | | - int nField = sqlite3_column_int(pCells, 0); |
| 19091 | | - int iMin = sqlite3_column_int(pCells, 2); |
| 19092 | | - const char *zVal = (const char*)sqlite3_column_text(pCells, 1); |
| 19093 | | - |
| 19094 | | - RecoverTable *pTab2 = pTab; |
| 19095 | | - if( pTab!=pOrphan && (iMin<0)!=bIntkey ){ |
| 19096 | | - if( pOrphan==0 ){ |
| 19097 | | - pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan); |
| 19098 | | - } |
| 19099 | | - pTab2 = pOrphan; |
| 19100 | | - if( pTab2==0 ) break; |
| 19101 | | - } |
| 19102 | | - |
| 19103 | | - nField = nField+1; |
| 19104 | | - if( pTab2==pOrphan ){ |
| 19105 | | - raw_printf(pState->out, |
| 19106 | | - "INSERT INTO %s VALUES(%d, %d, %d, %s%s%s);\n", |
| 19107 | | - pTab2->zQuoted, iRoot, iPgno, nField, |
| 19108 | | - iMin<0 ? "" : "NULL, ", zVal, pTab2->azlCol[nField] |
| 19109 | | - ); |
| 19110 | | - }else{ |
| 19111 | | - raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n", |
| 19112 | | - pTab2->zQuoted, pTab2->azlCol[nField], zVal |
| 19113 | | - ); |
| 19114 | | - } |
| 19115 | | - } |
| 19116 | | - shellReset(&rc, pCells); |
| 19117 | | - } |
| 19118 | | - shellReset(&rc, pPages); |
| 19119 | | - if( pTab!=pOrphan ) recoverFreeTable(pTab); |
| 19120 | | - } |
| 19121 | | - shellFinalize(&rc, pLoop); |
| 19122 | | - shellFinalize(&rc, pPages); |
| 19123 | | - shellFinalize(&rc, pCells); |
| 19124 | | - recoverFreeTable(pOrphan); |
| 19125 | | - |
| 19126 | | - /* The rest of the schema */ |
| 19127 | | - if( rc==SQLITE_OK ){ |
| 19128 | | - sqlite3_stmt *pStmt = 0; |
| 19129 | | - shellPrepare(pState->db, &rc, |
| 19130 | | - "SELECT sql, name FROM recovery.schema " |
| 19131 | | - "WHERE sql NOT LIKE 'create table%'", &pStmt |
| 19132 | | - ); |
| 19133 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 19134 | | - const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); |
| 19135 | | - if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){ |
| 19136 | | - const char *zName = (const char*)sqlite3_column_text(pStmt, 1); |
| 19137 | | - char *zPrint = shellMPrintf(&rc, |
| 19138 | | - "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", |
| 19139 | | - zName, zName, zSql |
| 19140 | | - ); |
| 19141 | | - raw_printf(pState->out, "%s;\n", zPrint); |
| 19142 | | - sqlite3_free(zPrint); |
| 19143 | | - }else{ |
| 19144 | | - raw_printf(pState->out, "%s;\n", zSql); |
| 19145 | | - } |
| 19146 | | - } |
| 19147 | | - shellFinalize(&rc, pStmt); |
| 19148 | | - } |
| 19149 | | - |
| 19150 | | - if( rc==SQLITE_OK ){ |
| 19151 | | - raw_printf(pState->out, "PRAGMA writable_schema = off;\n"); |
| 19152 | | - raw_printf(pState->out, "COMMIT;\n"); |
| 19153 | | - } |
| 19154 | | - sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0); |
| 19155 | | - return rc; |
| 19156 | | -} |
| 19157 | | -#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 21738 | + p = sqlite3_recover_init_sql( |
| 21739 | + pState->db, "main", recoverSqlCb, (void*)pState |
| 21740 | + ); |
| 21741 | + |
| 21742 | + sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 21743 | + sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 21744 | + sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 21745 | + sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 21746 | + |
| 21747 | + sqlite3_recover_run(p); |
| 21748 | + if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 21749 | + const char *zErr = sqlite3_recover_errmsg(p); |
| 21750 | + int errCode = sqlite3_recover_errcode(p); |
| 21751 | + raw_printf(stderr, "sql error: %s (%d)\n", zErr, errCode); |
| 21752 | + } |
| 21753 | + rc = sqlite3_recover_finish(p); |
| 21754 | + return rc; |
| 21755 | +} |
| 21756 | +#endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 19158 | 21757 | |
| 19159 | 21758 | |
| 19160 | 21759 | /* |
| 19161 | 21760 | * zAutoColumn(zCol, &db, ?) => Maybe init db, add column zCol to it. |
| 19162 | 21761 | * zAutoColumn(0, &db, ?) => (db!=0) Form columns spec for CREATE TABLE, |
| | @@ -19744,20 +22343,20 @@ |
| 19744 | 22343 | utf8_printf(stderr, "Error: unknown dbconfig \"%s\"\n", azArg[1]); |
| 19745 | 22344 | utf8_printf(stderr, "Enter \".dbconfig\" with no arguments for a list\n"); |
| 19746 | 22345 | } |
| 19747 | 22346 | }else |
| 19748 | 22347 | |
| 19749 | | -#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) |
| 22348 | +#if SQLITE_SHELL_HAVE_RECOVER |
| 19750 | 22349 | if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbinfo", n)==0 ){ |
| 19751 | 22350 | rc = shell_dbinfo_command(p, nArg, azArg); |
| 19752 | 22351 | }else |
| 19753 | 22352 | |
| 19754 | 22353 | if( c=='r' && cli_strncmp(azArg[0], "recover", n)==0 ){ |
| 19755 | 22354 | open_db(p, 0); |
| 19756 | 22355 | rc = recoverDatabaseCmd(p, nArg, azArg); |
| 19757 | 22356 | }else |
| 19758 | | -#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ |
| 22357 | +#endif /* SQLITE_SHELL_HAVE_RECOVER */ |
| 19759 | 22358 | |
| 19760 | 22359 | if( c=='d' && cli_strncmp(azArg[0], "dump", n)==0 ){ |
| 19761 | 22360 | char *zLike = 0; |
| 19762 | 22361 | char *zSql; |
| 19763 | 22362 | int i; |
| 19764 | 22363 | |