diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-20 11:42:21 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-07-20 11:42:21 +0000 |
commit | a077da7b45007de882a8d843ad084ff2eba14c2f (patch) | |
tree | 009489b2268f7ad927ed001db89d65612033aa9f /third_party/sqlite | |
parent | 7c71dbfb0be07ce82670d9a10d340e87c95d9eff (diff) | |
download | chromium_src-a077da7b45007de882a8d843ad084ff2eba14c2f.zip chromium_src-a077da7b45007de882a8d843ad084ff2eba14c2f.tar.gz chromium_src-a077da7b45007de882a8d843ad084ff2eba14c2f.tar.bz2 |
[sql] Integrate SQLite recover.c module.
Shift final recover.c into place, add to amalgamation and sqlite3.h.
This puts it into the Chromium build, but without any users.
BUG=109482
Review URL: https://chromiumcodereview.appspot.com/19685004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@212779 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/sqlite')
-rw-r--r-- | third_party/sqlite/README.chromium | 2 | ||||
-rw-r--r-- | third_party/sqlite/amalgamation/sqlite3.c | 2139 | ||||
-rw-r--r-- | third_party/sqlite/amalgamation/sqlite3.h | 11 | ||||
-rw-r--r-- | third_party/sqlite/recover.patch | 2194 | ||||
-rw-r--r-- | third_party/sqlite/src/Makefile.in | 1 | ||||
-rw-r--r-- | third_party/sqlite/src/src/recover.c (renamed from third_party/sqlite/src/src/recover-alt.c) | 315 | ||||
-rw-r--r-- | third_party/sqlite/src/src/sqlite.h.in | 11 | ||||
-rw-r--r-- | third_party/sqlite/src/tool/mksqlite3c.tcl | 2 |
8 files changed, 4552 insertions, 123 deletions
diff --git a/third_party/sqlite/README.chromium b/third_party/sqlite/README.chromium index 6becfe4..da14b0e 100644 --- a/third_party/sqlite/README.chromium +++ b/third_party/sqlite/README.chromium @@ -75,6 +75,7 @@ sqlite-3.7.6.3-fix-out-of-scope-memory-reference.patch misalignment.patch memcmp.patch separate_cache_pool.patch +recover.patch So, e.g. you could do this to apply all our patches to vanilla SQLite: @@ -93,6 +94,7 @@ patch -p0 < ../sqlite/sqlite-3.7.6.3-fix-out-of-scope-memory-reference.patch patch -p0 < ../sqlite/misalignment.patch patch -p0 < ../sqlite/memcmp.patch patch -p0 < ../sqlite/separate_cache_pool.patch +patch -p0 < ../sqlite/recover.patch This will only be the case if all changes we make also update the corresponding patch files. Therefore please remember to do that whenever you make a change! diff --git a/third_party/sqlite/amalgamation/sqlite3.c b/third_party/sqlite/amalgamation/sqlite3.c index a457c09..ebae328 100644 --- a/third_party/sqlite/amalgamation/sqlite3.c +++ b/third_party/sqlite/amalgamation/sqlite3.c @@ -6951,6 +6951,17 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( #define SQLITE_CHECKPOINT_RESTART 2 +/* Begin recover.patch for Chromium */ +/* +** Call to initialize the recover virtual-table modules (see recover.c). +** +** This could be loaded by default in main.c, but that would make the +** virtual table available to Web SQL. Breaking it out allows only +** selected users to enable it (currently sql/recovery.cc). +*/ +int recoverVtableInit(sqlite3 *db); +/* End recover.patch for Chromium */ + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. @@ -110444,6 +110455,2134 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){ #endif /************** End of notify.c **********************************************/ +/************** Begin file recover.c *****************************************/ +/* +** 2012 Jan 11 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +*/ +/* TODO(shess): THIS MODULE IS STILL EXPERIMENTAL. DO NOT USE IT. */ +/* Implements a virtual table "recover" which can be used to recover + * data from a corrupt table. The table is walked manually, with + * corrupt items skipped. Additionally, any errors while reading will + * be skipped. + * + * Given a table with this definition: + * + * CREATE TABLE Stuff ( + * name TEXT PRIMARY KEY, + * value TEXT NOT NULL + * ); + * + * to recover the data from teh table, you could do something like: + * + * -- Attach another database, the original is not trustworthy. + * ATTACH DATABASE '/tmp/db.db' AS rdb; + * -- Create a new version of the table. + * CREATE TABLE rdb.Stuff ( + * name TEXT PRIMARY KEY, + * value TEXT NOT NULL + * ); + * -- This will read the original table's data. + * CREATE VIRTUAL TABLE temp.recover_Stuff using recover( + * main.Stuff, + * name TEXT STRICT NOT NULL, -- only real TEXT data allowed + * value TEXT STRICT NOT NULL + * ); + * -- Corruption means the UNIQUE constraint may no longer hold for + * -- Stuff, so either OR REPLACE or OR IGNORE must be used. + * INSERT OR REPLACE INTO rdb.Stuff (rowid, name, value ) + * SELECT rowid, name, value FROM temp.recover_Stuff; + * DROP TABLE temp.recover_Stuff; + * DETACH DATABASE rdb; + * -- Move db.db to replace original db in filesystem. + * + * + * Usage + * + * Given the goal of dealing with corruption, it would not be safe to + * create a recovery table in the database being recovered. So + * recovery tables must be created in the temp database. They are not + * appropriate to persist, in any case. [As a bonus, sqlite_master + * tables can be recovered. Perhaps more cute than useful, though.] + * + * The parameters are a specifier for the table to read, and a column + * definition for each bit of data stored in that table. The named + * table must be convertable to a root page number by reading the + * sqlite_master table. Bare table names are assumed to be in + * database 0 ("main"), other databases can be specified in db.table + * fashion. + * + * Column definitions are similar to BUT NOT THE SAME AS those + * provided to CREATE statements: + * column-def: column-name [type-name [STRICT] [NOT NULL]] + * type-name: (ANY|ROWID|INTEGER|FLOAT|NUMERIC|TEXT|BLOB) + * + * Only those exact type names are accepted, there is no type + * intuition. The only constraints accepted are STRICT (see below) + * and NOT NULL. Anything unexpected will cause the create to fail. + * + * ANY is a convenience to indicate that manifest typing is desired. + * It is equivalent to not specifying a type at all. The results for + * such columns will have the type of the data's storage. The exposed + * schema will contain no type for that column. + * + * ROWID is used for columns representing aliases to the rowid + * (INTEGER PRIMARY KEY, with or without AUTOINCREMENT), to make the + * concept explicit. Such columns are actually stored as NULL, so + * they cannot be simply ignored. The exposed schema will be INTEGER + * for that column. + * + * NOT NULL causes rows with a NULL in that column to be skipped. It + * also adds NOT NULL to the column in the exposed schema. If the + * table has ever had columns added using ALTER TABLE, then those + * columns implicitly contain NULL for rows which have not been + * updated. [Workaround using COALESCE() in your SELECT statement.] + * + * The created table is read-only, with no indices. Any SELECT will + * be a full-table scan, returning each valid row read from the + * storage of the backing table. The rowid will be the rowid of the + * row from the backing table. "Valid" means: + * - The cell metadata for the row is well-formed. Mainly this means that + * the cell header info describes a payload of the size indicated by + * the cell's payload size. + * - The cell does not run off the page. + * - The cell does not overlap any other cell on the page. + * - The cell contains doesn't contain too many columns. + * - The types of the serialized data match the indicated types (see below). + * + * + * Type affinity versus type storage. + * + * http://www.sqlite.org/datatype3.html describes SQLite's type + * affinity system. The system provides for automated coercion of + * types in certain cases, transparently enough that many developers + * do not realize that it is happening. Importantly, it implies that + * the raw data stored in the database may not have the obvious type. + * + * Differences between the stored data types and the expected data + * types may be a signal of corruption. This module makes some + * allowances for automatic coercion. It is important to be concious + * of the difference between the schema exposed by the module, and the + * data types read from storage. The following table describes how + * the module interprets things: + * + * type schema data STRICT + * ---- ------ ---- ------ + * ANY <none> any any + * ROWID INTEGER n/a n/a + * INTEGER INTEGER integer integer + * FLOAT FLOAT integer or float float + * NUMERIC NUMERIC integer, float, or text integer or float + * TEXT TEXT text or blob text + * BLOB BLOB blob blob + * + * type is the type provided to the recover module, schema is the + * schema exposed by the module, data is the acceptable types of data + * decoded from storage, and STRICT is a modification of that. + * + * A very loose recovery system might use ANY for all columns, then + * use the appropriate sqlite3_column_*() calls to coerce to expected + * types. This doesn't provide much protection if a page from a + * different table with the same column count is linked into an + * inappropriate btree. + * + * A very tight recovery system might use STRICT to enforce typing on + * all columns, preferring to skip rows which are valid at the storage + * level but don't contain the right types. Note that FLOAT STRICT is + * almost certainly not appropriate, since integral values are + * transparently stored as integers, when that is more efficient. + * + * Another option is to use ANY for all columns and inspect each + * result manually (using sqlite3_column_*). This should only be + * necessary in cases where developers have used manifest typing (test + * to make sure before you decide that you aren't using manifest + * typing!). + * + * + * Caveats + * + * Leaf pages not referenced by interior nodes will not be found. + * + * Leaf pages referenced from interior nodes of other tables will not + * be resolved. + * + * Rows referencing invalid overflow pages will be skipped. + * + * SQlite rows have a header which describes how to interpret the rest + * of the payload. The header can be valid in cases where the rest of + * the record is actually corrupt (in the sense that the data is not + * the intended data). This can especially happen WRT overflow pages, + * as lack of atomic updates between pages is the primary form of + * corruption I have seen in the wild. + */ +/* The implementation is via a series of cursors. The cursor + * implementations follow the pattern: + * + * // Creates the cursor using various initialization info. + * int cursorCreate(...); + * + * // Returns 1 if there is no more data, 0 otherwise. + * int cursorEOF(Cursor *pCursor); + * + * // Various accessors can be used if not at EOF. + * + * // Move to the next item. + * int cursorNext(Cursor *pCursor); + * + * // Destroy the memory associated with the cursor. + * void cursorDestroy(Cursor *pCursor); + * + * References in the following are to sections at + * http://www.sqlite.org/fileformat2.html . + * + * RecoverLeafCursor iterates the records in a leaf table node + * described in section 1.5 "B-tree Pages". When the node is + * exhausted, an interior cursor is used to get the next leaf node, + * and iteration continues there. + * + * RecoverInteriorCursor iterates the child pages in an interior table + * node described in section 1.5 "B-tree Pages". When the node is + * exhausted, a parent interior cursor is used to get the next + * interior node at the same level, and iteration continues there. + * + * Together these record the path from the leaf level to the root of + * the tree. Iteration happens from the leaves rather than the root + * both for efficiency and putting the special case at the front of + * the list is easier to implement. + * + * RecoverCursor uses a RecoverLeafCursor to iterate the rows of a + * table, returning results via the SQLite virtual table interface. + */ +/* TODO(shess): It might be useful to allow DEFAULT in types to + * specify what to do for NULL when an ALTER TABLE case comes up. + * Unfortunately, simply adding it to the exposed schema and using + * sqlite3_result_null() does not cause the default to be generate. + * Handling it ourselves seems hard, unfortunately. + */ + + +/* Internal SQLite things that are used: + * u32, u64, i64 types. + * Btree, Pager, and DbPage structs. + * DbPage.pData, .pPager, and .pgno + * sqlite3 struct. + * sqlite3BtreePager() and sqlite3BtreeGetPageSize() + * sqlite3PagerAcquire() and sqlite3PagerUnref() + * getVarint(). + */ + +/* For debugging. */ +#if 0 +#define FNENTRY() fprintf(stderr, "In %s\n", __FUNCTION__) +#else +#define FNENTRY() +#endif + +/* Generic constants and helper functions. */ + +static const unsigned char kTableLeafPage = 0x0D; +static const unsigned char kTableInteriorPage = 0x05; + +/* From section 1.5. */ +static const unsigned kiPageTypeOffset = 0; +static const unsigned kiPageFreeBlockOffset = 1; +static const unsigned kiPageCellCountOffset = 3; +static const unsigned kiPageCellContentOffset = 5; +static const unsigned kiPageFragmentedBytesOffset = 7; +static const unsigned knPageLeafHeaderBytes = 8; +/* Interior pages contain an additional field. */ +static const unsigned kiPageRightChildOffset = 8; +static const unsigned kiPageInteriorHeaderBytes = 12; + +/* Accepted types are specified by a mask. */ +#define MASK_ROWID (1<<0) +#define MASK_INTEGER (1<<1) +#define MASK_FLOAT (1<<2) +#define MASK_TEXT (1<<3) +#define MASK_BLOB (1<<4) +#define MASK_NULL (1<<5) + +/* Helpers to decode fixed-size fields. */ +static u32 decodeUnsigned16(const unsigned char *pData){ + return (pData[0]<<8) + pData[1]; +} +static u32 decodeUnsigned32(const unsigned char *pData){ + return (decodeUnsigned16(pData)<<16) + decodeUnsigned16(pData+2); +} +static i64 decodeSigned(const unsigned char *pData, unsigned nBytes){ + i64 r = (char)(*pData); + while( --nBytes ){ + r <<= 8; + r += *(++pData); + } + return r; +} +/* Derived from vdbeaux.c, sqlite3VdbeSerialGet(), case 7. */ +/* TODO(shess): Determine if swapMixedEndianFloat() applies. */ +static double decodeFloat64(const unsigned char *pData){ +#if !defined(NDEBUG) + static const u64 t1 = ((u64)0x3ff00000)<<32; + static const double r1 = 1.0; + u64 t2 = t1; + assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); +#endif + i64 x = decodeSigned(pData, 8); + double d; + memcpy(&d, &x, sizeof(x)); + return d; +} + +/* Return true if a varint can safely be read from pData/nData. */ +/* TODO(shess): DbPage points into the middle of a buffer which + * contains the page data before DbPage. So code should always be + * able to read a small number of varints safely. Consider whether to + * trust that or not. + */ +static int checkVarint(const unsigned char *pData, unsigned nData){ + unsigned i; + + /* In the worst case the decoder takes all 8 bits of the 9th byte. */ + if( nData>=9 ){ + return 1; + } + + /* Look for a high-bit-clear byte in what's left. */ + for( i=0; i<nData; ++i ){ + if( !(pData[i]&0x80) ){ + return 1; + } + } + + /* Cannot decode in the space given. */ + return 0; +} + +/* Return 1 if n varints can be read from pData/nData. */ +static int checkVarints(const unsigned char *pData, unsigned nData, + unsigned n){ + unsigned nCur = 0; /* Byte offset within current varint. */ + unsigned nFound = 0; /* Number of varints found. */ + unsigned i; + + /* In the worst case the decoder takes all 8 bits of the 9th byte. */ + if( nData>=9*n ){ + return 1; + } + + for( i=0; nFound<n && i<nData; ++i ){ + nCur++; + if( nCur==9 || !(pData[i]&0x80) ){ + nFound++; + nCur = 0; + } + } + + return nFound==n; +} + +/* ctype and str[n]casecmp() can be affected by locale (eg, tr_TR). + * These versions consider only the ASCII space. + */ +/* TODO(shess): It may be reasonable to just remove the need for these + * entirely. The module could require "TEXT STRICT NOT NULL", not + * "Text Strict Not Null" or whatever the developer felt like typing + * that day. Handling corrupt data is a PERFECT place to be pedantic. + */ +static int ascii_isspace(char c){ + /* From fts3_expr.c */ + return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; +} +static int ascii_isalnum(int x){ + /* From fts3_tokenizer1.c */ + return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z'); +} +static int ascii_tolower(int x){ + /* From fts3_tokenizer1.c */ + return (x>='A' && x<='Z') ? x-'A'+'a' : x; +} +/* TODO(shess): Consider sqlite3_strnicmp() */ +static int ascii_strncasecmp(const char *s1, const char *s2, size_t n){ + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + while( *us1 && *us2 && n && ascii_tolower(*us1)==ascii_tolower(*us2) ){ + us1++, us2++, n--; + } + return n ? ascii_tolower(*us1)-ascii_tolower(*us2) : 0; +} +static int ascii_strcasecmp(const char *s1, const char *s2){ + /* If s2 is equal through strlen(s1), will exit while() due to s1's + * trailing NUL, and return NUL-s2[strlen(s1)]. + */ + return ascii_strncasecmp(s1, s2, strlen(s1)+1); +} + +/* For some reason I kept making mistakes with offset calculations. */ +static const unsigned char *PageData(DbPage *pPage, unsigned iOffset){ + assert( iOffset<=pPage->nPageSize ); + return (unsigned char *)pPage->pData + iOffset; +} + +/* The first page in the file contains a file header in the first 100 + * bytes. The page's header information comes after that. Note that + * the offsets in the page's header information are relative to the + * beginning of the page, NOT the end of the page header. + */ +static const unsigned char *PageHeader(DbPage *pPage){ + if( pPage->pgno==1 ){ + const unsigned nDatabaseHeader = 100; + return PageData(pPage, nDatabaseHeader); + }else{ + return PageData(pPage, 0); + } +} + +/* Helper to fetch the pager and page size for the named database. */ +static int GetPager(sqlite3 *db, const char *zName, + Pager **pPager, unsigned *pnPageSize){ + Btree *pBt = NULL; + int i; + for( i=0; i<db->nDb; ++i ){ + if( ascii_strcasecmp(db->aDb[i].zName, zName)==0 ){ + pBt = db->aDb[i].pBt; + break; + } + } + if( !pBt ){ + return SQLITE_ERROR; + } + + *pPager = sqlite3BtreePager(pBt); + *pnPageSize = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt); + return SQLITE_OK; +} + +/* iSerialType is a type read from a record header. See "2.1 Record Format". + */ + +/* Storage size of iSerialType in bytes. My interpretation of SQLite + * documentation is that text and blob fields can have 32-bit length. + * Values past 2^31-12 will need more than 32 bits to encode, which is + * why iSerialType is u64. + */ +static u32 SerialTypeLength(u64 iSerialType){ + switch( iSerialType ){ + case 0 : return 0; /* NULL */ + case 1 : return 1; /* Various integers. */ + case 2 : return 2; + case 3 : return 3; + case 4 : return 4; + case 5 : return 6; + case 6 : return 8; + case 7 : return 8; /* 64-bit float. */ + case 8 : return 0; /* Constant 0. */ + case 9 : return 0; /* Constant 1. */ + case 10 : case 11 : assert( !"RESERVED TYPE"); return 0; + } + return (u32)((iSerialType>>1) - 6); +} + +/* True if iSerialType refers to a blob. */ +static int SerialTypeIsBlob(u64 iSerialType){ + assert( iSerialType>=12 ); + return (iSerialType%2)==0; +} + +/* Returns true if the serialized type represented by iSerialType is + * compatible with the given type mask. + */ +static int SerialTypeIsCompatible(u64 iSerialType, unsigned char mask){ + switch( iSerialType ){ + case 0 : return (mask&MASK_NULL)!=0; + case 1 : return (mask&MASK_INTEGER)!=0; + case 2 : return (mask&MASK_INTEGER)!=0; + case 3 : return (mask&MASK_INTEGER)!=0; + case 4 : return (mask&MASK_INTEGER)!=0; + case 5 : return (mask&MASK_INTEGER)!=0; + case 6 : return (mask&MASK_INTEGER)!=0; + case 7 : return (mask&MASK_FLOAT)!=0; + case 8 : return (mask&MASK_INTEGER)!=0; + case 9 : return (mask&MASK_INTEGER)!=0; + case 10 : assert( !"RESERVED TYPE"); return 0; + case 11 : assert( !"RESERVED TYPE"); return 0; + } + return (mask&(SerialTypeIsBlob(iSerialType) ? MASK_BLOB : MASK_TEXT)); +} + +/* Versions of strdup() with return values appropriate for + * sqlite3_free(). malloc.c has sqlite3DbStrDup()/NDup(), but those + * need sqlite3DbFree(), which seems intrusive. + */ +static char *sqlite3_strndup(const char *z, unsigned n){ + char *zNew; + + if( z==NULL ){ + return NULL; + } + + zNew = sqlite3_malloc(n+1); + if( zNew!=NULL ){ + memcpy(zNew, z, n); + zNew[n] = '\0'; + } + return zNew; +} +static char *sqlite3_strdup(const char *z){ + if( z==NULL ){ + return NULL; + } + return sqlite3_strndup(z, strlen(z)); +} + +/* Fetch the page number of zTable in zDb from sqlite_master in zDb, + * and put it in *piRootPage. + */ +static int getRootPage(sqlite3 *db, const char *zDb, const char *zTable, + u32 *piRootPage){ + char *zSql; /* SQL selecting root page of named element. */ + sqlite3_stmt *pStmt; + int rc; + + if( strcmp(zTable, "sqlite_master")==0 ){ + *piRootPage = 1; + return SQLITE_OK; + } + + zSql = sqlite3_mprintf("SELECT rootpage FROM %s.sqlite_master " + "WHERE type = 'table' AND tbl_name = %Q", + zDb, zTable); + if( !zSql ){ + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ){ + return rc; + } + + /* Require a result. */ + rc = sqlite3_step(pStmt); + if( rc==SQLITE_DONE ){ + rc = SQLITE_CORRUPT; + }else if( rc==SQLITE_ROW ){ + *piRootPage = sqlite3_column_int(pStmt, 0); + + /* Require only one result. */ + rc = sqlite3_step(pStmt); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + }else if( rc==SQLITE_ROW ){ + rc = SQLITE_CORRUPT; + } + } + sqlite3_finalize(pStmt); + return rc; +} + +static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){ + sqlite3_stmt *pStmt; + int rc; + char *zSql = sqlite3_mprintf("PRAGMA %s.encoding", zDb); + if( !zSql ){ + return SQLITE_NOMEM; + } + + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK ){ + return rc; + } + + /* Require a result. */ + rc = sqlite3_step(pStmt); + if( rc==SQLITE_DONE ){ + /* This case should not be possible. */ + rc = SQLITE_CORRUPT; + }else if( rc==SQLITE_ROW ){ + if( sqlite3_column_type(pStmt, 0)==SQLITE_TEXT ){ + const char* z = (const char *)sqlite3_column_text(pStmt, 0); + /* These strings match the literals in pragma.c. */ + if( !strcmp(z, "UTF-16le") ){ + *piEncoding = SQLITE_UTF16LE; + }else if( !strcmp(z, "UTF-16be") ){ + *piEncoding = SQLITE_UTF16BE; + }else if( !strcmp(z, "UTF-8") ){ + *piEncoding = SQLITE_UTF8; + }else{ + /* This case should not be possible. */ + *piEncoding = SQLITE_UTF8; + } + }else{ + /* This case should not be possible. */ + *piEncoding = SQLITE_UTF8; + } + + /* Require only one result. */ + rc = sqlite3_step(pStmt); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + }else if( rc==SQLITE_ROW ){ + /* This case should not be possible. */ + rc = SQLITE_CORRUPT; + } + } + sqlite3_finalize(pStmt); + return rc; +} + +/* Cursor for iterating interior nodes. Interior page cells contain a + * child page number and a rowid. The child page contains items left + * of the rowid (less than). The rightmost page of the subtree is + * stored in the page header. + * + * interiorCursorDestroy - release all resources associated with the + * cursor and any parent cursors. + * interiorCursorCreate - create a cursor with the given parent and page. + * interiorCursorEOF - returns true if neither the cursor nor the + * parent cursors can return any more data. + * interiorCursorNextPage - fetch the next child page from the cursor. + * + * Logically, interiorCursorNextPage() returns the next child page + * number from the page the cursor is currently reading, calling the + * parent cursor as necessary to get new pages to read, until done. + * SQLITE_ROW if a page is returned, SQLITE_DONE if out of pages, + * error otherwise. Unfortunately, if the table is corrupted + * unexpected pages can be returned. If any unexpected page is found, + * leaf or otherwise, it is returned to the caller for processing, + * with the interior cursor left empty. The next call to + * interiorCursorNextPage() will recurse to the parent cursor until an + * interior page to iterate is returned. + * + * Note that while interiorCursorNextPage() will refuse to follow + * loops, it does not keep track of pages returned for purposes of + * preventing duplication. + * + * Note that interiorCursorEOF() could return false (not at EOF), and + * interiorCursorNextPage() could still return SQLITE_DONE. This + * could happen if there are more cells to iterate in an interior + * page, but those cells refer to invalid pages. + */ +typedef struct RecoverInteriorCursor RecoverInteriorCursor; +struct RecoverInteriorCursor { + RecoverInteriorCursor *pParent; /* Parent node to this node. */ + DbPage *pPage; /* Reference to leaf page. */ + unsigned nPageSize; /* Size of page. */ + unsigned nChildren; /* Number of children on the page. */ + unsigned iChild; /* Index of next child to return. */ +}; + +static void interiorCursorDestroy(RecoverInteriorCursor *pCursor){ + /* Destroy all the cursors to the root. */ + while( pCursor ){ + RecoverInteriorCursor *p = pCursor; + pCursor = pCursor->pParent; + + if( p->pPage ){ + sqlite3PagerUnref(p->pPage); + p->pPage = NULL; + } + + memset(p, 0xA5, sizeof(*p)); + sqlite3_free(p); + } +} + +/* Internal helper. Reset storage in preparation for iterating pPage. */ +static void interiorCursorSetPage(RecoverInteriorCursor *pCursor, + DbPage *pPage){ + assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage ); + + if( pCursor->pPage ){ + sqlite3PagerUnref(pCursor->pPage); + pCursor->pPage = NULL; + } + pCursor->pPage = pPage; + pCursor->iChild = 0; + + /* A child for each cell, plus one in the header. */ + /* TODO(shess): Sanity-check the count? Page header plus per-cell + * cost of 16-bit offset, 32-bit page number, and one varint + * (minimum 1 byte). + */ + pCursor->nChildren = decodeUnsigned16(PageHeader(pPage) + + kiPageCellCountOffset) + 1; +} + +static int interiorCursorCreate(RecoverInteriorCursor *pParent, + DbPage *pPage, int nPageSize, + RecoverInteriorCursor **ppCursor){ + RecoverInteriorCursor *pCursor = + sqlite3_malloc(sizeof(RecoverInteriorCursor)); + if( !pCursor ){ + return SQLITE_NOMEM; + } + + memset(pCursor, 0, sizeof(*pCursor)); + pCursor->pParent = pParent; + pCursor->nPageSize = nPageSize; + interiorCursorSetPage(pCursor, pPage); + *ppCursor = pCursor; + return SQLITE_OK; +} + +/* Internal helper. Return the child page number at iChild. */ +static unsigned interiorCursorChildPage(RecoverInteriorCursor *pCursor){ + const unsigned char *pPageHeader; /* Header of the current page. */ + const unsigned char *pCellOffsets; /* Offset to page's cell offsets. */ + unsigned iCellOffset; /* Offset of target cell. */ + + assert( pCursor->iChild<pCursor->nChildren ); + + /* Rightmost child is in the header. */ + pPageHeader = PageHeader(pCursor->pPage); + if( pCursor->iChild==pCursor->nChildren-1 ){ + return decodeUnsigned32(pPageHeader + kiPageRightChildOffset); + } + + /* Each cell is a 4-byte integer page number and a varint rowid + * which is greater than the rowid of items in that sub-tree (this + * module ignores ordering). The offset is from the beginning of the + * page, not from the page header. + */ + pCellOffsets = pPageHeader + kiPageInteriorHeaderBytes; + iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iChild*2); + if( iCellOffset<=pCursor->nPageSize-4 ){ + return decodeUnsigned32(PageData(pCursor->pPage, iCellOffset)); + } + + /* TODO(shess): Check for cell overlaps? Cells require 4 bytes plus + * a varint. Check could be identical to leaf check (or even a + * shared helper testing for "Cells starting in this range"?). + */ + + /* If the offset is broken, return an invalid page number. */ + return 0; +} + +static int interiorCursorEOF(RecoverInteriorCursor *pCursor){ + /* Find a parent with remaining children. EOF if none found. */ + while( pCursor && pCursor->iChild>=pCursor->nChildren ){ + pCursor = pCursor->pParent; + } + return pCursor==NULL; +} + +/* Internal helper. Used to detect if iPage would cause a loop. */ +static int interiorCursorPageInUse(RecoverInteriorCursor *pCursor, + unsigned iPage){ + /* Find any parent using the indicated page. */ + while( pCursor && pCursor->pPage->pgno!=iPage ){ + pCursor = pCursor->pParent; + } + return pCursor!=NULL; +} + +/* Get the next page from the interior cursor at *ppCursor. Returns + * SQLITE_ROW with the page in *ppPage, or SQLITE_DONE if out of + * pages, or the error SQLite returned. + * + * If the tree is uneven, then when the cursor attempts to get a new + * interior page from the parent cursor, it may get a non-interior + * page. In that case, the new page is returned, and *ppCursor is + * updated to point to the parent cursor (this cursor is freed). + */ +/* TODO(shess): I've tried to avoid recursion in most of this code, + * but this case is more challenging because the recursive call is in + * the middle of operation. One option for converting it without + * adding memory management would be to retain the head pointer and + * use a helper to "back up" as needed. Another option would be to + * reverse the list during traversal. + */ +static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, + DbPage **ppPage){ + RecoverInteriorCursor *pCursor = *ppCursor; + while( 1 ){ + int rc; + const unsigned char *pPageHeader; /* Header of found page. */ + + /* Find a valid child page which isn't on the stack. */ + while( pCursor->iChild<pCursor->nChildren ){ + const unsigned iPage = interiorCursorChildPage(pCursor); + pCursor->iChild++; + if( interiorCursorPageInUse(pCursor, iPage) ){ + fprintf(stderr, "Loop detected at %d\n", iPage); + }else{ + int rc = sqlite3PagerAcquire(pCursor->pPage->pPager, iPage, ppPage, 0); + if( rc==SQLITE_OK ){ + return SQLITE_ROW; + } + } + } + + /* This page has no more children. Get next page from parent. */ + if( !pCursor->pParent ){ + return SQLITE_DONE; + } + rc = interiorCursorNextPage(&pCursor->pParent, ppPage); + if( rc!=SQLITE_ROW ){ + return rc; + } + + /* If a non-interior page is received, that either means that the + * tree is uneven, or that a child was re-used (say as an overflow + * page). Remove this cursor and let the caller handle the page. + */ + pPageHeader = PageHeader(*ppPage); + if( pPageHeader[kiPageTypeOffset]!=kTableInteriorPage ){ + *ppCursor = pCursor->pParent; + pCursor->pParent = NULL; + interiorCursorDestroy(pCursor); + return SQLITE_ROW; + } + + /* Iterate the new page. */ + interiorCursorSetPage(pCursor, *ppPage); + *ppPage = NULL; + } + + assert(NULL); /* NOTREACHED() */ + return SQLITE_CORRUPT; +} + +/* Large rows are spilled to overflow pages. The row's main page + * stores the overflow page number after the local payload, with a + * linked list forward from there as necessary. overflowMaybeCreate() + * and overflowGetSegment() provide an abstraction for accessing such + * data while centralizing the code. + * + * overflowDestroy - releases all resources associated with the structure. + * overflowMaybeCreate - create the overflow structure if it is needed + * to represent the given record. See function comment. + * overflowGetSegment - fetch a segment from the record, accounting + * for overflow pages. Segments which are not + * entirely contained with a page are constructed + * into a buffer which is returned. See function comment. + */ +typedef struct RecoverOverflow RecoverOverflow; +struct RecoverOverflow { + RecoverOverflow *pNextOverflow; + DbPage *pPage; + unsigned nPageSize; +}; + +static void overflowDestroy(RecoverOverflow *pOverflow){ + while( pOverflow ){ + RecoverOverflow *p = pOverflow; + pOverflow = p->pNextOverflow; + + if( p->pPage ){ + sqlite3PagerUnref(p->pPage); + p->pPage = NULL; + } + + memset(p, 0xA5, sizeof(*p)); + sqlite3_free(p); + } +} + +/* Internal helper. Used to detect if iPage would cause a loop. */ +static int overflowPageInUse(RecoverOverflow *pOverflow, unsigned iPage){ + while( pOverflow && pOverflow->pPage->pgno!=iPage ){ + pOverflow = pOverflow->pNextOverflow; + } + return pOverflow!=NULL; +} + +/* Setup to access an nRecordBytes record beginning at iRecordOffset + * in pPage. If nRecordBytes can be satisfied entirely from pPage, + * then no overflow pages are needed an *pnLocalRecordBytes is set to + * nRecordBytes. Otherwise, *ppOverflow is set to the head of a list + * of overflow pages, and *pnLocalRecordBytes is set to the number of + * bytes local to pPage. + * + * overflowGetSegment() will do the right thing regardless of whether + * those values are set to be in-page or not. + */ +static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, + unsigned iRecordOffset, unsigned nRecordBytes, + unsigned *pnLocalRecordBytes, + RecoverOverflow **ppOverflow){ + unsigned nLocalRecordBytes; /* Record bytes in the leaf page. */ + unsigned iNextPage; /* Next page number for record data. */ + unsigned nBytes; /* Maximum record bytes as of current page. */ + int rc; + RecoverOverflow *pFirstOverflow; /* First in linked list of pages. */ + RecoverOverflow *pLastOverflow; /* End of linked list. */ + + /* Calculations from the "Table B-Tree Leaf Cell" part of section + * 1.5 of http://www.sqlite.org/fileformat2.html . maxLocal and + * minLocal to match naming in btree.c. + */ + const unsigned maxLocal = nPageSize - 35; + const unsigned minLocal = ((nPageSize-12)*32/255)-23; /* m */ + + /* Always fit anything smaller than maxLocal. */ + if( nRecordBytes<=maxLocal ){ + *pnLocalRecordBytes = nRecordBytes; + *ppOverflow = NULL; + return SQLITE_OK; + } + + /* Calculate the remainder after accounting for minLocal on the leaf + * page and what packs evenly into overflow pages. If the remainder + * does not fit into maxLocal, then a partially-full overflow page + * will be required in any case, so store as little as possible locally. + */ + nLocalRecordBytes = minLocal+((nRecordBytes-minLocal)%(nPageSize-4)); + if( maxLocal<nLocalRecordBytes ){ + nLocalRecordBytes = minLocal; + } + + /* Don't read off the end of the page. */ + if( iRecordOffset+nLocalRecordBytes+4>nPageSize ){ + return SQLITE_CORRUPT; + } + + /* First overflow page number is after the local bytes. */ + iNextPage = + decodeUnsigned32(PageData(pPage, iRecordOffset + nLocalRecordBytes)); + nBytes = nLocalRecordBytes; + + /* While there are more pages to read, and more bytes are needed, + * get another page. + */ + pFirstOverflow = pLastOverflow = NULL; + rc = SQLITE_OK; + while( iNextPage && nBytes<nRecordBytes ){ + RecoverOverflow *pOverflow; /* New overflow page for the list. */ + + rc = sqlite3PagerAcquire(pPage->pPager, iNextPage, &pPage, 0); + if( rc!=SQLITE_OK ){ + break; + } + + pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); + if( !pOverflow ){ + sqlite3PagerUnref(pPage); + rc = SQLITE_NOMEM; + break; + } + memset(pOverflow, 0, sizeof(*pOverflow)); + pOverflow->pPage = pPage; + pOverflow->nPageSize = nPageSize; + + if( !pFirstOverflow ){ + pFirstOverflow = pOverflow; + }else{ + pLastOverflow->pNextOverflow = pOverflow; + } + pLastOverflow = pOverflow; + + iNextPage = decodeUnsigned32(pPage->pData); + nBytes += nPageSize-4; + + /* Avoid loops. */ + if( overflowPageInUse(pFirstOverflow, iNextPage) ){ + fprintf(stderr, "Overflow loop detected at %d\n", iNextPage); + rc = SQLITE_CORRUPT; + break; + } + } + + /* If there were not enough pages, or too many, things are corrupt. + * Not having enough pages is an obvious problem, all the data + * cannot be read. Too many pages means that the contents of the + * row between the main page and the overflow page(s) is + * inconsistent (most likely one or more of the overflow pages does + * not really belong to this row). + */ + if( rc==SQLITE_OK && (nBytes<nRecordBytes || iNextPage) ){ + rc = SQLITE_CORRUPT; + } + + if( rc==SQLITE_OK ){ + *ppOverflow = pFirstOverflow; + *pnLocalRecordBytes = nLocalRecordBytes; + }else if( pFirstOverflow ){ + overflowDestroy(pFirstOverflow); + } + return rc; +} + +/* Use in concert with overflowMaybeCreate() to efficiently read parts + * of a potentially-overflowing record. pPage and iRecordOffset are + * the values passed into overflowMaybeCreate(), nLocalRecordBytes and + * pOverflow are the values returned by that call. + * + * On SQLITE_OK, *ppBase points to nRequestBytes of data at + * iRequestOffset within the record. If the data exists contiguously + * in a page, a direct pointer is returned, otherwise a buffer from + * sqlite3_malloc() is returned with the data. *pbFree is set true if + * sqlite3_free() should be called on *ppBase. + */ +/* Operation of this function is subtle. At any time, pPage is the + * current page, with iRecordOffset and nLocalRecordBytes being record + * data within pPage, and pOverflow being the overflow page after + * pPage. This allows the code to handle both the initial leaf page + * and overflow pages consistently by adjusting the values + * appropriately. + */ +static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, + unsigned nLocalRecordBytes, + RecoverOverflow *pOverflow, + unsigned iRequestOffset, unsigned nRequestBytes, + unsigned char **ppBase, int *pbFree){ + unsigned nBase; /* Amount of data currently collected. */ + unsigned char *pBase; /* Buffer to collect record data into. */ + + /* Skip to the page containing the start of the data. */ + while( iRequestOffset>=nLocalRecordBytes && pOverflow ){ + /* Factor out current page's contribution. */ + iRequestOffset -= nLocalRecordBytes; + + /* Move forward to the next page in the list. */ + pPage = pOverflow->pPage; + iRecordOffset = 4; + nLocalRecordBytes = pOverflow->nPageSize - iRecordOffset; + pOverflow = pOverflow->pNextOverflow; + } + + /* If the requested data is entirely within this page, return a + * pointer into the page. + */ + if( iRequestOffset+nRequestBytes<=nLocalRecordBytes ){ + /* TODO(shess): "assignment discards qualifiers from pointer target type" + * Having ppBase be const makes sense, but sqlite3_free() takes non-const. + */ + *ppBase = (unsigned char *)PageData(pPage, iRecordOffset + iRequestOffset); + *pbFree = 0; + return SQLITE_OK; + } + + /* The data range would require additional pages. */ + if( !pOverflow ){ + /* Should never happen, the range is outside the nRecordBytes + * passed to overflowMaybeCreate(). + */ + assert(NULL); /* NOTREACHED */ + return SQLITE_ERROR; + } + + /* Get a buffer to construct into. */ + nBase = 0; + pBase = sqlite3_malloc(nRequestBytes); + if( !pBase ){ + return SQLITE_NOMEM; + } + while( nBase<nRequestBytes ){ + /* Copy over data present on this page. */ + unsigned nCopyBytes = nRequestBytes - nBase; + if( nLocalRecordBytes-iRequestOffset<nCopyBytes ){ + nCopyBytes = nLocalRecordBytes - iRequestOffset; + } + memcpy(pBase + nBase, PageData(pPage, iRecordOffset + iRequestOffset), + nCopyBytes); + nBase += nCopyBytes; + + if( pOverflow ){ + /* Copy from start of record data in future pages. */ + iRequestOffset = 0; + + /* Move forward to the next page in the list. Should match + * first while() loop. + */ + pPage = pOverflow->pPage; + iRecordOffset = 4; + nLocalRecordBytes = pOverflow->nPageSize - iRecordOffset; + pOverflow = pOverflow->pNextOverflow; + }else if( nBase<nRequestBytes ){ + /* Ran out of overflow pages with data left to deliver. Not + * possible if the requested range fits within nRecordBytes + * passed to overflowMaybeCreate() when creating pOverflow. + */ + assert(NULL); /* NOTREACHED */ + sqlite3_free(pBase); + return SQLITE_ERROR; + } + } + assert( nBase==nRequestBytes ); + *ppBase = pBase; + *pbFree = 1; + return SQLITE_OK; +} + +/* Primary structure for iterating the contents of a table. + * + * leafCursorDestroy - release all resources associated with the cursor. + * leafCursorCreate - create a cursor to iterate items from tree at + * the provided root page. + * leafCursorNextValidCell - get the cursor ready to access data from + * the next valid cell in the table. + * leafCursorCellRowid - get the current cell's rowid. + * leafCursorCellColumns - get current cell's column count. + * leafCursorCellColInfo - get type and data for a column in current cell. + * + * leafCursorNextValidCell skips cells which fail simple integrity + * checks, such as overlapping other cells, or being located at + * impossible offsets, or where header data doesn't correctly describe + * payload data. Returns SQLITE_ROW if a valid cell is found, + * SQLITE_DONE if all pages in the tree were exhausted. + * + * leafCursorCellColInfo() accounts for overflow pages in the style of + * overflowGetSegment(). + */ +typedef struct RecoverLeafCursor RecoverLeafCursor; +struct RecoverLeafCursor { + RecoverInteriorCursor *pParent; /* Parent node to this node. */ + DbPage *pPage; /* Reference to leaf page. */ + unsigned nPageSize; /* Size of pPage. */ + unsigned nCells; /* Number of cells in pPage. */ + unsigned iCell; /* Current cell. */ + + /* Info parsed from data in iCell. */ + i64 iRowid; /* rowid parsed. */ + unsigned nRecordCols; /* how many items in the record. */ + u64 iRecordOffset; /* offset to record data. */ + /* TODO(shess): nRecordBytes and nRecordHeaderBytes are used in + * leafCursorCellColInfo() to prevent buffer overruns. + * leafCursorCellDecode() already verified that the cell is valid, so + * those checks should be redundant. + */ + u64 nRecordBytes; /* Size of record data. */ + unsigned nLocalRecordBytes; /* Amount of record data in-page. */ + unsigned nRecordHeaderBytes; /* Size of record header data. */ + unsigned char *pRecordHeader; /* Pointer to record header data. */ + int bFreeRecordHeader; /* True if record header requires free. */ + RecoverOverflow *pOverflow; /* Cell overflow info, if needed. */ +}; + +/* Internal helper shared between next-page and create-cursor. If + * pPage is a leaf page, it will be stored in the cursor and state + * initialized for reading cells. + * + * If pPage is an interior page, a new parent cursor is created and + * injected on the stack. This is necessary to handle trees with + * uneven depth, but also is used during initial setup. + * + * If pPage is not a table page at all, it is discarded. + * + * If SQLITE_OK is returned, the caller no longer owns pPage, + * otherwise the caller is responsible for discarding it. + */ +static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){ + const unsigned char *pPageHeader; /* Header of *pPage */ + + /* Release the current page. */ + if( pCursor->pPage ){ + sqlite3PagerUnref(pCursor->pPage); + pCursor->pPage = NULL; + pCursor->iCell = pCursor->nCells = 0; + } + + /* If the page is an unexpected interior node, inject a new stack + * layer and try again from there. + */ + pPageHeader = PageHeader(pPage); + if( pPageHeader[kiPageTypeOffset]==kTableInteriorPage ){ + RecoverInteriorCursor *pParent; + int rc = interiorCursorCreate(pCursor->pParent, pPage, pCursor->nPageSize, + &pParent); + if( rc!=SQLITE_OK ){ + return rc; + } + pCursor->pParent = pParent; + return SQLITE_OK; + } + + /* Not a leaf page, skip it. */ + if( pPageHeader[kiPageTypeOffset]!=kTableLeafPage ){ + sqlite3PagerUnref(pPage); + return SQLITE_OK; + } + + /* Take ownership of the page and start decoding. */ + pCursor->pPage = pPage; + pCursor->iCell = 0; + pCursor->nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset); + return SQLITE_OK; +} + +/* Get the next leaf-level page in the tree. Returns SQLITE_ROW when + * a leaf page is found, SQLITE_DONE when no more leaves exist, or any + * error which occurred. + */ +static int leafCursorNextPage(RecoverLeafCursor *pCursor){ + if( !pCursor->pParent ){ + return SQLITE_DONE; + } + + /* Repeatedly load the parent's next child page until a leaf is found. */ + do { + DbPage *pNextPage; + int rc = interiorCursorNextPage(&pCursor->pParent, &pNextPage); + if( rc!=SQLITE_ROW ){ + assert( rc==SQLITE_DONE ); + return rc; + } + + rc = leafCursorLoadPage(pCursor, pNextPage); + if( rc!=SQLITE_OK ){ + sqlite3PagerUnref(pNextPage); + return rc; + } + } while( !pCursor->pPage ); + + return SQLITE_ROW; +} + +static void leafCursorDestroyCellData(RecoverLeafCursor *pCursor){ + if( pCursor->bFreeRecordHeader ){ + sqlite3_free(pCursor->pRecordHeader); + } + pCursor->bFreeRecordHeader = 0; + pCursor->pRecordHeader = NULL; + + if( pCursor->pOverflow ){ + overflowDestroy(pCursor->pOverflow); + pCursor->pOverflow = NULL; + } +} + +static void leafCursorDestroy(RecoverLeafCursor *pCursor){ + leafCursorDestroyCellData(pCursor); + + if( pCursor->pParent ){ + interiorCursorDestroy(pCursor->pParent); + pCursor->pParent = NULL; + } + + if( pCursor->pPage ){ + sqlite3PagerUnref(pCursor->pPage); + pCursor->pPage = NULL; + } + + memset(pCursor, 0xA5, sizeof(*pCursor)); + sqlite3_free(pCursor); +} + +/* Create a cursor to iterate the rows from the leaf pages of a table + * rooted at iRootPage. + */ +/* TODO(shess): recoverOpen() calls this to setup the cursor, and I + * think that recoverFilter() may make a hard assumption that the + * cursor returned will turn up at least one valid cell. + * + * The cases I can think of which break this assumption are: + * - pPage is a valid leaf page with no valid cells. + * - pPage is a valid interior page with no valid leaves. + * - pPage is a valid interior page who's leaves contain no valid cells. + * - pPage is not a valid leaf or interior page. + */ +static int leafCursorCreate(Pager *pPager, unsigned nPageSize, + u32 iRootPage, RecoverLeafCursor **ppCursor){ + DbPage *pPage; /* Reference to page at iRootPage. */ + RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */ + int rc; + + /* Start out with the root page. */ + rc = sqlite3PagerAcquire(pPager, iRootPage, &pPage, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + + pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); + if( !pCursor ){ + sqlite3PagerUnref(pPage); + return SQLITE_NOMEM; + } + memset(pCursor, 0, sizeof(*pCursor)); + + pCursor->nPageSize = nPageSize; + + rc = leafCursorLoadPage(pCursor, pPage); + if( rc!=SQLITE_OK ){ + sqlite3PagerUnref(pPage); + leafCursorDestroy(pCursor); + return rc; + } + + /* pPage wasn't a leaf page, find the next leaf page. */ + if( !pCursor->pPage ){ + rc = leafCursorNextPage(pCursor); + if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ){ + leafCursorDestroy(pCursor); + return rc; + } + } + + *ppCursor = pCursor; + return SQLITE_OK; +} + +/* Useful for setting breakpoints. */ +static int ValidateError(){ + return SQLITE_ERROR; +} + +/* Setup the cursor for reading the information from cell iCell. */ +static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ + const unsigned char *pPageHeader; /* Header of current page. */ + const unsigned char *pCellOffsets; /* Pointer to page's cell offsets. */ + unsigned iCellOffset; /* Offset of current cell (iCell). */ + const unsigned char *pCell; /* Pointer to data at iCellOffset. */ + unsigned nCellMaxBytes; /* Maximum local size of iCell. */ + unsigned iEndOffset; /* End of iCell's in-page data. */ + u64 nRecordBytes; /* Expected size of cell, w/overflow. */ + u64 iRowid; /* iCell's rowid (in table). */ + unsigned nRead; /* Amount of cell read. */ + unsigned nRecordHeaderRead; /* Header data read. */ + u64 nRecordHeaderBytes; /* Header size expected. */ + unsigned nRecordCols; /* Columns read from header. */ + u64 nRecordColBytes; /* Bytes in payload for those columns. */ + unsigned i; + int rc; + + assert( pCursor->iCell<pCursor->nCells ); + + leafCursorDestroyCellData(pCursor); + + /* Find the offset to the row. */ + pPageHeader = PageHeader(pCursor->pPage); + pCellOffsets = pPageHeader + knPageLeafHeaderBytes; + iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iCell*2); + if( iCellOffset>=pCursor->nPageSize ){ + return ValidateError(); + } + + pCell = PageData(pCursor->pPage, iCellOffset); + nCellMaxBytes = pCursor->nPageSize - iCellOffset; + + /* B-tree leaf cells lead with varint record size, varint rowid and + * varint header size. + */ + /* TODO(shess): The smallest page size is 512 bytes, which has an m + * of 39. Three varints need at most 27 bytes to encode. I think. + */ + if( !checkVarints(pCell, nCellMaxBytes, 3) ){ + return ValidateError(); + } + + nRead = getVarint(pCell, &nRecordBytes); + assert( iCellOffset+nRead<=pCursor->nPageSize ); + pCursor->nRecordBytes = nRecordBytes; + + nRead += getVarint(pCell + nRead, &iRowid); + assert( iCellOffset+nRead<=pCursor->nPageSize ); + pCursor->iRowid = (i64)iRowid; + + pCursor->iRecordOffset = iCellOffset + nRead; + + /* Start overflow setup here because nLocalRecordBytes is needed to + * check cell overlap. + */ + rc = overflowMaybeCreate(pCursor->pPage, pCursor->nPageSize, + pCursor->iRecordOffset, pCursor->nRecordBytes, + &pCursor->nLocalRecordBytes, + &pCursor->pOverflow); + if( rc!=SQLITE_OK ){ + return ValidateError(); + } + + /* Check that no other cell starts within this cell. */ + iEndOffset = pCursor->iRecordOffset + pCursor->nLocalRecordBytes; + for( i=0; i<pCursor->nCells; ++i ){ + const unsigned iOtherOffset = decodeUnsigned16(pCellOffsets + i*2); + if( iOtherOffset>iCellOffset && iOtherOffset<iEndOffset ){ + return ValidateError(); + } + } + + nRecordHeaderRead = getVarint(pCell + nRead, &nRecordHeaderBytes); + assert( nRecordHeaderBytes<=nRecordBytes ); + pCursor->nRecordHeaderBytes = nRecordHeaderBytes; + + /* Large headers could overflow if pages are small. */ + rc = overflowGetSegment(pCursor->pPage, + pCursor->iRecordOffset, pCursor->nLocalRecordBytes, + pCursor->pOverflow, 0, nRecordHeaderBytes, + &pCursor->pRecordHeader, &pCursor->bFreeRecordHeader); + if( rc!=SQLITE_OK ){ + return ValidateError(); + } + + /* Tally up the column count and size of data. */ + nRecordCols = 0; + nRecordColBytes = 0; + while( nRecordHeaderRead<nRecordHeaderBytes ){ + u64 iSerialType; /* Type descriptor for current column. */ + if( !checkVarint(pCursor->pRecordHeader + nRecordHeaderRead, + nRecordHeaderBytes - nRecordHeaderRead) ){ + return ValidateError(); + } + nRecordHeaderRead += getVarint(pCursor->pRecordHeader + nRecordHeaderRead, + &iSerialType); + if( iSerialType==10 || iSerialType==11 ){ + return ValidateError(); + } + nRecordColBytes += SerialTypeLength(iSerialType); + nRecordCols++; + } + pCursor->nRecordCols = nRecordCols; + + /* Parsing the header used as many bytes as expected. */ + if( nRecordHeaderRead!=nRecordHeaderBytes ){ + return ValidateError(); + } + + /* Calculated record is size of expected record. */ + if( nRecordHeaderBytes+nRecordColBytes!=nRecordBytes ){ + return ValidateError(); + } + + return SQLITE_OK; +} + +static i64 leafCursorCellRowid(RecoverLeafCursor *pCursor){ + return pCursor->iRowid; +} + +static unsigned leafCursorCellColumns(RecoverLeafCursor *pCursor){ + return pCursor->nRecordCols; +} + +/* Get the column info for the cell. Pass NULL for ppBase to prevent + * retrieving the data segment. If *pbFree is true, *ppBase must be + * freed by the caller using sqlite3_free(). + */ +static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, + unsigned iCol, u64 *piColType, + unsigned char **ppBase, int *pbFree){ + const unsigned char *pRecordHeader; /* Current cell's header. */ + u64 nRecordHeaderBytes; /* Bytes in pRecordHeader. */ + unsigned nRead; /* Bytes read from header. */ + u64 iColEndOffset; /* Offset to end of column in cell. */ + unsigned nColsSkipped; /* Count columns as procesed. */ + u64 iSerialType; /* Type descriptor for current column. */ + + /* Implicit NULL for columns past the end. This case happens when + * rows have not been updated since an ALTER TABLE added columns. + * It is more convenient to address here than in callers. + */ + if( iCol>=pCursor->nRecordCols ){ + *piColType = 0; + if( ppBase ){ + *ppBase = 0; + *pbFree = 0; + } + return SQLITE_OK; + } + + /* Must be able to decode header size. */ + pRecordHeader = pCursor->pRecordHeader; + if( !checkVarint(pRecordHeader, pCursor->nRecordHeaderBytes) ){ + return SQLITE_CORRUPT; + } + + /* Rather than caching the header size and how many bytes it took, + * decode it every time. + */ + nRead = getVarint(pRecordHeader, &nRecordHeaderBytes); + assert( nRecordHeaderBytes==pCursor->nRecordHeaderBytes ); + + /* Scan forward to the indicated column. Scans to _after_ column + * for later range checking. + */ + /* TODO(shess): This could get expensive for very wide tables. An + * array of iSerialType could be built in leafCursorCellDecode(), but + * the number of columns is dynamic per row, so it would add memory + * management complexity. Enough info to efficiently forward + * iterate could be kept, if all clients forward iterate + * (recoverColumn() may not). + */ + iColEndOffset = 0; + nColsSkipped = 0; + while( nColsSkipped<=iCol && nRead<nRecordHeaderBytes ){ + if( !checkVarint(pRecordHeader + nRead, nRecordHeaderBytes - nRead) ){ + return SQLITE_CORRUPT; + } + nRead += getVarint(pRecordHeader + nRead, &iSerialType); + iColEndOffset += SerialTypeLength(iSerialType); + nColsSkipped++; + } + + /* Column's data extends past record's end. */ + if( nRecordHeaderBytes+iColEndOffset>pCursor->nRecordBytes ){ + return SQLITE_CORRUPT; + } + + *piColType = iSerialType; + if( ppBase ){ + const u32 nColBytes = SerialTypeLength(iSerialType); + + /* Offset from start of record to beginning of column. */ + const unsigned iColOffset = nRecordHeaderBytes+iColEndOffset-nColBytes; + + return overflowGetSegment(pCursor->pPage, pCursor->iRecordOffset, + pCursor->nLocalRecordBytes, pCursor->pOverflow, + iColOffset, nColBytes, ppBase, pbFree); + } + return SQLITE_OK; +} + +static int leafCursorNextValidCell(RecoverLeafCursor *pCursor){ + while( 1 ){ + int rc; + + /* Move to the next cell. */ + pCursor->iCell++; + + /* No more cells, get the next leaf. */ + if( pCursor->iCell>=pCursor->nCells ){ + rc = leafCursorNextPage(pCursor); + if( rc!=SQLITE_ROW ){ + return rc; + } + assert( pCursor->iCell==0 ); + } + + /* If the cell is valid, indicate that a row is available. */ + rc = leafCursorCellDecode(pCursor); + if( rc==SQLITE_OK ){ + return SQLITE_ROW; + } + + /* Iterate until done or a valid row is found. */ + /* TODO(shess): Remove debugging output. */ + fprintf(stderr, "Skipping invalid cell\n"); + } + return SQLITE_ERROR; +} + +typedef struct Recover Recover; +struct Recover { + sqlite3_vtab base; + sqlite3 *db; /* Host database connection */ + char *zDb; /* Database containing target table */ + char *zTable; /* Target table */ + unsigned nCols; /* Number of columns in target table */ + unsigned char *pTypes; /* Types of columns in target table */ +}; + +/* Internal helper for deleting the module. */ +static void recoverRelease(Recover *pRecover){ + sqlite3_free(pRecover->zDb); + sqlite3_free(pRecover->zTable); + sqlite3_free(pRecover->pTypes); + memset(pRecover, 0xA5, sizeof(*pRecover)); + sqlite3_free(pRecover); +} + +/* Helper function for initializing the module. Forward-declared so + * recoverCreate() and recoverConnect() can see it. + */ +static int recoverInit( + sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char ** +); + +static int recoverCreate( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + FNENTRY(); + return recoverInit(db, pAux, argc, argv, ppVtab, pzErr); +} + +/* This should never be called. */ +static int recoverConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + FNENTRY(); + return recoverInit(db, pAux, argc, argv, ppVtab, pzErr); +} + +/* No indices supported. */ +static int recoverBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ + FNENTRY(); + return SQLITE_OK; +} + +/* Logically, this should never be called. */ +static int recoverDisconnect(sqlite3_vtab *pVtab){ + FNENTRY(); + recoverRelease((Recover*)pVtab); + return SQLITE_OK; +} + +static int recoverDestroy(sqlite3_vtab *pVtab){ + FNENTRY(); + recoverRelease((Recover*)pVtab); + return SQLITE_OK; +} + +typedef struct RecoverCursor RecoverCursor; +struct RecoverCursor { + sqlite3_vtab_cursor base; + RecoverLeafCursor *pLeafCursor; + int iEncoding; + int bEOF; +}; + +static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ + Recover *pRecover = (Recover*)pVTab; + u32 iRootPage; /* Root page of the backing table. */ + int iEncoding; /* UTF encoding for backing database. */ + unsigned nPageSize; /* Size of pages in backing database. */ + Pager *pPager; /* Backing database pager. */ + RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */ + RecoverCursor *pCursor; /* Cursor to read rows from leaves. */ + int rc; + + FNENTRY(); + + iRootPage = 0; + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, + &iRootPage); + if( rc!=SQLITE_OK ){ + return rc; + } + + iEncoding = 0; + rc = getEncoding(pRecover->db, pRecover->zDb, &iEncoding); + if( rc!=SQLITE_OK ){ + return rc; + } + + rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize); + if( rc!=SQLITE_OK ){ + return rc; + } + + rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor); + if( rc!=SQLITE_OK ){ + return rc; + } + + pCursor = sqlite3_malloc(sizeof(RecoverCursor)); + if( !pCursor ){ + leafCursorDestroy(pLeafCursor); + return SQLITE_NOMEM; + } + memset(pCursor, 0, sizeof(*pCursor)); + pCursor->base.pVtab = pVTab; + pCursor->pLeafCursor = pLeafCursor; + pCursor->iEncoding = iEncoding; + + *ppCursor = (sqlite3_vtab_cursor*)pCursor; + return SQLITE_OK; +} + +static int recoverClose(sqlite3_vtab_cursor *cur){ + RecoverCursor *pCursor = (RecoverCursor*)cur; + FNENTRY(); + if( pCursor->pLeafCursor ){ + leafCursorDestroy(pCursor->pLeafCursor); + pCursor->pLeafCursor = NULL; + } + memset(pCursor, 0xA5, sizeof(*pCursor)); + sqlite3_free(cur); + return SQLITE_OK; +} + +/* Helpful place to set a breakpoint. */ +static int RecoverInvalidCell(){ + return SQLITE_ERROR; +} + +/* Returns SQLITE_OK if the cell has an appropriate number of columns + * with the appropriate types of data. + */ +static int recoverValidateLeafCell(Recover *pRecover, RecoverCursor *pCursor){ + unsigned i; + + /* If the row's storage has too many columns, skip it. */ + if( leafCursorCellColumns(pCursor->pLeafCursor)>pRecover->nCols ){ + return RecoverInvalidCell(); + } + + /* Skip rows with unexpected types. */ + for( i=0; i<pRecover->nCols; ++i ){ + u64 iType; /* Storage type of column i. */ + int rc; + + /* ROWID alias. */ + if( (pRecover->pTypes[i]&MASK_ROWID) ){ + continue; + } + + rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iType, NULL, NULL); + assert( rc==SQLITE_OK ); + if( rc!=SQLITE_OK || !SerialTypeIsCompatible(iType, pRecover->pTypes[i]) ){ + return RecoverInvalidCell(); + } + } + + return SQLITE_OK; +} + +static int recoverNext(sqlite3_vtab_cursor *pVtabCursor){ + RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + Recover *pRecover = (Recover*)pCursor->base.pVtab; + int rc; + + FNENTRY(); + + /* Scan forward to the next cell with valid storage, then check that + * the stored data matches the schema. + */ + while( (rc = leafCursorNextValidCell(pCursor->pLeafCursor))==SQLITE_ROW ){ + if( recoverValidateLeafCell(pRecover, pCursor)==SQLITE_OK ){ + return SQLITE_OK; + } + } + + if( rc==SQLITE_DONE ){ + pCursor->bEOF = 1; + return SQLITE_OK; + } + + assert( rc!=SQLITE_OK ); + return rc; +} + +static int recoverFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + Recover *pRecover = (Recover*)pCursor->base.pVtab; + int rc; + + FNENTRY(); + + /* Load the first cell, and iterate forward if it's not valid. */ + /* TODO(shess): What happens if no cells at all are valid? */ + rc = leafCursorCellDecode(pCursor->pLeafCursor); + if( rc!=SQLITE_OK || recoverValidateLeafCell(pRecover, pCursor)!=SQLITE_OK ){ + return recoverNext(pVtabCursor); + } + + return SQLITE_OK; +} + +static int recoverEof(sqlite3_vtab_cursor *pVtabCursor){ + RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + FNENTRY(); + return pCursor->bEOF; +} + +static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ + RecoverCursor *pCursor = (RecoverCursor*)cur; + Recover *pRecover = (Recover*)pCursor->base.pVtab; + u64 iColType; /* Storage type of column i. */ + unsigned char *pColData; /* Column i's data. */ + int shouldFree; /* Non-zero if pColData should be freed. */ + int rc; + + FNENTRY(); + + if( i>=pRecover->nCols ){ + return SQLITE_ERROR; + } + + /* ROWID alias. */ + if( (pRecover->pTypes[i]&MASK_ROWID) ){ + sqlite3_result_int64(ctx, leafCursorCellRowid(pCursor->pLeafCursor)); + return SQLITE_OK; + } + + pColData = NULL; + shouldFree = 0; + rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iColType, + &pColData, &shouldFree); + if( rc!=SQLITE_OK ){ + return rc; + } + /* recoverValidateLeafCell() should guarantee that this will never + * occur. + */ + if( !SerialTypeIsCompatible(iColType, pRecover->pTypes[i]) ){ + if( shouldFree ){ + sqlite3_free(pColData); + } + return SQLITE_ERROR; + } + + switch( iColType ){ + case 0 : sqlite3_result_null(ctx); break; + case 1 : sqlite3_result_int64(ctx, decodeSigned(pColData, 1)); break; + case 2 : sqlite3_result_int64(ctx, decodeSigned(pColData, 2)); break; + case 3 : sqlite3_result_int64(ctx, decodeSigned(pColData, 3)); break; + case 4 : sqlite3_result_int64(ctx, decodeSigned(pColData, 4)); break; + case 5 : sqlite3_result_int64(ctx, decodeSigned(pColData, 6)); break; + case 6 : sqlite3_result_int64(ctx, decodeSigned(pColData, 8)); break; + case 7 : sqlite3_result_double(ctx, decodeFloat64(pColData)); break; + case 8 : sqlite3_result_int(ctx, 0); break; + case 9 : sqlite3_result_int(ctx, 1); break; + case 10 : assert( iColType!=10 ); break; + case 11 : assert( iColType!=11 ); break; + + default : { + u32 l = SerialTypeLength(iColType); + + /* If pColData was already allocated, arrange to pass ownership. */ + sqlite3_destructor_type pFn = SQLITE_TRANSIENT; + if( shouldFree ){ + pFn = sqlite3_free; + shouldFree = 0; + } + + if( SerialTypeIsBlob(iColType) ){ + sqlite3_result_blob(ctx, pColData, l, pFn); + }else{ + if( pCursor->iEncoding==SQLITE_UTF16LE ){ + sqlite3_result_text16le(ctx, (const void*)pColData, l, pFn); + }else if( pCursor->iEncoding==SQLITE_UTF16BE ){ + sqlite3_result_text16be(ctx, (const void*)pColData, l, pFn); + }else{ + sqlite3_result_text(ctx, (const char*)pColData, l, pFn); + } + } + } break; + } + if( shouldFree ){ + sqlite3_free(pColData); + } + return SQLITE_OK; +} + +static int recoverRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ + RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + FNENTRY(); + *pRowid = leafCursorCellRowid(pCursor->pLeafCursor); + return SQLITE_OK; +} + +static sqlite3_module recoverModule = { + 0, /* iVersion */ + recoverCreate, /* xCreate - create a table */ + recoverConnect, /* xConnect - connect to an existing table */ + recoverBestIndex, /* xBestIndex - Determine search strategy */ + recoverDisconnect, /* xDisconnect - Disconnect from a table */ + recoverDestroy, /* xDestroy - Drop a table */ + recoverOpen, /* xOpen - open a cursor */ + recoverClose, /* xClose - close a cursor */ + recoverFilter, /* xFilter - configure scan constraints */ + recoverNext, /* xNext - advance a cursor */ + recoverEof, /* xEof */ + recoverColumn, /* xColumn - read data */ + recoverRowid, /* xRowid - read data */ + 0, /* xUpdate - write data */ + 0, /* xBegin - begin transaction */ + 0, /* xSync - sync transaction */ + 0, /* xCommit - commit transaction */ + 0, /* xRollback - rollback transaction */ + 0, /* xFindFunction - function overloading */ + 0, /* xRename - rename the table */ +}; + +int recoverVtableInit(sqlite3 *db){ + return sqlite3_create_module_v2(db, "recover", &recoverModule, NULL, 0); +} + +/* This section of code is for parsing the create input and + * initializing the module. + */ + +/* Find the next word in zText and place the endpoints in pzWord*. + * Returns true if the word is non-empty. "Word" is defined as + * ASCII alphanumeric plus '_' at this time. + */ +static int findWord(const char *zText, + const char **pzWordStart, const char **pzWordEnd){ + int r; + while( ascii_isspace(*zText) ){ + zText++; + } + *pzWordStart = zText; + while( ascii_isalnum(*zText) || *zText=='_' ){ + zText++; + } + r = zText>*pzWordStart; /* In case pzWordStart==pzWordEnd */ + *pzWordEnd = zText; + return r; +} + +/* Return true if the next word in zText is zWord, also setting + * *pzContinue to the character after the word. + */ +static int expectWord(const char *zText, const char *zWord, + const char **pzContinue){ + const char *zWordStart, *zWordEnd; + if( findWord(zText, &zWordStart, &zWordEnd) && + ascii_strncasecmp(zWord, zWordStart, zWordEnd - zWordStart)==0 ){ + *pzContinue = zWordEnd; + return 1; + } + return 0; +} + +/* Parse the name and type information out of parameter. In case of + * success, *pzNameStart/End contain the name of the column, + * *pzTypeStart/End contain the top-level type, and *pTypeMask has the + * type mask to use for the column. + */ +static int findNameAndType(const char *parameter, + const char **pzNameStart, const char **pzNameEnd, + const char **pzTypeStart, const char **pzTypeEnd, + unsigned char *pTypeMask){ + unsigned nNameLen; /* Length of found name. */ + const char *zEnd; /* Current end of parsed column information. */ + int bNotNull; /* Non-zero if NULL is not allowed for name. */ + int bStrict; /* Non-zero if column requires exact type match. */ + const char *zDummy; /* Dummy parameter, result unused. */ + unsigned i; + + /* strictMask is used for STRICT, strictMask|otherMask if STRICT is + * not supplied. zReplace provides an alternate type to expose to + * the caller. + */ + static struct { + const char *zName; + unsigned char strictMask; + unsigned char otherMask; + const char *zReplace; + } kTypeInfo[] = { + { "ANY", + MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL, + 0, "", + }, + { "ROWID", MASK_INTEGER | MASK_ROWID, 0, "INTEGER", }, + { "INTEGER", MASK_INTEGER | MASK_NULL, 0, NULL, }, + { "FLOAT", MASK_FLOAT | MASK_NULL, MASK_INTEGER, NULL, }, + { "NUMERIC", MASK_INTEGER | MASK_FLOAT | MASK_NULL, MASK_TEXT, NULL, }, + { "TEXT", MASK_TEXT | MASK_NULL, MASK_BLOB, NULL, }, + { "BLOB", MASK_BLOB | MASK_NULL, 0, NULL, }, + }; + + if( !findWord(parameter, pzNameStart, pzNameEnd) ){ + return SQLITE_MISUSE; + } + + /* Manifest typing, accept any storage type. */ + if( !findWord(*pzNameEnd, pzTypeStart, pzTypeEnd) ){ + *pzTypeEnd = *pzTypeStart = ""; + *pTypeMask = MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL; + return SQLITE_OK; + } + + nNameLen = *pzTypeEnd - *pzTypeStart; + for( i=0; i<ArraySize(kTypeInfo); ++i ){ + if( ascii_strncasecmp(kTypeInfo[i].zName, *pzTypeStart, nNameLen)==0 ){ + break; + } + } + if( i==ArraySize(kTypeInfo) ){ + return SQLITE_MISUSE; + } + + zEnd = *pzTypeEnd; + bStrict = 0; + if( expectWord(zEnd, "STRICT", &zEnd) ){ + /* TODO(shess): Ick. But I don't want another single-purpose + * flag, either. + */ + if( kTypeInfo[i].zReplace && !kTypeInfo[i].zReplace[0] ){ + return SQLITE_MISUSE; + } + bStrict = 1; + } + + bNotNull = 0; + if( expectWord(zEnd, "NOT", &zEnd) ){ + if( expectWord(zEnd, "NULL", &zEnd) ){ + bNotNull = 1; + }else{ + /* Anything other than NULL after NOT is an error. */ + return SQLITE_MISUSE; + } + } + + /* Anything else is an error. */ + if( findWord(zEnd, &zDummy, &zDummy) ){ + return SQLITE_MISUSE; + } + + *pTypeMask = kTypeInfo[i].strictMask; + if( !bStrict ){ + *pTypeMask |= kTypeInfo[i].otherMask; + } + if( bNotNull ){ + *pTypeMask &= ~MASK_NULL; + } + if( kTypeInfo[i].zReplace ){ + *pzTypeStart = kTypeInfo[i].zReplace; + *pzTypeEnd = *pzTypeStart + strlen(*pzTypeStart); + } + return SQLITE_OK; +} + +/* Parse the arguments, placing type masks in *pTypes and the exposed + * schema in *pzCreateSql (for sqlite3_declare_vtab). + */ +static int ParseColumnsAndGenerateCreate(unsigned nCols, + const char *const *pCols, + char **pzCreateSql, + unsigned char *pTypes, + char **pzErr){ + unsigned i; + char *zCreateSql = sqlite3_mprintf("CREATE TABLE x("); + if( !zCreateSql ){ + return SQLITE_NOMEM; + } + + for( i=0; i<nCols; i++ ){ + const char *zSep = (i < nCols - 1 ? ", " : ")"); + const char *zNotNull = ""; + const char *zNameStart, *zNameEnd; + const char *zTypeStart, *zTypeEnd; + int rc = findNameAndType(pCols[i], + &zNameStart, &zNameEnd, + &zTypeStart, &zTypeEnd, + &pTypes[i]); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("unable to parse column %d", i); + sqlite3_free(zCreateSql); + return rc; + } + + if( !(pTypes[i]&MASK_NULL) ){ + zNotNull = " NOT NULL"; + } + + /* Add name and type to the create statement. */ + zCreateSql = sqlite3_mprintf("%z%.*s %.*s%s%s", + zCreateSql, + zNameEnd - zNameStart, zNameStart, + zTypeEnd - zTypeStart, zTypeStart, + zNotNull, zSep); + if( !zCreateSql ){ + return SQLITE_NOMEM; + } + } + + *pzCreateSql = zCreateSql; + return SQLITE_OK; +} + +/* Helper function for initializing the module. */ +/* argv[0] module name + * argv[1] db name for virtual table + * argv[2] virtual table name + * argv[3] backing table name + * argv[4] columns + */ +/* TODO(shess): Since connect isn't supported, could inline into + * recoverCreate(). + */ +/* TODO(shess): Explore cases where it would make sense to set *pzErr. */ +static int recoverInit( + sqlite3 *db, /* Database connection */ + void *pAux, /* unused */ + int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */ + sqlite3_vtab **ppVtab, /* OUT: New virtual table */ + char **pzErr /* OUT: Error message, if any */ +){ + const unsigned kTypeCol = 4; /* First argument with column type info. */ + Recover *pRecover; /* Virtual table structure being created. */ + char *zDot; /* Any dot found in "db.table" backing. */ + u32 iRootPage; /* Root page of backing table. */ + char *zCreateSql; /* Schema of created virtual table. */ + int rc; + + /* Require to be in the temp database. */ + if( ascii_strcasecmp(argv[1], "temp")!=0 ){ + *pzErr = sqlite3_mprintf("recover table must be in temp database"); + return SQLITE_MISUSE; + } + + /* Need the backing table and at least one column. */ + if( argc<=kTypeCol ){ + *pzErr = sqlite3_mprintf("no columns specified"); + return SQLITE_MISUSE; + } + + pRecover = sqlite3_malloc(sizeof(Recover)); + if( !pRecover ){ + return SQLITE_NOMEM; + } + memset(pRecover, 0, sizeof(*pRecover)); + pRecover->base.pModule = &recoverModule; + pRecover->db = db; + + /* Parse out db.table, assuming main if no dot. */ + zDot = strchr(argv[3], '.'); + if( !zDot ){ + pRecover->zDb = sqlite3_strdup(db->aDb[0].zName); + pRecover->zTable = sqlite3_strdup(argv[3]); + }else if( zDot>argv[3] && zDot[1]!='\0' ){ + pRecover->zDb = sqlite3_strndup(argv[3], zDot - argv[3]); + pRecover->zTable = sqlite3_strdup(zDot + 1); + }else{ + /* ".table" or "db." not allowed. */ + *pzErr = sqlite3_mprintf("ill-formed table specifier"); + recoverRelease(pRecover); + return SQLITE_ERROR; + } + + pRecover->nCols = argc - kTypeCol; + pRecover->pTypes = sqlite3_malloc(pRecover->nCols); + if( !pRecover->zDb || !pRecover->zTable || !pRecover->pTypes ){ + recoverRelease(pRecover); + return SQLITE_NOMEM; + } + + /* Require the backing table to exist. */ + /* TODO(shess): Be more pedantic about the form of the descriptor + * string. This already fails for poorly-formed strings, simply + * because there won't be a root page, but it would make more sense + * to be explicit. + */ + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, &iRootPage); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("unable to find backing table"); + recoverRelease(pRecover); + return rc; + } + + /* Parse the column definitions. */ + rc = ParseColumnsAndGenerateCreate(pRecover->nCols, argv + kTypeCol, + &zCreateSql, pRecover->pTypes, pzErr); + if( rc!=SQLITE_OK ){ + recoverRelease(pRecover); + return rc; + } + + rc = sqlite3_declare_vtab(db, zCreateSql); + sqlite3_free(zCreateSql); + if( rc!=SQLITE_OK ){ + recoverRelease(pRecover); + return rc; + } + + *ppVtab = (sqlite3_vtab *)pRecover; + return SQLITE_OK; +} + +/************** End of recover.c *********************************************/ /************** Begin file fts3.c ********************************************/ /* ** 2006 Oct 10 diff --git a/third_party/sqlite/amalgamation/sqlite3.h b/third_party/sqlite/amalgamation/sqlite3.h index 0d42790..5d56618 100644 --- a/third_party/sqlite/amalgamation/sqlite3.h +++ b/third_party/sqlite/amalgamation/sqlite3.h @@ -6408,6 +6408,17 @@ SQLITE_API int sqlite3_wal_checkpoint_v2( #define SQLITE_CHECKPOINT_RESTART 2 +/* Begin recover.patch for Chromium */ +/* +** Call to initialize the recover virtual-table modules (see recover.c). +** +** This could be loaded by default in main.c, but that would make the +** virtual table available to Web SQL. Breaking it out allows only +** selected users to enable it (currently sql/recovery.cc). +*/ +int recoverVtableInit(sqlite3 *db); +/* End recover.patch for Chromium */ + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/third_party/sqlite/recover.patch b/third_party/sqlite/recover.patch new file mode 100644 index 0000000..b601a44 --- /dev/null +++ b/third_party/sqlite/recover.patch @@ -0,0 +1,2194 @@ +Add new virtual table 'recover' to src/ and the amalgamation. + +Since recover.c is in somewhat active development, it is possible that +the patch below will not reliably re-create the file. + +shess@chromium.org + +Generated with: +git diff --cached --relative=third_party/sqlite/src --src-prefix='' --dst-prefix='' > third_party/sqlite/recover.patch +[--cached because otherwise the diff adding recover.c wasn't generated.] + +diff --git Makefile.in Makefile.in +index f3239f3..216742c 100644 +--- Makefile.in ++++ Makefile.in +@@ -251,6 +251,7 @@ SRC = \ + $(TOP)/src/prepare.c \ + $(TOP)/src/printf.c \ + $(TOP)/src/random.c \ ++ $(TOP)/src/recover.c \ + $(TOP)/src/resolve.c \ + $(TOP)/src/rowset.c \ + $(TOP)/src/select.c \ +diff --git src/sqlite.h.in src/sqlite.h.in +index 62b9326..fb76659 100644 +--- src/sqlite.h.in ++++ src/sqlite.h.in +@@ -6403,6 +6403,17 @@ int sqlite3_wal_checkpoint_v2( + #define SQLITE_CHECKPOINT_RESTART 2 + + ++/* Begin recover.patch for Chromium */ ++/* ++** Call to initialize the recover virtual-table modules (see recover.c). ++** ++** This could be loaded by default in main.c, but that would make the ++** virtual table available to Web SQL. Breaking it out allows only ++** selected users to enable it (currently sql/recovery.cc). ++*/ ++int recoverVtableInit(sqlite3 *db); ++/* End recover.patch for Chromium */ ++ + /* + ** Undo the hack that converts floating point types to integer for + ** builds on processors without floating point support. +diff --git tool/mksqlite3c.tcl tool/mksqlite3c.tcl +index fa99f2d..df2df07 100644 +--- tool/mksqlite3c.tcl ++++ tool/mksqlite3c.tcl +@@ -293,6 +293,8 @@ foreach file { + main.c + notify.c + ++ recover.c ++ + fts3.c + fts3_aux.c + fts3_expr.c +diff --git src/recover.c src/recover.c +new file mode 100644 +index 0000000..6430c8b +--- /dev/null ++++ src/recover.c +@@ -0,0 +1,2130 @@ ++/* ++** 2012 Jan 11 ++** ++** The author disclaims copyright to this source code. In place of ++** a legal notice, here is a blessing: ++** ++** May you do good and not evil. ++** May you find forgiveness for yourself and forgive others. ++** May you share freely, never taking more than you give. ++*/ ++/* TODO(shess): THIS MODULE IS STILL EXPERIMENTAL. DO NOT USE IT. */ ++/* Implements a virtual table "recover" which can be used to recover ++ * data from a corrupt table. The table is walked manually, with ++ * corrupt items skipped. Additionally, any errors while reading will ++ * be skipped. ++ * ++ * Given a table with this definition: ++ * ++ * CREATE TABLE Stuff ( ++ * name TEXT PRIMARY KEY, ++ * value TEXT NOT NULL ++ * ); ++ * ++ * to recover the data from teh table, you could do something like: ++ * ++ * -- Attach another database, the original is not trustworthy. ++ * ATTACH DATABASE '/tmp/db.db' AS rdb; ++ * -- Create a new version of the table. ++ * CREATE TABLE rdb.Stuff ( ++ * name TEXT PRIMARY KEY, ++ * value TEXT NOT NULL ++ * ); ++ * -- This will read the original table's data. ++ * CREATE VIRTUAL TABLE temp.recover_Stuff using recover( ++ * main.Stuff, ++ * name TEXT STRICT NOT NULL, -- only real TEXT data allowed ++ * value TEXT STRICT NOT NULL ++ * ); ++ * -- Corruption means the UNIQUE constraint may no longer hold for ++ * -- Stuff, so either OR REPLACE or OR IGNORE must be used. ++ * INSERT OR REPLACE INTO rdb.Stuff (rowid, name, value ) ++ * SELECT rowid, name, value FROM temp.recover_Stuff; ++ * DROP TABLE temp.recover_Stuff; ++ * DETACH DATABASE rdb; ++ * -- Move db.db to replace original db in filesystem. ++ * ++ * ++ * Usage ++ * ++ * Given the goal of dealing with corruption, it would not be safe to ++ * create a recovery table in the database being recovered. So ++ * recovery tables must be created in the temp database. They are not ++ * appropriate to persist, in any case. [As a bonus, sqlite_master ++ * tables can be recovered. Perhaps more cute than useful, though.] ++ * ++ * The parameters are a specifier for the table to read, and a column ++ * definition for each bit of data stored in that table. The named ++ * table must be convertable to a root page number by reading the ++ * sqlite_master table. Bare table names are assumed to be in ++ * database 0 ("main"), other databases can be specified in db.table ++ * fashion. ++ * ++ * Column definitions are similar to BUT NOT THE SAME AS those ++ * provided to CREATE statements: ++ * column-def: column-name [type-name [STRICT] [NOT NULL]] ++ * type-name: (ANY|ROWID|INTEGER|FLOAT|NUMERIC|TEXT|BLOB) ++ * ++ * Only those exact type names are accepted, there is no type ++ * intuition. The only constraints accepted are STRICT (see below) ++ * and NOT NULL. Anything unexpected will cause the create to fail. ++ * ++ * ANY is a convenience to indicate that manifest typing is desired. ++ * It is equivalent to not specifying a type at all. The results for ++ * such columns will have the type of the data's storage. The exposed ++ * schema will contain no type for that column. ++ * ++ * ROWID is used for columns representing aliases to the rowid ++ * (INTEGER PRIMARY KEY, with or without AUTOINCREMENT), to make the ++ * concept explicit. Such columns are actually stored as NULL, so ++ * they cannot be simply ignored. The exposed schema will be INTEGER ++ * for that column. ++ * ++ * NOT NULL causes rows with a NULL in that column to be skipped. It ++ * also adds NOT NULL to the column in the exposed schema. If the ++ * table has ever had columns added using ALTER TABLE, then those ++ * columns implicitly contain NULL for rows which have not been ++ * updated. [Workaround using COALESCE() in your SELECT statement.] ++ * ++ * The created table is read-only, with no indices. Any SELECT will ++ * be a full-table scan, returning each valid row read from the ++ * storage of the backing table. The rowid will be the rowid of the ++ * row from the backing table. "Valid" means: ++ * - The cell metadata for the row is well-formed. Mainly this means that ++ * the cell header info describes a payload of the size indicated by ++ * the cell's payload size. ++ * - The cell does not run off the page. ++ * - The cell does not overlap any other cell on the page. ++ * - The cell contains doesn't contain too many columns. ++ * - The types of the serialized data match the indicated types (see below). ++ * ++ * ++ * Type affinity versus type storage. ++ * ++ * http://www.sqlite.org/datatype3.html describes SQLite's type ++ * affinity system. The system provides for automated coercion of ++ * types in certain cases, transparently enough that many developers ++ * do not realize that it is happening. Importantly, it implies that ++ * the raw data stored in the database may not have the obvious type. ++ * ++ * Differences between the stored data types and the expected data ++ * types may be a signal of corruption. This module makes some ++ * allowances for automatic coercion. It is important to be concious ++ * of the difference between the schema exposed by the module, and the ++ * data types read from storage. The following table describes how ++ * the module interprets things: ++ * ++ * type schema data STRICT ++ * ---- ------ ---- ------ ++ * ANY <none> any any ++ * ROWID INTEGER n/a n/a ++ * INTEGER INTEGER integer integer ++ * FLOAT FLOAT integer or float float ++ * NUMERIC NUMERIC integer, float, or text integer or float ++ * TEXT TEXT text or blob text ++ * BLOB BLOB blob blob ++ * ++ * type is the type provided to the recover module, schema is the ++ * schema exposed by the module, data is the acceptable types of data ++ * decoded from storage, and STRICT is a modification of that. ++ * ++ * A very loose recovery system might use ANY for all columns, then ++ * use the appropriate sqlite3_column_*() calls to coerce to expected ++ * types. This doesn't provide much protection if a page from a ++ * different table with the same column count is linked into an ++ * inappropriate btree. ++ * ++ * A very tight recovery system might use STRICT to enforce typing on ++ * all columns, preferring to skip rows which are valid at the storage ++ * level but don't contain the right types. Note that FLOAT STRICT is ++ * almost certainly not appropriate, since integral values are ++ * transparently stored as integers, when that is more efficient. ++ * ++ * Another option is to use ANY for all columns and inspect each ++ * result manually (using sqlite3_column_*). This should only be ++ * necessary in cases where developers have used manifest typing (test ++ * to make sure before you decide that you aren't using manifest ++ * typing!). ++ * ++ * ++ * Caveats ++ * ++ * Leaf pages not referenced by interior nodes will not be found. ++ * ++ * Leaf pages referenced from interior nodes of other tables will not ++ * be resolved. ++ * ++ * Rows referencing invalid overflow pages will be skipped. ++ * ++ * SQlite rows have a header which describes how to interpret the rest ++ * of the payload. The header can be valid in cases where the rest of ++ * the record is actually corrupt (in the sense that the data is not ++ * the intended data). This can especially happen WRT overflow pages, ++ * as lack of atomic updates between pages is the primary form of ++ * corruption I have seen in the wild. ++ */ ++/* The implementation is via a series of cursors. The cursor ++ * implementations follow the pattern: ++ * ++ * // Creates the cursor using various initialization info. ++ * int cursorCreate(...); ++ * ++ * // Returns 1 if there is no more data, 0 otherwise. ++ * int cursorEOF(Cursor *pCursor); ++ * ++ * // Various accessors can be used if not at EOF. ++ * ++ * // Move to the next item. ++ * int cursorNext(Cursor *pCursor); ++ * ++ * // Destroy the memory associated with the cursor. ++ * void cursorDestroy(Cursor *pCursor); ++ * ++ * References in the following are to sections at ++ * http://www.sqlite.org/fileformat2.html . ++ * ++ * RecoverLeafCursor iterates the records in a leaf table node ++ * described in section 1.5 "B-tree Pages". When the node is ++ * exhausted, an interior cursor is used to get the next leaf node, ++ * and iteration continues there. ++ * ++ * RecoverInteriorCursor iterates the child pages in an interior table ++ * node described in section 1.5 "B-tree Pages". When the node is ++ * exhausted, a parent interior cursor is used to get the next ++ * interior node at the same level, and iteration continues there. ++ * ++ * Together these record the path from the leaf level to the root of ++ * the tree. Iteration happens from the leaves rather than the root ++ * both for efficiency and putting the special case at the front of ++ * the list is easier to implement. ++ * ++ * RecoverCursor uses a RecoverLeafCursor to iterate the rows of a ++ * table, returning results via the SQLite virtual table interface. ++ */ ++/* TODO(shess): It might be useful to allow DEFAULT in types to ++ * specify what to do for NULL when an ALTER TABLE case comes up. ++ * Unfortunately, simply adding it to the exposed schema and using ++ * sqlite3_result_null() does not cause the default to be generate. ++ * Handling it ourselves seems hard, unfortunately. ++ */ ++ ++#include <assert.h> ++#include <ctype.h> ++#include <stdio.h> ++#include <string.h> ++ ++/* Internal SQLite things that are used: ++ * u32, u64, i64 types. ++ * Btree, Pager, and DbPage structs. ++ * DbPage.pData, .pPager, and .pgno ++ * sqlite3 struct. ++ * sqlite3BtreePager() and sqlite3BtreeGetPageSize() ++ * sqlite3PagerAcquire() and sqlite3PagerUnref() ++ * getVarint(). ++ */ ++#include "sqliteInt.h" ++ ++/* For debugging. */ ++#if 0 ++#define FNENTRY() fprintf(stderr, "In %s\n", __FUNCTION__) ++#else ++#define FNENTRY() ++#endif ++ ++/* Generic constants and helper functions. */ ++ ++static const unsigned char kTableLeafPage = 0x0D; ++static const unsigned char kTableInteriorPage = 0x05; ++ ++/* From section 1.5. */ ++static const unsigned kiPageTypeOffset = 0; ++static const unsigned kiPageFreeBlockOffset = 1; ++static const unsigned kiPageCellCountOffset = 3; ++static const unsigned kiPageCellContentOffset = 5; ++static const unsigned kiPageFragmentedBytesOffset = 7; ++static const unsigned knPageLeafHeaderBytes = 8; ++/* Interior pages contain an additional field. */ ++static const unsigned kiPageRightChildOffset = 8; ++static const unsigned kiPageInteriorHeaderBytes = 12; ++ ++/* Accepted types are specified by a mask. */ ++#define MASK_ROWID (1<<0) ++#define MASK_INTEGER (1<<1) ++#define MASK_FLOAT (1<<2) ++#define MASK_TEXT (1<<3) ++#define MASK_BLOB (1<<4) ++#define MASK_NULL (1<<5) ++ ++/* Helpers to decode fixed-size fields. */ ++static u32 decodeUnsigned16(const unsigned char *pData){ ++ return (pData[0]<<8) + pData[1]; ++} ++static u32 decodeUnsigned32(const unsigned char *pData){ ++ return (decodeUnsigned16(pData)<<16) + decodeUnsigned16(pData+2); ++} ++static i64 decodeSigned(const unsigned char *pData, unsigned nBytes){ ++ i64 r = (char)(*pData); ++ while( --nBytes ){ ++ r <<= 8; ++ r += *(++pData); ++ } ++ return r; ++} ++/* Derived from vdbeaux.c, sqlite3VdbeSerialGet(), case 7. */ ++/* TODO(shess): Determine if swapMixedEndianFloat() applies. */ ++static double decodeFloat64(const unsigned char *pData){ ++#if !defined(NDEBUG) ++ static const u64 t1 = ((u64)0x3ff00000)<<32; ++ static const double r1 = 1.0; ++ u64 t2 = t1; ++ assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 ); ++#endif ++ i64 x = decodeSigned(pData, 8); ++ double d; ++ memcpy(&d, &x, sizeof(x)); ++ return d; ++} ++ ++/* Return true if a varint can safely be read from pData/nData. */ ++/* TODO(shess): DbPage points into the middle of a buffer which ++ * contains the page data before DbPage. So code should always be ++ * able to read a small number of varints safely. Consider whether to ++ * trust that or not. ++ */ ++static int checkVarint(const unsigned char *pData, unsigned nData){ ++ unsigned i; ++ ++ /* In the worst case the decoder takes all 8 bits of the 9th byte. */ ++ if( nData>=9 ){ ++ return 1; ++ } ++ ++ /* Look for a high-bit-clear byte in what's left. */ ++ for( i=0; i<nData; ++i ){ ++ if( !(pData[i]&0x80) ){ ++ return 1; ++ } ++ } ++ ++ /* Cannot decode in the space given. */ ++ return 0; ++} ++ ++/* Return 1 if n varints can be read from pData/nData. */ ++static int checkVarints(const unsigned char *pData, unsigned nData, ++ unsigned n){ ++ unsigned nCur = 0; /* Byte offset within current varint. */ ++ unsigned nFound = 0; /* Number of varints found. */ ++ unsigned i; ++ ++ /* In the worst case the decoder takes all 8 bits of the 9th byte. */ ++ if( nData>=9*n ){ ++ return 1; ++ } ++ ++ for( i=0; nFound<n && i<nData; ++i ){ ++ nCur++; ++ if( nCur==9 || !(pData[i]&0x80) ){ ++ nFound++; ++ nCur = 0; ++ } ++ } ++ ++ return nFound==n; ++} ++ ++/* ctype and str[n]casecmp() can be affected by locale (eg, tr_TR). ++ * These versions consider only the ASCII space. ++ */ ++/* TODO(shess): It may be reasonable to just remove the need for these ++ * entirely. The module could require "TEXT STRICT NOT NULL", not ++ * "Text Strict Not Null" or whatever the developer felt like typing ++ * that day. Handling corrupt data is a PERFECT place to be pedantic. ++ */ ++static int ascii_isspace(char c){ ++ /* From fts3_expr.c */ ++ return c==' ' || c=='\t' || c=='\n' || c=='\r' || c=='\v' || c=='\f'; ++} ++static int ascii_isalnum(int x){ ++ /* From fts3_tokenizer1.c */ ++ return (x>='0' && x<='9') || (x>='A' && x<='Z') || (x>='a' && x<='z'); ++} ++static int ascii_tolower(int x){ ++ /* From fts3_tokenizer1.c */ ++ return (x>='A' && x<='Z') ? x-'A'+'a' : x; ++} ++/* TODO(shess): Consider sqlite3_strnicmp() */ ++static int ascii_strncasecmp(const char *s1, const char *s2, size_t n){ ++ const unsigned char *us1 = (const unsigned char *)s1; ++ const unsigned char *us2 = (const unsigned char *)s2; ++ while( *us1 && *us2 && n && ascii_tolower(*us1)==ascii_tolower(*us2) ){ ++ us1++, us2++, n--; ++ } ++ return n ? ascii_tolower(*us1)-ascii_tolower(*us2) : 0; ++} ++static int ascii_strcasecmp(const char *s1, const char *s2){ ++ /* If s2 is equal through strlen(s1), will exit while() due to s1's ++ * trailing NUL, and return NUL-s2[strlen(s1)]. ++ */ ++ return ascii_strncasecmp(s1, s2, strlen(s1)+1); ++} ++ ++/* For some reason I kept making mistakes with offset calculations. */ ++static const unsigned char *PageData(DbPage *pPage, unsigned iOffset){ ++ assert( iOffset<=pPage->nPageSize ); ++ return (unsigned char *)pPage->pData + iOffset; ++} ++ ++/* The first page in the file contains a file header in the first 100 ++ * bytes. The page's header information comes after that. Note that ++ * the offsets in the page's header information are relative to the ++ * beginning of the page, NOT the end of the page header. ++ */ ++static const unsigned char *PageHeader(DbPage *pPage){ ++ if( pPage->pgno==1 ){ ++ const unsigned nDatabaseHeader = 100; ++ return PageData(pPage, nDatabaseHeader); ++ }else{ ++ return PageData(pPage, 0); ++ } ++} ++ ++/* Helper to fetch the pager and page size for the named database. */ ++static int GetPager(sqlite3 *db, const char *zName, ++ Pager **pPager, unsigned *pnPageSize){ ++ Btree *pBt = NULL; ++ int i; ++ for( i=0; i<db->nDb; ++i ){ ++ if( ascii_strcasecmp(db->aDb[i].zName, zName)==0 ){ ++ pBt = db->aDb[i].pBt; ++ break; ++ } ++ } ++ if( !pBt ){ ++ return SQLITE_ERROR; ++ } ++ ++ *pPager = sqlite3BtreePager(pBt); ++ *pnPageSize = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt); ++ return SQLITE_OK; ++} ++ ++/* iSerialType is a type read from a record header. See "2.1 Record Format". ++ */ ++ ++/* Storage size of iSerialType in bytes. My interpretation of SQLite ++ * documentation is that text and blob fields can have 32-bit length. ++ * Values past 2^31-12 will need more than 32 bits to encode, which is ++ * why iSerialType is u64. ++ */ ++static u32 SerialTypeLength(u64 iSerialType){ ++ switch( iSerialType ){ ++ case 0 : return 0; /* NULL */ ++ case 1 : return 1; /* Various integers. */ ++ case 2 : return 2; ++ case 3 : return 3; ++ case 4 : return 4; ++ case 5 : return 6; ++ case 6 : return 8; ++ case 7 : return 8; /* 64-bit float. */ ++ case 8 : return 0; /* Constant 0. */ ++ case 9 : return 0; /* Constant 1. */ ++ case 10 : case 11 : assert( !"RESERVED TYPE"); return 0; ++ } ++ return (u32)((iSerialType>>1) - 6); ++} ++ ++/* True if iSerialType refers to a blob. */ ++static int SerialTypeIsBlob(u64 iSerialType){ ++ assert( iSerialType>=12 ); ++ return (iSerialType%2)==0; ++} ++ ++/* Returns true if the serialized type represented by iSerialType is ++ * compatible with the given type mask. ++ */ ++static int SerialTypeIsCompatible(u64 iSerialType, unsigned char mask){ ++ switch( iSerialType ){ ++ case 0 : return (mask&MASK_NULL)!=0; ++ case 1 : return (mask&MASK_INTEGER)!=0; ++ case 2 : return (mask&MASK_INTEGER)!=0; ++ case 3 : return (mask&MASK_INTEGER)!=0; ++ case 4 : return (mask&MASK_INTEGER)!=0; ++ case 5 : return (mask&MASK_INTEGER)!=0; ++ case 6 : return (mask&MASK_INTEGER)!=0; ++ case 7 : return (mask&MASK_FLOAT)!=0; ++ case 8 : return (mask&MASK_INTEGER)!=0; ++ case 9 : return (mask&MASK_INTEGER)!=0; ++ case 10 : assert( !"RESERVED TYPE"); return 0; ++ case 11 : assert( !"RESERVED TYPE"); return 0; ++ } ++ return (mask&(SerialTypeIsBlob(iSerialType) ? MASK_BLOB : MASK_TEXT)); ++} ++ ++/* Versions of strdup() with return values appropriate for ++ * sqlite3_free(). malloc.c has sqlite3DbStrDup()/NDup(), but those ++ * need sqlite3DbFree(), which seems intrusive. ++ */ ++static char *sqlite3_strndup(const char *z, unsigned n){ ++ char *zNew; ++ ++ if( z==NULL ){ ++ return NULL; ++ } ++ ++ zNew = sqlite3_malloc(n+1); ++ if( zNew!=NULL ){ ++ memcpy(zNew, z, n); ++ zNew[n] = '\0'; ++ } ++ return zNew; ++} ++static char *sqlite3_strdup(const char *z){ ++ if( z==NULL ){ ++ return NULL; ++ } ++ return sqlite3_strndup(z, strlen(z)); ++} ++ ++/* Fetch the page number of zTable in zDb from sqlite_master in zDb, ++ * and put it in *piRootPage. ++ */ ++static int getRootPage(sqlite3 *db, const char *zDb, const char *zTable, ++ u32 *piRootPage){ ++ char *zSql; /* SQL selecting root page of named element. */ ++ sqlite3_stmt *pStmt; ++ int rc; ++ ++ if( strcmp(zTable, "sqlite_master")==0 ){ ++ *piRootPage = 1; ++ return SQLITE_OK; ++ } ++ ++ zSql = sqlite3_mprintf("SELECT rootpage FROM %s.sqlite_master " ++ "WHERE type = 'table' AND tbl_name = %Q", ++ zDb, zTable); ++ if( !zSql ){ ++ return SQLITE_NOMEM; ++ } ++ ++ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); ++ sqlite3_free(zSql); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ /* Require a result. */ ++ rc = sqlite3_step(pStmt); ++ if( rc==SQLITE_DONE ){ ++ rc = SQLITE_CORRUPT; ++ }else if( rc==SQLITE_ROW ){ ++ *piRootPage = sqlite3_column_int(pStmt, 0); ++ ++ /* Require only one result. */ ++ rc = sqlite3_step(pStmt); ++ if( rc==SQLITE_DONE ){ ++ rc = SQLITE_OK; ++ }else if( rc==SQLITE_ROW ){ ++ rc = SQLITE_CORRUPT; ++ } ++ } ++ sqlite3_finalize(pStmt); ++ return rc; ++} ++ ++static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){ ++ sqlite3_stmt *pStmt; ++ int rc; ++ char *zSql = sqlite3_mprintf("PRAGMA %s.encoding", zDb); ++ if( !zSql ){ ++ return SQLITE_NOMEM; ++ } ++ ++ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); ++ sqlite3_free(zSql); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ /* Require a result. */ ++ rc = sqlite3_step(pStmt); ++ if( rc==SQLITE_DONE ){ ++ /* This case should not be possible. */ ++ rc = SQLITE_CORRUPT; ++ }else if( rc==SQLITE_ROW ){ ++ if( sqlite3_column_type(pStmt, 0)==SQLITE_TEXT ){ ++ const char* z = (const char *)sqlite3_column_text(pStmt, 0); ++ /* These strings match the literals in pragma.c. */ ++ if( !strcmp(z, "UTF-16le") ){ ++ *piEncoding = SQLITE_UTF16LE; ++ }else if( !strcmp(z, "UTF-16be") ){ ++ *piEncoding = SQLITE_UTF16BE; ++ }else if( !strcmp(z, "UTF-8") ){ ++ *piEncoding = SQLITE_UTF8; ++ }else{ ++ /* This case should not be possible. */ ++ *piEncoding = SQLITE_UTF8; ++ } ++ }else{ ++ /* This case should not be possible. */ ++ *piEncoding = SQLITE_UTF8; ++ } ++ ++ /* Require only one result. */ ++ rc = sqlite3_step(pStmt); ++ if( rc==SQLITE_DONE ){ ++ rc = SQLITE_OK; ++ }else if( rc==SQLITE_ROW ){ ++ /* This case should not be possible. */ ++ rc = SQLITE_CORRUPT; ++ } ++ } ++ sqlite3_finalize(pStmt); ++ return rc; ++} ++ ++/* Cursor for iterating interior nodes. Interior page cells contain a ++ * child page number and a rowid. The child page contains items left ++ * of the rowid (less than). The rightmost page of the subtree is ++ * stored in the page header. ++ * ++ * interiorCursorDestroy - release all resources associated with the ++ * cursor and any parent cursors. ++ * interiorCursorCreate - create a cursor with the given parent and page. ++ * interiorCursorEOF - returns true if neither the cursor nor the ++ * parent cursors can return any more data. ++ * interiorCursorNextPage - fetch the next child page from the cursor. ++ * ++ * Logically, interiorCursorNextPage() returns the next child page ++ * number from the page the cursor is currently reading, calling the ++ * parent cursor as necessary to get new pages to read, until done. ++ * SQLITE_ROW if a page is returned, SQLITE_DONE if out of pages, ++ * error otherwise. Unfortunately, if the table is corrupted ++ * unexpected pages can be returned. If any unexpected page is found, ++ * leaf or otherwise, it is returned to the caller for processing, ++ * with the interior cursor left empty. The next call to ++ * interiorCursorNextPage() will recurse to the parent cursor until an ++ * interior page to iterate is returned. ++ * ++ * Note that while interiorCursorNextPage() will refuse to follow ++ * loops, it does not keep track of pages returned for purposes of ++ * preventing duplication. ++ * ++ * Note that interiorCursorEOF() could return false (not at EOF), and ++ * interiorCursorNextPage() could still return SQLITE_DONE. This ++ * could happen if there are more cells to iterate in an interior ++ * page, but those cells refer to invalid pages. ++ */ ++typedef struct RecoverInteriorCursor RecoverInteriorCursor; ++struct RecoverInteriorCursor { ++ RecoverInteriorCursor *pParent; /* Parent node to this node. */ ++ DbPage *pPage; /* Reference to leaf page. */ ++ unsigned nPageSize; /* Size of page. */ ++ unsigned nChildren; /* Number of children on the page. */ ++ unsigned iChild; /* Index of next child to return. */ ++}; ++ ++static void interiorCursorDestroy(RecoverInteriorCursor *pCursor){ ++ /* Destroy all the cursors to the root. */ ++ while( pCursor ){ ++ RecoverInteriorCursor *p = pCursor; ++ pCursor = pCursor->pParent; ++ ++ if( p->pPage ){ ++ sqlite3PagerUnref(p->pPage); ++ p->pPage = NULL; ++ } ++ ++ memset(p, 0xA5, sizeof(*p)); ++ sqlite3_free(p); ++ } ++} ++ ++/* Internal helper. Reset storage in preparation for iterating pPage. */ ++static void interiorCursorSetPage(RecoverInteriorCursor *pCursor, ++ DbPage *pPage){ ++ assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage ); ++ ++ if( pCursor->pPage ){ ++ sqlite3PagerUnref(pCursor->pPage); ++ pCursor->pPage = NULL; ++ } ++ pCursor->pPage = pPage; ++ pCursor->iChild = 0; ++ ++ /* A child for each cell, plus one in the header. */ ++ /* TODO(shess): Sanity-check the count? Page header plus per-cell ++ * cost of 16-bit offset, 32-bit page number, and one varint ++ * (minimum 1 byte). ++ */ ++ pCursor->nChildren = decodeUnsigned16(PageHeader(pPage) + ++ kiPageCellCountOffset) + 1; ++} ++ ++static int interiorCursorCreate(RecoverInteriorCursor *pParent, ++ DbPage *pPage, int nPageSize, ++ RecoverInteriorCursor **ppCursor){ ++ RecoverInteriorCursor *pCursor = ++ sqlite3_malloc(sizeof(RecoverInteriorCursor)); ++ if( !pCursor ){ ++ return SQLITE_NOMEM; ++ } ++ ++ memset(pCursor, 0, sizeof(*pCursor)); ++ pCursor->pParent = pParent; ++ pCursor->nPageSize = nPageSize; ++ interiorCursorSetPage(pCursor, pPage); ++ *ppCursor = pCursor; ++ return SQLITE_OK; ++} ++ ++/* Internal helper. Return the child page number at iChild. */ ++static unsigned interiorCursorChildPage(RecoverInteriorCursor *pCursor){ ++ const unsigned char *pPageHeader; /* Header of the current page. */ ++ const unsigned char *pCellOffsets; /* Offset to page's cell offsets. */ ++ unsigned iCellOffset; /* Offset of target cell. */ ++ ++ assert( pCursor->iChild<pCursor->nChildren ); ++ ++ /* Rightmost child is in the header. */ ++ pPageHeader = PageHeader(pCursor->pPage); ++ if( pCursor->iChild==pCursor->nChildren-1 ){ ++ return decodeUnsigned32(pPageHeader + kiPageRightChildOffset); ++ } ++ ++ /* Each cell is a 4-byte integer page number and a varint rowid ++ * which is greater than the rowid of items in that sub-tree (this ++ * module ignores ordering). The offset is from the beginning of the ++ * page, not from the page header. ++ */ ++ pCellOffsets = pPageHeader + kiPageInteriorHeaderBytes; ++ iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iChild*2); ++ if( iCellOffset<=pCursor->nPageSize-4 ){ ++ return decodeUnsigned32(PageData(pCursor->pPage, iCellOffset)); ++ } ++ ++ /* TODO(shess): Check for cell overlaps? Cells require 4 bytes plus ++ * a varint. Check could be identical to leaf check (or even a ++ * shared helper testing for "Cells starting in this range"?). ++ */ ++ ++ /* If the offset is broken, return an invalid page number. */ ++ return 0; ++} ++ ++static int interiorCursorEOF(RecoverInteriorCursor *pCursor){ ++ /* Find a parent with remaining children. EOF if none found. */ ++ while( pCursor && pCursor->iChild>=pCursor->nChildren ){ ++ pCursor = pCursor->pParent; ++ } ++ return pCursor==NULL; ++} ++ ++/* Internal helper. Used to detect if iPage would cause a loop. */ ++static int interiorCursorPageInUse(RecoverInteriorCursor *pCursor, ++ unsigned iPage){ ++ /* Find any parent using the indicated page. */ ++ while( pCursor && pCursor->pPage->pgno!=iPage ){ ++ pCursor = pCursor->pParent; ++ } ++ return pCursor!=NULL; ++} ++ ++/* Get the next page from the interior cursor at *ppCursor. Returns ++ * SQLITE_ROW with the page in *ppPage, or SQLITE_DONE if out of ++ * pages, or the error SQLite returned. ++ * ++ * If the tree is uneven, then when the cursor attempts to get a new ++ * interior page from the parent cursor, it may get a non-interior ++ * page. In that case, the new page is returned, and *ppCursor is ++ * updated to point to the parent cursor (this cursor is freed). ++ */ ++/* TODO(shess): I've tried to avoid recursion in most of this code, ++ * but this case is more challenging because the recursive call is in ++ * the middle of operation. One option for converting it without ++ * adding memory management would be to retain the head pointer and ++ * use a helper to "back up" as needed. Another option would be to ++ * reverse the list during traversal. ++ */ ++static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, ++ DbPage **ppPage){ ++ RecoverInteriorCursor *pCursor = *ppCursor; ++ while( 1 ){ ++ int rc; ++ const unsigned char *pPageHeader; /* Header of found page. */ ++ ++ /* Find a valid child page which isn't on the stack. */ ++ while( pCursor->iChild<pCursor->nChildren ){ ++ const unsigned iPage = interiorCursorChildPage(pCursor); ++ pCursor->iChild++; ++ if( interiorCursorPageInUse(pCursor, iPage) ){ ++ fprintf(stderr, "Loop detected at %d\n", iPage); ++ }else{ ++ int rc = sqlite3PagerAcquire(pCursor->pPage->pPager, iPage, ppPage, 0); ++ if( rc==SQLITE_OK ){ ++ return SQLITE_ROW; ++ } ++ } ++ } ++ ++ /* This page has no more children. Get next page from parent. */ ++ if( !pCursor->pParent ){ ++ return SQLITE_DONE; ++ } ++ rc = interiorCursorNextPage(&pCursor->pParent, ppPage); ++ if( rc!=SQLITE_ROW ){ ++ return rc; ++ } ++ ++ /* If a non-interior page is received, that either means that the ++ * tree is uneven, or that a child was re-used (say as an overflow ++ * page). Remove this cursor and let the caller handle the page. ++ */ ++ pPageHeader = PageHeader(*ppPage); ++ if( pPageHeader[kiPageTypeOffset]!=kTableInteriorPage ){ ++ *ppCursor = pCursor->pParent; ++ pCursor->pParent = NULL; ++ interiorCursorDestroy(pCursor); ++ return SQLITE_ROW; ++ } ++ ++ /* Iterate the new page. */ ++ interiorCursorSetPage(pCursor, *ppPage); ++ *ppPage = NULL; ++ } ++ ++ assert(NULL); /* NOTREACHED() */ ++ return SQLITE_CORRUPT; ++} ++ ++/* Large rows are spilled to overflow pages. The row's main page ++ * stores the overflow page number after the local payload, with a ++ * linked list forward from there as necessary. overflowMaybeCreate() ++ * and overflowGetSegment() provide an abstraction for accessing such ++ * data while centralizing the code. ++ * ++ * overflowDestroy - releases all resources associated with the structure. ++ * overflowMaybeCreate - create the overflow structure if it is needed ++ * to represent the given record. See function comment. ++ * overflowGetSegment - fetch a segment from the record, accounting ++ * for overflow pages. Segments which are not ++ * entirely contained with a page are constructed ++ * into a buffer which is returned. See function comment. ++ */ ++typedef struct RecoverOverflow RecoverOverflow; ++struct RecoverOverflow { ++ RecoverOverflow *pNextOverflow; ++ DbPage *pPage; ++ unsigned nPageSize; ++}; ++ ++static void overflowDestroy(RecoverOverflow *pOverflow){ ++ while( pOverflow ){ ++ RecoverOverflow *p = pOverflow; ++ pOverflow = p->pNextOverflow; ++ ++ if( p->pPage ){ ++ sqlite3PagerUnref(p->pPage); ++ p->pPage = NULL; ++ } ++ ++ memset(p, 0xA5, sizeof(*p)); ++ sqlite3_free(p); ++ } ++} ++ ++/* Internal helper. Used to detect if iPage would cause a loop. */ ++static int overflowPageInUse(RecoverOverflow *pOverflow, unsigned iPage){ ++ while( pOverflow && pOverflow->pPage->pgno!=iPage ){ ++ pOverflow = pOverflow->pNextOverflow; ++ } ++ return pOverflow!=NULL; ++} ++ ++/* Setup to access an nRecordBytes record beginning at iRecordOffset ++ * in pPage. If nRecordBytes can be satisfied entirely from pPage, ++ * then no overflow pages are needed an *pnLocalRecordBytes is set to ++ * nRecordBytes. Otherwise, *ppOverflow is set to the head of a list ++ * of overflow pages, and *pnLocalRecordBytes is set to the number of ++ * bytes local to pPage. ++ * ++ * overflowGetSegment() will do the right thing regardless of whether ++ * those values are set to be in-page or not. ++ */ ++static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, ++ unsigned iRecordOffset, unsigned nRecordBytes, ++ unsigned *pnLocalRecordBytes, ++ RecoverOverflow **ppOverflow){ ++ unsigned nLocalRecordBytes; /* Record bytes in the leaf page. */ ++ unsigned iNextPage; /* Next page number for record data. */ ++ unsigned nBytes; /* Maximum record bytes as of current page. */ ++ int rc; ++ RecoverOverflow *pFirstOverflow; /* First in linked list of pages. */ ++ RecoverOverflow *pLastOverflow; /* End of linked list. */ ++ ++ /* Calculations from the "Table B-Tree Leaf Cell" part of section ++ * 1.5 of http://www.sqlite.org/fileformat2.html . maxLocal and ++ * minLocal to match naming in btree.c. ++ */ ++ const unsigned maxLocal = nPageSize - 35; ++ const unsigned minLocal = ((nPageSize-12)*32/255)-23; /* m */ ++ ++ /* Always fit anything smaller than maxLocal. */ ++ if( nRecordBytes<=maxLocal ){ ++ *pnLocalRecordBytes = nRecordBytes; ++ *ppOverflow = NULL; ++ return SQLITE_OK; ++ } ++ ++ /* Calculate the remainder after accounting for minLocal on the leaf ++ * page and what packs evenly into overflow pages. If the remainder ++ * does not fit into maxLocal, then a partially-full overflow page ++ * will be required in any case, so store as little as possible locally. ++ */ ++ nLocalRecordBytes = minLocal+((nRecordBytes-minLocal)%(nPageSize-4)); ++ if( maxLocal<nLocalRecordBytes ){ ++ nLocalRecordBytes = minLocal; ++ } ++ ++ /* Don't read off the end of the page. */ ++ if( iRecordOffset+nLocalRecordBytes+4>nPageSize ){ ++ return SQLITE_CORRUPT; ++ } ++ ++ /* First overflow page number is after the local bytes. */ ++ iNextPage = ++ decodeUnsigned32(PageData(pPage, iRecordOffset + nLocalRecordBytes)); ++ nBytes = nLocalRecordBytes; ++ ++ /* While there are more pages to read, and more bytes are needed, ++ * get another page. ++ */ ++ pFirstOverflow = pLastOverflow = NULL; ++ rc = SQLITE_OK; ++ while( iNextPage && nBytes<nRecordBytes ){ ++ RecoverOverflow *pOverflow; /* New overflow page for the list. */ ++ ++ rc = sqlite3PagerAcquire(pPage->pPager, iNextPage, &pPage, 0); ++ if( rc!=SQLITE_OK ){ ++ break; ++ } ++ ++ pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); ++ if( !pOverflow ){ ++ sqlite3PagerUnref(pPage); ++ rc = SQLITE_NOMEM; ++ break; ++ } ++ memset(pOverflow, 0, sizeof(*pOverflow)); ++ pOverflow->pPage = pPage; ++ pOverflow->nPageSize = nPageSize; ++ ++ if( !pFirstOverflow ){ ++ pFirstOverflow = pOverflow; ++ }else{ ++ pLastOverflow->pNextOverflow = pOverflow; ++ } ++ pLastOverflow = pOverflow; ++ ++ iNextPage = decodeUnsigned32(pPage->pData); ++ nBytes += nPageSize-4; ++ ++ /* Avoid loops. */ ++ if( overflowPageInUse(pFirstOverflow, iNextPage) ){ ++ fprintf(stderr, "Overflow loop detected at %d\n", iNextPage); ++ rc = SQLITE_CORRUPT; ++ break; ++ } ++ } ++ ++ /* If there were not enough pages, or too many, things are corrupt. ++ * Not having enough pages is an obvious problem, all the data ++ * cannot be read. Too many pages means that the contents of the ++ * row between the main page and the overflow page(s) is ++ * inconsistent (most likely one or more of the overflow pages does ++ * not really belong to this row). ++ */ ++ if( rc==SQLITE_OK && (nBytes<nRecordBytes || iNextPage) ){ ++ rc = SQLITE_CORRUPT; ++ } ++ ++ if( rc==SQLITE_OK ){ ++ *ppOverflow = pFirstOverflow; ++ *pnLocalRecordBytes = nLocalRecordBytes; ++ }else if( pFirstOverflow ){ ++ overflowDestroy(pFirstOverflow); ++ } ++ return rc; ++} ++ ++/* Use in concert with overflowMaybeCreate() to efficiently read parts ++ * of a potentially-overflowing record. pPage and iRecordOffset are ++ * the values passed into overflowMaybeCreate(), nLocalRecordBytes and ++ * pOverflow are the values returned by that call. ++ * ++ * On SQLITE_OK, *ppBase points to nRequestBytes of data at ++ * iRequestOffset within the record. If the data exists contiguously ++ * in a page, a direct pointer is returned, otherwise a buffer from ++ * sqlite3_malloc() is returned with the data. *pbFree is set true if ++ * sqlite3_free() should be called on *ppBase. ++ */ ++/* Operation of this function is subtle. At any time, pPage is the ++ * current page, with iRecordOffset and nLocalRecordBytes being record ++ * data within pPage, and pOverflow being the overflow page after ++ * pPage. This allows the code to handle both the initial leaf page ++ * and overflow pages consistently by adjusting the values ++ * appropriately. ++ */ ++static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, ++ unsigned nLocalRecordBytes, ++ RecoverOverflow *pOverflow, ++ unsigned iRequestOffset, unsigned nRequestBytes, ++ unsigned char **ppBase, int *pbFree){ ++ unsigned nBase; /* Amount of data currently collected. */ ++ unsigned char *pBase; /* Buffer to collect record data into. */ ++ ++ /* Skip to the page containing the start of the data. */ ++ while( iRequestOffset>=nLocalRecordBytes && pOverflow ){ ++ /* Factor out current page's contribution. */ ++ iRequestOffset -= nLocalRecordBytes; ++ ++ /* Move forward to the next page in the list. */ ++ pPage = pOverflow->pPage; ++ iRecordOffset = 4; ++ nLocalRecordBytes = pOverflow->nPageSize - iRecordOffset; ++ pOverflow = pOverflow->pNextOverflow; ++ } ++ ++ /* If the requested data is entirely within this page, return a ++ * pointer into the page. ++ */ ++ if( iRequestOffset+nRequestBytes<=nLocalRecordBytes ){ ++ /* TODO(shess): "assignment discards qualifiers from pointer target type" ++ * Having ppBase be const makes sense, but sqlite3_free() takes non-const. ++ */ ++ *ppBase = (unsigned char *)PageData(pPage, iRecordOffset + iRequestOffset); ++ *pbFree = 0; ++ return SQLITE_OK; ++ } ++ ++ /* The data range would require additional pages. */ ++ if( !pOverflow ){ ++ /* Should never happen, the range is outside the nRecordBytes ++ * passed to overflowMaybeCreate(). ++ */ ++ assert(NULL); /* NOTREACHED */ ++ return SQLITE_ERROR; ++ } ++ ++ /* Get a buffer to construct into. */ ++ nBase = 0; ++ pBase = sqlite3_malloc(nRequestBytes); ++ if( !pBase ){ ++ return SQLITE_NOMEM; ++ } ++ while( nBase<nRequestBytes ){ ++ /* Copy over data present on this page. */ ++ unsigned nCopyBytes = nRequestBytes - nBase; ++ if( nLocalRecordBytes-iRequestOffset<nCopyBytes ){ ++ nCopyBytes = nLocalRecordBytes - iRequestOffset; ++ } ++ memcpy(pBase + nBase, PageData(pPage, iRecordOffset + iRequestOffset), ++ nCopyBytes); ++ nBase += nCopyBytes; ++ ++ if( pOverflow ){ ++ /* Copy from start of record data in future pages. */ ++ iRequestOffset = 0; ++ ++ /* Move forward to the next page in the list. Should match ++ * first while() loop. ++ */ ++ pPage = pOverflow->pPage; ++ iRecordOffset = 4; ++ nLocalRecordBytes = pOverflow->nPageSize - iRecordOffset; ++ pOverflow = pOverflow->pNextOverflow; ++ }else if( nBase<nRequestBytes ){ ++ /* Ran out of overflow pages with data left to deliver. Not ++ * possible if the requested range fits within nRecordBytes ++ * passed to overflowMaybeCreate() when creating pOverflow. ++ */ ++ assert(NULL); /* NOTREACHED */ ++ sqlite3_free(pBase); ++ return SQLITE_ERROR; ++ } ++ } ++ assert( nBase==nRequestBytes ); ++ *ppBase = pBase; ++ *pbFree = 1; ++ return SQLITE_OK; ++} ++ ++/* Primary structure for iterating the contents of a table. ++ * ++ * leafCursorDestroy - release all resources associated with the cursor. ++ * leafCursorCreate - create a cursor to iterate items from tree at ++ * the provided root page. ++ * leafCursorNextValidCell - get the cursor ready to access data from ++ * the next valid cell in the table. ++ * leafCursorCellRowid - get the current cell's rowid. ++ * leafCursorCellColumns - get current cell's column count. ++ * leafCursorCellColInfo - get type and data for a column in current cell. ++ * ++ * leafCursorNextValidCell skips cells which fail simple integrity ++ * checks, such as overlapping other cells, or being located at ++ * impossible offsets, or where header data doesn't correctly describe ++ * payload data. Returns SQLITE_ROW if a valid cell is found, ++ * SQLITE_DONE if all pages in the tree were exhausted. ++ * ++ * leafCursorCellColInfo() accounts for overflow pages in the style of ++ * overflowGetSegment(). ++ */ ++typedef struct RecoverLeafCursor RecoverLeafCursor; ++struct RecoverLeafCursor { ++ RecoverInteriorCursor *pParent; /* Parent node to this node. */ ++ DbPage *pPage; /* Reference to leaf page. */ ++ unsigned nPageSize; /* Size of pPage. */ ++ unsigned nCells; /* Number of cells in pPage. */ ++ unsigned iCell; /* Current cell. */ ++ ++ /* Info parsed from data in iCell. */ ++ i64 iRowid; /* rowid parsed. */ ++ unsigned nRecordCols; /* how many items in the record. */ ++ u64 iRecordOffset; /* offset to record data. */ ++ /* TODO(shess): nRecordBytes and nRecordHeaderBytes are used in ++ * leafCursorCellColInfo() to prevent buffer overruns. ++ * leafCursorCellDecode() already verified that the cell is valid, so ++ * those checks should be redundant. ++ */ ++ u64 nRecordBytes; /* Size of record data. */ ++ unsigned nLocalRecordBytes; /* Amount of record data in-page. */ ++ unsigned nRecordHeaderBytes; /* Size of record header data. */ ++ unsigned char *pRecordHeader; /* Pointer to record header data. */ ++ int bFreeRecordHeader; /* True if record header requires free. */ ++ RecoverOverflow *pOverflow; /* Cell overflow info, if needed. */ ++}; ++ ++/* Internal helper shared between next-page and create-cursor. If ++ * pPage is a leaf page, it will be stored in the cursor and state ++ * initialized for reading cells. ++ * ++ * If pPage is an interior page, a new parent cursor is created and ++ * injected on the stack. This is necessary to handle trees with ++ * uneven depth, but also is used during initial setup. ++ * ++ * If pPage is not a table page at all, it is discarded. ++ * ++ * If SQLITE_OK is returned, the caller no longer owns pPage, ++ * otherwise the caller is responsible for discarding it. ++ */ ++static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){ ++ const unsigned char *pPageHeader; /* Header of *pPage */ ++ ++ /* Release the current page. */ ++ if( pCursor->pPage ){ ++ sqlite3PagerUnref(pCursor->pPage); ++ pCursor->pPage = NULL; ++ pCursor->iCell = pCursor->nCells = 0; ++ } ++ ++ /* If the page is an unexpected interior node, inject a new stack ++ * layer and try again from there. ++ */ ++ pPageHeader = PageHeader(pPage); ++ if( pPageHeader[kiPageTypeOffset]==kTableInteriorPage ){ ++ RecoverInteriorCursor *pParent; ++ int rc = interiorCursorCreate(pCursor->pParent, pPage, pCursor->nPageSize, ++ &pParent); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ pCursor->pParent = pParent; ++ return SQLITE_OK; ++ } ++ ++ /* Not a leaf page, skip it. */ ++ if( pPageHeader[kiPageTypeOffset]!=kTableLeafPage ){ ++ sqlite3PagerUnref(pPage); ++ return SQLITE_OK; ++ } ++ ++ /* Take ownership of the page and start decoding. */ ++ pCursor->pPage = pPage; ++ pCursor->iCell = 0; ++ pCursor->nCells = decodeUnsigned16(pPageHeader + kiPageCellCountOffset); ++ return SQLITE_OK; ++} ++ ++/* Get the next leaf-level page in the tree. Returns SQLITE_ROW when ++ * a leaf page is found, SQLITE_DONE when no more leaves exist, or any ++ * error which occurred. ++ */ ++static int leafCursorNextPage(RecoverLeafCursor *pCursor){ ++ if( !pCursor->pParent ){ ++ return SQLITE_DONE; ++ } ++ ++ /* Repeatedly load the parent's next child page until a leaf is found. */ ++ do { ++ DbPage *pNextPage; ++ int rc = interiorCursorNextPage(&pCursor->pParent, &pNextPage); ++ if( rc!=SQLITE_ROW ){ ++ assert( rc==SQLITE_DONE ); ++ return rc; ++ } ++ ++ rc = leafCursorLoadPage(pCursor, pNextPage); ++ if( rc!=SQLITE_OK ){ ++ sqlite3PagerUnref(pNextPage); ++ return rc; ++ } ++ } while( !pCursor->pPage ); ++ ++ return SQLITE_ROW; ++} ++ ++static void leafCursorDestroyCellData(RecoverLeafCursor *pCursor){ ++ if( pCursor->bFreeRecordHeader ){ ++ sqlite3_free(pCursor->pRecordHeader); ++ } ++ pCursor->bFreeRecordHeader = 0; ++ pCursor->pRecordHeader = NULL; ++ ++ if( pCursor->pOverflow ){ ++ overflowDestroy(pCursor->pOverflow); ++ pCursor->pOverflow = NULL; ++ } ++} ++ ++static void leafCursorDestroy(RecoverLeafCursor *pCursor){ ++ leafCursorDestroyCellData(pCursor); ++ ++ if( pCursor->pParent ){ ++ interiorCursorDestroy(pCursor->pParent); ++ pCursor->pParent = NULL; ++ } ++ ++ if( pCursor->pPage ){ ++ sqlite3PagerUnref(pCursor->pPage); ++ pCursor->pPage = NULL; ++ } ++ ++ memset(pCursor, 0xA5, sizeof(*pCursor)); ++ sqlite3_free(pCursor); ++} ++ ++/* Create a cursor to iterate the rows from the leaf pages of a table ++ * rooted at iRootPage. ++ */ ++/* TODO(shess): recoverOpen() calls this to setup the cursor, and I ++ * think that recoverFilter() may make a hard assumption that the ++ * cursor returned will turn up at least one valid cell. ++ * ++ * The cases I can think of which break this assumption are: ++ * - pPage is a valid leaf page with no valid cells. ++ * - pPage is a valid interior page with no valid leaves. ++ * - pPage is a valid interior page who's leaves contain no valid cells. ++ * - pPage is not a valid leaf or interior page. ++ */ ++static int leafCursorCreate(Pager *pPager, unsigned nPageSize, ++ u32 iRootPage, RecoverLeafCursor **ppCursor){ ++ DbPage *pPage; /* Reference to page at iRootPage. */ ++ RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */ ++ int rc; ++ ++ /* Start out with the root page. */ ++ rc = sqlite3PagerAcquire(pPager, iRootPage, &pPage, 0); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); ++ if( !pCursor ){ ++ sqlite3PagerUnref(pPage); ++ return SQLITE_NOMEM; ++ } ++ memset(pCursor, 0, sizeof(*pCursor)); ++ ++ pCursor->nPageSize = nPageSize; ++ ++ rc = leafCursorLoadPage(pCursor, pPage); ++ if( rc!=SQLITE_OK ){ ++ sqlite3PagerUnref(pPage); ++ leafCursorDestroy(pCursor); ++ return rc; ++ } ++ ++ /* pPage wasn't a leaf page, find the next leaf page. */ ++ if( !pCursor->pPage ){ ++ rc = leafCursorNextPage(pCursor); ++ if( rc!=SQLITE_DONE && rc!=SQLITE_ROW ){ ++ leafCursorDestroy(pCursor); ++ return rc; ++ } ++ } ++ ++ *ppCursor = pCursor; ++ return SQLITE_OK; ++} ++ ++/* Useful for setting breakpoints. */ ++static int ValidateError(){ ++ return SQLITE_ERROR; ++} ++ ++/* Setup the cursor for reading the information from cell iCell. */ ++static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ ++ const unsigned char *pPageHeader; /* Header of current page. */ ++ const unsigned char *pCellOffsets; /* Pointer to page's cell offsets. */ ++ unsigned iCellOffset; /* Offset of current cell (iCell). */ ++ const unsigned char *pCell; /* Pointer to data at iCellOffset. */ ++ unsigned nCellMaxBytes; /* Maximum local size of iCell. */ ++ unsigned iEndOffset; /* End of iCell's in-page data. */ ++ u64 nRecordBytes; /* Expected size of cell, w/overflow. */ ++ u64 iRowid; /* iCell's rowid (in table). */ ++ unsigned nRead; /* Amount of cell read. */ ++ unsigned nRecordHeaderRead; /* Header data read. */ ++ u64 nRecordHeaderBytes; /* Header size expected. */ ++ unsigned nRecordCols; /* Columns read from header. */ ++ u64 nRecordColBytes; /* Bytes in payload for those columns. */ ++ unsigned i; ++ int rc; ++ ++ assert( pCursor->iCell<pCursor->nCells ); ++ ++ leafCursorDestroyCellData(pCursor); ++ ++ /* Find the offset to the row. */ ++ pPageHeader = PageHeader(pCursor->pPage); ++ pCellOffsets = pPageHeader + knPageLeafHeaderBytes; ++ iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iCell*2); ++ if( iCellOffset>=pCursor->nPageSize ){ ++ return ValidateError(); ++ } ++ ++ pCell = PageData(pCursor->pPage, iCellOffset); ++ nCellMaxBytes = pCursor->nPageSize - iCellOffset; ++ ++ /* B-tree leaf cells lead with varint record size, varint rowid and ++ * varint header size. ++ */ ++ /* TODO(shess): The smallest page size is 512 bytes, which has an m ++ * of 39. Three varints need at most 27 bytes to encode. I think. ++ */ ++ if( !checkVarints(pCell, nCellMaxBytes, 3) ){ ++ return ValidateError(); ++ } ++ ++ nRead = getVarint(pCell, &nRecordBytes); ++ assert( iCellOffset+nRead<=pCursor->nPageSize ); ++ pCursor->nRecordBytes = nRecordBytes; ++ ++ nRead += getVarint(pCell + nRead, &iRowid); ++ assert( iCellOffset+nRead<=pCursor->nPageSize ); ++ pCursor->iRowid = (i64)iRowid; ++ ++ pCursor->iRecordOffset = iCellOffset + nRead; ++ ++ /* Start overflow setup here because nLocalRecordBytes is needed to ++ * check cell overlap. ++ */ ++ rc = overflowMaybeCreate(pCursor->pPage, pCursor->nPageSize, ++ pCursor->iRecordOffset, pCursor->nRecordBytes, ++ &pCursor->nLocalRecordBytes, ++ &pCursor->pOverflow); ++ if( rc!=SQLITE_OK ){ ++ return ValidateError(); ++ } ++ ++ /* Check that no other cell starts within this cell. */ ++ iEndOffset = pCursor->iRecordOffset + pCursor->nLocalRecordBytes; ++ for( i=0; i<pCursor->nCells; ++i ){ ++ const unsigned iOtherOffset = decodeUnsigned16(pCellOffsets + i*2); ++ if( iOtherOffset>iCellOffset && iOtherOffset<iEndOffset ){ ++ return ValidateError(); ++ } ++ } ++ ++ nRecordHeaderRead = getVarint(pCell + nRead, &nRecordHeaderBytes); ++ assert( nRecordHeaderBytes<=nRecordBytes ); ++ pCursor->nRecordHeaderBytes = nRecordHeaderBytes; ++ ++ /* Large headers could overflow if pages are small. */ ++ rc = overflowGetSegment(pCursor->pPage, ++ pCursor->iRecordOffset, pCursor->nLocalRecordBytes, ++ pCursor->pOverflow, 0, nRecordHeaderBytes, ++ &pCursor->pRecordHeader, &pCursor->bFreeRecordHeader); ++ if( rc!=SQLITE_OK ){ ++ return ValidateError(); ++ } ++ ++ /* Tally up the column count and size of data. */ ++ nRecordCols = 0; ++ nRecordColBytes = 0; ++ while( nRecordHeaderRead<nRecordHeaderBytes ){ ++ u64 iSerialType; /* Type descriptor for current column. */ ++ if( !checkVarint(pCursor->pRecordHeader + nRecordHeaderRead, ++ nRecordHeaderBytes - nRecordHeaderRead) ){ ++ return ValidateError(); ++ } ++ nRecordHeaderRead += getVarint(pCursor->pRecordHeader + nRecordHeaderRead, ++ &iSerialType); ++ if( iSerialType==10 || iSerialType==11 ){ ++ return ValidateError(); ++ } ++ nRecordColBytes += SerialTypeLength(iSerialType); ++ nRecordCols++; ++ } ++ pCursor->nRecordCols = nRecordCols; ++ ++ /* Parsing the header used as many bytes as expected. */ ++ if( nRecordHeaderRead!=nRecordHeaderBytes ){ ++ return ValidateError(); ++ } ++ ++ /* Calculated record is size of expected record. */ ++ if( nRecordHeaderBytes+nRecordColBytes!=nRecordBytes ){ ++ return ValidateError(); ++ } ++ ++ return SQLITE_OK; ++} ++ ++static i64 leafCursorCellRowid(RecoverLeafCursor *pCursor){ ++ return pCursor->iRowid; ++} ++ ++static unsigned leafCursorCellColumns(RecoverLeafCursor *pCursor){ ++ return pCursor->nRecordCols; ++} ++ ++/* Get the column info for the cell. Pass NULL for ppBase to prevent ++ * retrieving the data segment. If *pbFree is true, *ppBase must be ++ * freed by the caller using sqlite3_free(). ++ */ ++static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, ++ unsigned iCol, u64 *piColType, ++ unsigned char **ppBase, int *pbFree){ ++ const unsigned char *pRecordHeader; /* Current cell's header. */ ++ u64 nRecordHeaderBytes; /* Bytes in pRecordHeader. */ ++ unsigned nRead; /* Bytes read from header. */ ++ u64 iColEndOffset; /* Offset to end of column in cell. */ ++ unsigned nColsSkipped; /* Count columns as procesed. */ ++ u64 iSerialType; /* Type descriptor for current column. */ ++ ++ /* Implicit NULL for columns past the end. This case happens when ++ * rows have not been updated since an ALTER TABLE added columns. ++ * It is more convenient to address here than in callers. ++ */ ++ if( iCol>=pCursor->nRecordCols ){ ++ *piColType = 0; ++ if( ppBase ){ ++ *ppBase = 0; ++ *pbFree = 0; ++ } ++ return SQLITE_OK; ++ } ++ ++ /* Must be able to decode header size. */ ++ pRecordHeader = pCursor->pRecordHeader; ++ if( !checkVarint(pRecordHeader, pCursor->nRecordHeaderBytes) ){ ++ return SQLITE_CORRUPT; ++ } ++ ++ /* Rather than caching the header size and how many bytes it took, ++ * decode it every time. ++ */ ++ nRead = getVarint(pRecordHeader, &nRecordHeaderBytes); ++ assert( nRecordHeaderBytes==pCursor->nRecordHeaderBytes ); ++ ++ /* Scan forward to the indicated column. Scans to _after_ column ++ * for later range checking. ++ */ ++ /* TODO(shess): This could get expensive for very wide tables. An ++ * array of iSerialType could be built in leafCursorCellDecode(), but ++ * the number of columns is dynamic per row, so it would add memory ++ * management complexity. Enough info to efficiently forward ++ * iterate could be kept, if all clients forward iterate ++ * (recoverColumn() may not). ++ */ ++ iColEndOffset = 0; ++ nColsSkipped = 0; ++ while( nColsSkipped<=iCol && nRead<nRecordHeaderBytes ){ ++ if( !checkVarint(pRecordHeader + nRead, nRecordHeaderBytes - nRead) ){ ++ return SQLITE_CORRUPT; ++ } ++ nRead += getVarint(pRecordHeader + nRead, &iSerialType); ++ iColEndOffset += SerialTypeLength(iSerialType); ++ nColsSkipped++; ++ } ++ ++ /* Column's data extends past record's end. */ ++ if( nRecordHeaderBytes+iColEndOffset>pCursor->nRecordBytes ){ ++ return SQLITE_CORRUPT; ++ } ++ ++ *piColType = iSerialType; ++ if( ppBase ){ ++ const u32 nColBytes = SerialTypeLength(iSerialType); ++ ++ /* Offset from start of record to beginning of column. */ ++ const unsigned iColOffset = nRecordHeaderBytes+iColEndOffset-nColBytes; ++ ++ return overflowGetSegment(pCursor->pPage, pCursor->iRecordOffset, ++ pCursor->nLocalRecordBytes, pCursor->pOverflow, ++ iColOffset, nColBytes, ppBase, pbFree); ++ } ++ return SQLITE_OK; ++} ++ ++static int leafCursorNextValidCell(RecoverLeafCursor *pCursor){ ++ while( 1 ){ ++ int rc; ++ ++ /* Move to the next cell. */ ++ pCursor->iCell++; ++ ++ /* No more cells, get the next leaf. */ ++ if( pCursor->iCell>=pCursor->nCells ){ ++ rc = leafCursorNextPage(pCursor); ++ if( rc!=SQLITE_ROW ){ ++ return rc; ++ } ++ assert( pCursor->iCell==0 ); ++ } ++ ++ /* If the cell is valid, indicate that a row is available. */ ++ rc = leafCursorCellDecode(pCursor); ++ if( rc==SQLITE_OK ){ ++ return SQLITE_ROW; ++ } ++ ++ /* Iterate until done or a valid row is found. */ ++ /* TODO(shess): Remove debugging output. */ ++ fprintf(stderr, "Skipping invalid cell\n"); ++ } ++ return SQLITE_ERROR; ++} ++ ++typedef struct Recover Recover; ++struct Recover { ++ sqlite3_vtab base; ++ sqlite3 *db; /* Host database connection */ ++ char *zDb; /* Database containing target table */ ++ char *zTable; /* Target table */ ++ unsigned nCols; /* Number of columns in target table */ ++ unsigned char *pTypes; /* Types of columns in target table */ ++}; ++ ++/* Internal helper for deleting the module. */ ++static void recoverRelease(Recover *pRecover){ ++ sqlite3_free(pRecover->zDb); ++ sqlite3_free(pRecover->zTable); ++ sqlite3_free(pRecover->pTypes); ++ memset(pRecover, 0xA5, sizeof(*pRecover)); ++ sqlite3_free(pRecover); ++} ++ ++/* Helper function for initializing the module. Forward-declared so ++ * recoverCreate() and recoverConnect() can see it. ++ */ ++static int recoverInit( ++ sqlite3 *, void *, int, const char *const*, sqlite3_vtab **, char ** ++); ++ ++static int recoverCreate( ++ sqlite3 *db, ++ void *pAux, ++ int argc, const char *const*argv, ++ sqlite3_vtab **ppVtab, ++ char **pzErr ++){ ++ FNENTRY(); ++ return recoverInit(db, pAux, argc, argv, ppVtab, pzErr); ++} ++ ++/* This should never be called. */ ++static int recoverConnect( ++ sqlite3 *db, ++ void *pAux, ++ int argc, const char *const*argv, ++ sqlite3_vtab **ppVtab, ++ char **pzErr ++){ ++ FNENTRY(); ++ return recoverInit(db, pAux, argc, argv, ppVtab, pzErr); ++} ++ ++/* No indices supported. */ ++static int recoverBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ++ FNENTRY(); ++ return SQLITE_OK; ++} ++ ++/* Logically, this should never be called. */ ++static int recoverDisconnect(sqlite3_vtab *pVtab){ ++ FNENTRY(); ++ recoverRelease((Recover*)pVtab); ++ return SQLITE_OK; ++} ++ ++static int recoverDestroy(sqlite3_vtab *pVtab){ ++ FNENTRY(); ++ recoverRelease((Recover*)pVtab); ++ return SQLITE_OK; ++} ++ ++typedef struct RecoverCursor RecoverCursor; ++struct RecoverCursor { ++ sqlite3_vtab_cursor base; ++ RecoverLeafCursor *pLeafCursor; ++ int iEncoding; ++ int bEOF; ++}; ++ ++static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ ++ Recover *pRecover = (Recover*)pVTab; ++ u32 iRootPage; /* Root page of the backing table. */ ++ int iEncoding; /* UTF encoding for backing database. */ ++ unsigned nPageSize; /* Size of pages in backing database. */ ++ Pager *pPager; /* Backing database pager. */ ++ RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */ ++ RecoverCursor *pCursor; /* Cursor to read rows from leaves. */ ++ int rc; ++ ++ FNENTRY(); ++ ++ iRootPage = 0; ++ rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, ++ &iRootPage); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ iEncoding = 0; ++ rc = getEncoding(pRecover->db, pRecover->zDb, &iEncoding); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ ++ pCursor = sqlite3_malloc(sizeof(RecoverCursor)); ++ if( !pCursor ){ ++ leafCursorDestroy(pLeafCursor); ++ return SQLITE_NOMEM; ++ } ++ memset(pCursor, 0, sizeof(*pCursor)); ++ pCursor->base.pVtab = pVTab; ++ pCursor->pLeafCursor = pLeafCursor; ++ pCursor->iEncoding = iEncoding; ++ ++ *ppCursor = (sqlite3_vtab_cursor*)pCursor; ++ return SQLITE_OK; ++} ++ ++static int recoverClose(sqlite3_vtab_cursor *cur){ ++ RecoverCursor *pCursor = (RecoverCursor*)cur; ++ FNENTRY(); ++ if( pCursor->pLeafCursor ){ ++ leafCursorDestroy(pCursor->pLeafCursor); ++ pCursor->pLeafCursor = NULL; ++ } ++ memset(pCursor, 0xA5, sizeof(*pCursor)); ++ sqlite3_free(cur); ++ return SQLITE_OK; ++} ++ ++/* Helpful place to set a breakpoint. */ ++static int RecoverInvalidCell(){ ++ return SQLITE_ERROR; ++} ++ ++/* Returns SQLITE_OK if the cell has an appropriate number of columns ++ * with the appropriate types of data. ++ */ ++static int recoverValidateLeafCell(Recover *pRecover, RecoverCursor *pCursor){ ++ unsigned i; ++ ++ /* If the row's storage has too many columns, skip it. */ ++ if( leafCursorCellColumns(pCursor->pLeafCursor)>pRecover->nCols ){ ++ return RecoverInvalidCell(); ++ } ++ ++ /* Skip rows with unexpected types. */ ++ for( i=0; i<pRecover->nCols; ++i ){ ++ u64 iType; /* Storage type of column i. */ ++ int rc; ++ ++ /* ROWID alias. */ ++ if( (pRecover->pTypes[i]&MASK_ROWID) ){ ++ continue; ++ } ++ ++ rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iType, NULL, NULL); ++ assert( rc==SQLITE_OK ); ++ if( rc!=SQLITE_OK || !SerialTypeIsCompatible(iType, pRecover->pTypes[i]) ){ ++ return RecoverInvalidCell(); ++ } ++ } ++ ++ return SQLITE_OK; ++} ++ ++static int recoverNext(sqlite3_vtab_cursor *pVtabCursor){ ++ RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; ++ Recover *pRecover = (Recover*)pCursor->base.pVtab; ++ int rc; ++ ++ FNENTRY(); ++ ++ /* Scan forward to the next cell with valid storage, then check that ++ * the stored data matches the schema. ++ */ ++ while( (rc = leafCursorNextValidCell(pCursor->pLeafCursor))==SQLITE_ROW ){ ++ if( recoverValidateLeafCell(pRecover, pCursor)==SQLITE_OK ){ ++ return SQLITE_OK; ++ } ++ } ++ ++ if( rc==SQLITE_DONE ){ ++ pCursor->bEOF = 1; ++ return SQLITE_OK; ++ } ++ ++ assert( rc!=SQLITE_OK ); ++ return rc; ++} ++ ++static int recoverFilter( ++ sqlite3_vtab_cursor *pVtabCursor, ++ int idxNum, const char *idxStr, ++ int argc, sqlite3_value **argv ++){ ++ RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; ++ Recover *pRecover = (Recover*)pCursor->base.pVtab; ++ int rc; ++ ++ FNENTRY(); ++ ++ /* Load the first cell, and iterate forward if it's not valid. */ ++ /* TODO(shess): What happens if no cells at all are valid? */ ++ rc = leafCursorCellDecode(pCursor->pLeafCursor); ++ if( rc!=SQLITE_OK || recoverValidateLeafCell(pRecover, pCursor)!=SQLITE_OK ){ ++ return recoverNext(pVtabCursor); ++ } ++ ++ return SQLITE_OK; ++} ++ ++static int recoverEof(sqlite3_vtab_cursor *pVtabCursor){ ++ RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; ++ FNENTRY(); ++ return pCursor->bEOF; ++} ++ ++static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ ++ RecoverCursor *pCursor = (RecoverCursor*)cur; ++ Recover *pRecover = (Recover*)pCursor->base.pVtab; ++ u64 iColType; /* Storage type of column i. */ ++ unsigned char *pColData; /* Column i's data. */ ++ int shouldFree; /* Non-zero if pColData should be freed. */ ++ int rc; ++ ++ FNENTRY(); ++ ++ if( i>=pRecover->nCols ){ ++ return SQLITE_ERROR; ++ } ++ ++ /* ROWID alias. */ ++ if( (pRecover->pTypes[i]&MASK_ROWID) ){ ++ sqlite3_result_int64(ctx, leafCursorCellRowid(pCursor->pLeafCursor)); ++ return SQLITE_OK; ++ } ++ ++ pColData = NULL; ++ shouldFree = 0; ++ rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iColType, ++ &pColData, &shouldFree); ++ if( rc!=SQLITE_OK ){ ++ return rc; ++ } ++ /* recoverValidateLeafCell() should guarantee that this will never ++ * occur. ++ */ ++ if( !SerialTypeIsCompatible(iColType, pRecover->pTypes[i]) ){ ++ if( shouldFree ){ ++ sqlite3_free(pColData); ++ } ++ return SQLITE_ERROR; ++ } ++ ++ switch( iColType ){ ++ case 0 : sqlite3_result_null(ctx); break; ++ case 1 : sqlite3_result_int64(ctx, decodeSigned(pColData, 1)); break; ++ case 2 : sqlite3_result_int64(ctx, decodeSigned(pColData, 2)); break; ++ case 3 : sqlite3_result_int64(ctx, decodeSigned(pColData, 3)); break; ++ case 4 : sqlite3_result_int64(ctx, decodeSigned(pColData, 4)); break; ++ case 5 : sqlite3_result_int64(ctx, decodeSigned(pColData, 6)); break; ++ case 6 : sqlite3_result_int64(ctx, decodeSigned(pColData, 8)); break; ++ case 7 : sqlite3_result_double(ctx, decodeFloat64(pColData)); break; ++ case 8 : sqlite3_result_int(ctx, 0); break; ++ case 9 : sqlite3_result_int(ctx, 1); break; ++ case 10 : assert( iColType!=10 ); break; ++ case 11 : assert( iColType!=11 ); break; ++ ++ default : { ++ u32 l = SerialTypeLength(iColType); ++ ++ /* If pColData was already allocated, arrange to pass ownership. */ ++ sqlite3_destructor_type pFn = SQLITE_TRANSIENT; ++ if( shouldFree ){ ++ pFn = sqlite3_free; ++ shouldFree = 0; ++ } ++ ++ if( SerialTypeIsBlob(iColType) ){ ++ sqlite3_result_blob(ctx, pColData, l, pFn); ++ }else{ ++ if( pCursor->iEncoding==SQLITE_UTF16LE ){ ++ sqlite3_result_text16le(ctx, (const void*)pColData, l, pFn); ++ }else if( pCursor->iEncoding==SQLITE_UTF16BE ){ ++ sqlite3_result_text16be(ctx, (const void*)pColData, l, pFn); ++ }else{ ++ sqlite3_result_text(ctx, (const char*)pColData, l, pFn); ++ } ++ } ++ } break; ++ } ++ if( shouldFree ){ ++ sqlite3_free(pColData); ++ } ++ return SQLITE_OK; ++} ++ ++static int recoverRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ ++ RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; ++ FNENTRY(); ++ *pRowid = leafCursorCellRowid(pCursor->pLeafCursor); ++ return SQLITE_OK; ++} ++ ++static sqlite3_module recoverModule = { ++ 0, /* iVersion */ ++ recoverCreate, /* xCreate - create a table */ ++ recoverConnect, /* xConnect - connect to an existing table */ ++ recoverBestIndex, /* xBestIndex - Determine search strategy */ ++ recoverDisconnect, /* xDisconnect - Disconnect from a table */ ++ recoverDestroy, /* xDestroy - Drop a table */ ++ recoverOpen, /* xOpen - open a cursor */ ++ recoverClose, /* xClose - close a cursor */ ++ recoverFilter, /* xFilter - configure scan constraints */ ++ recoverNext, /* xNext - advance a cursor */ ++ recoverEof, /* xEof */ ++ recoverColumn, /* xColumn - read data */ ++ recoverRowid, /* xRowid - read data */ ++ 0, /* xUpdate - write data */ ++ 0, /* xBegin - begin transaction */ ++ 0, /* xSync - sync transaction */ ++ 0, /* xCommit - commit transaction */ ++ 0, /* xRollback - rollback transaction */ ++ 0, /* xFindFunction - function overloading */ ++ 0, /* xRename - rename the table */ ++}; ++ ++int recoverVtableInit(sqlite3 *db){ ++ return sqlite3_create_module_v2(db, "recover", &recoverModule, NULL, 0); ++} ++ ++/* This section of code is for parsing the create input and ++ * initializing the module. ++ */ ++ ++/* Find the next word in zText and place the endpoints in pzWord*. ++ * Returns true if the word is non-empty. "Word" is defined as ++ * ASCII alphanumeric plus '_' at this time. ++ */ ++static int findWord(const char *zText, ++ const char **pzWordStart, const char **pzWordEnd){ ++ int r; ++ while( ascii_isspace(*zText) ){ ++ zText++; ++ } ++ *pzWordStart = zText; ++ while( ascii_isalnum(*zText) || *zText=='_' ){ ++ zText++; ++ } ++ r = zText>*pzWordStart; /* In case pzWordStart==pzWordEnd */ ++ *pzWordEnd = zText; ++ return r; ++} ++ ++/* Return true if the next word in zText is zWord, also setting ++ * *pzContinue to the character after the word. ++ */ ++static int expectWord(const char *zText, const char *zWord, ++ const char **pzContinue){ ++ const char *zWordStart, *zWordEnd; ++ if( findWord(zText, &zWordStart, &zWordEnd) && ++ ascii_strncasecmp(zWord, zWordStart, zWordEnd - zWordStart)==0 ){ ++ *pzContinue = zWordEnd; ++ return 1; ++ } ++ return 0; ++} ++ ++/* Parse the name and type information out of parameter. In case of ++ * success, *pzNameStart/End contain the name of the column, ++ * *pzTypeStart/End contain the top-level type, and *pTypeMask has the ++ * type mask to use for the column. ++ */ ++static int findNameAndType(const char *parameter, ++ const char **pzNameStart, const char **pzNameEnd, ++ const char **pzTypeStart, const char **pzTypeEnd, ++ unsigned char *pTypeMask){ ++ unsigned nNameLen; /* Length of found name. */ ++ const char *zEnd; /* Current end of parsed column information. */ ++ int bNotNull; /* Non-zero if NULL is not allowed for name. */ ++ int bStrict; /* Non-zero if column requires exact type match. */ ++ const char *zDummy; /* Dummy parameter, result unused. */ ++ unsigned i; ++ ++ /* strictMask is used for STRICT, strictMask|otherMask if STRICT is ++ * not supplied. zReplace provides an alternate type to expose to ++ * the caller. ++ */ ++ static struct { ++ const char *zName; ++ unsigned char strictMask; ++ unsigned char otherMask; ++ const char *zReplace; ++ } kTypeInfo[] = { ++ { "ANY", ++ MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL, ++ 0, "", ++ }, ++ { "ROWID", MASK_INTEGER | MASK_ROWID, 0, "INTEGER", }, ++ { "INTEGER", MASK_INTEGER | MASK_NULL, 0, NULL, }, ++ { "FLOAT", MASK_FLOAT | MASK_NULL, MASK_INTEGER, NULL, }, ++ { "NUMERIC", MASK_INTEGER | MASK_FLOAT | MASK_NULL, MASK_TEXT, NULL, }, ++ { "TEXT", MASK_TEXT | MASK_NULL, MASK_BLOB, NULL, }, ++ { "BLOB", MASK_BLOB | MASK_NULL, 0, NULL, }, ++ }; ++ ++ if( !findWord(parameter, pzNameStart, pzNameEnd) ){ ++ return SQLITE_MISUSE; ++ } ++ ++ /* Manifest typing, accept any storage type. */ ++ if( !findWord(*pzNameEnd, pzTypeStart, pzTypeEnd) ){ ++ *pzTypeEnd = *pzTypeStart = ""; ++ *pTypeMask = MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL; ++ return SQLITE_OK; ++ } ++ ++ nNameLen = *pzTypeEnd - *pzTypeStart; ++ for( i=0; i<ArraySize(kTypeInfo); ++i ){ ++ if( ascii_strncasecmp(kTypeInfo[i].zName, *pzTypeStart, nNameLen)==0 ){ ++ break; ++ } ++ } ++ if( i==ArraySize(kTypeInfo) ){ ++ return SQLITE_MISUSE; ++ } ++ ++ zEnd = *pzTypeEnd; ++ bStrict = 0; ++ if( expectWord(zEnd, "STRICT", &zEnd) ){ ++ /* TODO(shess): Ick. But I don't want another single-purpose ++ * flag, either. ++ */ ++ if( kTypeInfo[i].zReplace && !kTypeInfo[i].zReplace[0] ){ ++ return SQLITE_MISUSE; ++ } ++ bStrict = 1; ++ } ++ ++ bNotNull = 0; ++ if( expectWord(zEnd, "NOT", &zEnd) ){ ++ if( expectWord(zEnd, "NULL", &zEnd) ){ ++ bNotNull = 1; ++ }else{ ++ /* Anything other than NULL after NOT is an error. */ ++ return SQLITE_MISUSE; ++ } ++ } ++ ++ /* Anything else is an error. */ ++ if( findWord(zEnd, &zDummy, &zDummy) ){ ++ return SQLITE_MISUSE; ++ } ++ ++ *pTypeMask = kTypeInfo[i].strictMask; ++ if( !bStrict ){ ++ *pTypeMask |= kTypeInfo[i].otherMask; ++ } ++ if( bNotNull ){ ++ *pTypeMask &= ~MASK_NULL; ++ } ++ if( kTypeInfo[i].zReplace ){ ++ *pzTypeStart = kTypeInfo[i].zReplace; ++ *pzTypeEnd = *pzTypeStart + strlen(*pzTypeStart); ++ } ++ return SQLITE_OK; ++} ++ ++/* Parse the arguments, placing type masks in *pTypes and the exposed ++ * schema in *pzCreateSql (for sqlite3_declare_vtab). ++ */ ++static int ParseColumnsAndGenerateCreate(unsigned nCols, ++ const char *const *pCols, ++ char **pzCreateSql, ++ unsigned char *pTypes, ++ char **pzErr){ ++ unsigned i; ++ char *zCreateSql = sqlite3_mprintf("CREATE TABLE x("); ++ if( !zCreateSql ){ ++ return SQLITE_NOMEM; ++ } ++ ++ for( i=0; i<nCols; i++ ){ ++ const char *zSep = (i < nCols - 1 ? ", " : ")"); ++ const char *zNotNull = ""; ++ const char *zNameStart, *zNameEnd; ++ const char *zTypeStart, *zTypeEnd; ++ int rc = findNameAndType(pCols[i], ++ &zNameStart, &zNameEnd, ++ &zTypeStart, &zTypeEnd, ++ &pTypes[i]); ++ if( rc!=SQLITE_OK ){ ++ *pzErr = sqlite3_mprintf("unable to parse column %d", i); ++ sqlite3_free(zCreateSql); ++ return rc; ++ } ++ ++ if( !(pTypes[i]&MASK_NULL) ){ ++ zNotNull = " NOT NULL"; ++ } ++ ++ /* Add name and type to the create statement. */ ++ zCreateSql = sqlite3_mprintf("%z%.*s %.*s%s%s", ++ zCreateSql, ++ zNameEnd - zNameStart, zNameStart, ++ zTypeEnd - zTypeStart, zTypeStart, ++ zNotNull, zSep); ++ if( !zCreateSql ){ ++ return SQLITE_NOMEM; ++ } ++ } ++ ++ *pzCreateSql = zCreateSql; ++ return SQLITE_OK; ++} ++ ++/* Helper function for initializing the module. */ ++/* argv[0] module name ++ * argv[1] db name for virtual table ++ * argv[2] virtual table name ++ * argv[3] backing table name ++ * argv[4] columns ++ */ ++/* TODO(shess): Since connect isn't supported, could inline into ++ * recoverCreate(). ++ */ ++/* TODO(shess): Explore cases where it would make sense to set *pzErr. */ ++static int recoverInit( ++ sqlite3 *db, /* Database connection */ ++ void *pAux, /* unused */ ++ int argc, const char *const*argv, /* Parameters to CREATE TABLE statement */ ++ sqlite3_vtab **ppVtab, /* OUT: New virtual table */ ++ char **pzErr /* OUT: Error message, if any */ ++){ ++ const unsigned kTypeCol = 4; /* First argument with column type info. */ ++ Recover *pRecover; /* Virtual table structure being created. */ ++ char *zDot; /* Any dot found in "db.table" backing. */ ++ u32 iRootPage; /* Root page of backing table. */ ++ char *zCreateSql; /* Schema of created virtual table. */ ++ int rc; ++ ++ /* Require to be in the temp database. */ ++ if( ascii_strcasecmp(argv[1], "temp")!=0 ){ ++ *pzErr = sqlite3_mprintf("recover table must be in temp database"); ++ return SQLITE_MISUSE; ++ } ++ ++ /* Need the backing table and at least one column. */ ++ if( argc<=kTypeCol ){ ++ *pzErr = sqlite3_mprintf("no columns specified"); ++ return SQLITE_MISUSE; ++ } ++ ++ pRecover = sqlite3_malloc(sizeof(Recover)); ++ if( !pRecover ){ ++ return SQLITE_NOMEM; ++ } ++ memset(pRecover, 0, sizeof(*pRecover)); ++ pRecover->base.pModule = &recoverModule; ++ pRecover->db = db; ++ ++ /* Parse out db.table, assuming main if no dot. */ ++ zDot = strchr(argv[3], '.'); ++ if( !zDot ){ ++ pRecover->zDb = sqlite3_strdup(db->aDb[0].zName); ++ pRecover->zTable = sqlite3_strdup(argv[3]); ++ }else if( zDot>argv[3] && zDot[1]!='\0' ){ ++ pRecover->zDb = sqlite3_strndup(argv[3], zDot - argv[3]); ++ pRecover->zTable = sqlite3_strdup(zDot + 1); ++ }else{ ++ /* ".table" or "db." not allowed. */ ++ *pzErr = sqlite3_mprintf("ill-formed table specifier"); ++ recoverRelease(pRecover); ++ return SQLITE_ERROR; ++ } ++ ++ pRecover->nCols = argc - kTypeCol; ++ pRecover->pTypes = sqlite3_malloc(pRecover->nCols); ++ if( !pRecover->zDb || !pRecover->zTable || !pRecover->pTypes ){ ++ recoverRelease(pRecover); ++ return SQLITE_NOMEM; ++ } ++ ++ /* Require the backing table to exist. */ ++ /* TODO(shess): Be more pedantic about the form of the descriptor ++ * string. This already fails for poorly-formed strings, simply ++ * because there won't be a root page, but it would make more sense ++ * to be explicit. ++ */ ++ rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, &iRootPage); ++ if( rc!=SQLITE_OK ){ ++ *pzErr = sqlite3_mprintf("unable to find backing table"); ++ recoverRelease(pRecover); ++ return rc; ++ } ++ ++ /* Parse the column definitions. */ ++ rc = ParseColumnsAndGenerateCreate(pRecover->nCols, argv + kTypeCol, ++ &zCreateSql, pRecover->pTypes, pzErr); ++ if( rc!=SQLITE_OK ){ ++ recoverRelease(pRecover); ++ return rc; ++ } ++ ++ rc = sqlite3_declare_vtab(db, zCreateSql); ++ sqlite3_free(zCreateSql); ++ if( rc!=SQLITE_OK ){ ++ recoverRelease(pRecover); ++ return rc; ++ } ++ ++ *ppVtab = (sqlite3_vtab *)pRecover; ++ return SQLITE_OK; ++} diff --git a/third_party/sqlite/src/Makefile.in b/third_party/sqlite/src/Makefile.in index f3239f3..216742c 100644 --- a/third_party/sqlite/src/Makefile.in +++ b/third_party/sqlite/src/Makefile.in @@ -251,6 +251,7 @@ SRC = \ $(TOP)/src/prepare.c \ $(TOP)/src/printf.c \ $(TOP)/src/random.c \ + $(TOP)/src/recover.c \ $(TOP)/src/resolve.c \ $(TOP)/src/rowset.c \ $(TOP)/src/select.c \ diff --git a/third_party/sqlite/src/src/recover-alt.c b/third_party/sqlite/src/src/recover.c index ff4ae1c..6430c8b 100644 --- a/third_party/sqlite/src/src/recover-alt.c +++ b/third_party/sqlite/src/src/recover.c @@ -292,13 +292,14 @@ static double decodeFloat64(const unsigned char *pData){ * trust that or not. */ static int checkVarint(const unsigned char *pData, unsigned nData){ + unsigned i; + /* In the worst case the decoder takes all 8 bits of the 9th byte. */ if( nData>=9 ){ return 1; } /* Look for a high-bit-clear byte in what's left. */ - unsigned i; for( i=0; i<nData; ++i ){ if( !(pData[i]&0x80) ){ return 1; @@ -312,13 +313,15 @@ static int checkVarint(const unsigned char *pData, unsigned nData){ /* Return 1 if n varints can be read from pData/nData. */ static int checkVarints(const unsigned char *pData, unsigned nData, unsigned n){ + unsigned nCur = 0; /* Byte offset within current varint. */ + unsigned nFound = 0; /* Number of varints found. */ + unsigned i; + /* In the worst case the decoder takes all 8 bits of the 9th byte. */ if( nData>=9*n ){ return 1; } - unsigned nCur = 0, nFound = 0; - unsigned i; for( i=0; nFound<n && i<nData; ++i ){ nCur++; if( nCur==9 || !(pData[i]&0x80) ){ @@ -369,7 +372,7 @@ static int ascii_strcasecmp(const char *s1, const char *s2){ /* For some reason I kept making mistakes with offset calculations. */ static const unsigned char *PageData(DbPage *pPage, unsigned iOffset){ assert( iOffset<=pPage->nPageSize ); - return pPage->pData + iOffset; + return (unsigned char *)pPage->pData + iOffset; } /* The first page in the file contains a file header in the first 100 @@ -463,11 +466,13 @@ static int SerialTypeIsCompatible(u64 iSerialType, unsigned char mask){ * need sqlite3DbFree(), which seems intrusive. */ static char *sqlite3_strndup(const char *z, unsigned n){ + char *zNew; + if( z==NULL ){ return NULL; } - char *zNew = sqlite3_malloc(n+1); + zNew = sqlite3_malloc(n+1); if( zNew!=NULL ){ memcpy(zNew, z, n); zNew[n] = '\0'; @@ -486,20 +491,23 @@ static char *sqlite3_strdup(const char *z){ */ static int getRootPage(sqlite3 *db, const char *zDb, const char *zTable, u32 *piRootPage){ + char *zSql; /* SQL selecting root page of named element. */ + sqlite3_stmt *pStmt; + int rc; + if( strcmp(zTable, "sqlite_master")==0 ){ *piRootPage = 1; return SQLITE_OK; } - char *zSql = sqlite3_mprintf("SELECT rootpage FROM %s.sqlite_master " - "WHERE type = 'table' AND tbl_name = %Q", - zDb, zTable); + zSql = sqlite3_mprintf("SELECT rootpage FROM %s.sqlite_master " + "WHERE type = 'table' AND tbl_name = %Q", + zDb, zTable); if( !zSql ){ return SQLITE_NOMEM; } - sqlite3_stmt *pStmt = 0; - int rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ return rc; @@ -525,13 +533,14 @@ static int getRootPage(sqlite3 *db, const char *zDb, const char *zTable, } static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){ + sqlite3_stmt *pStmt; + int rc; char *zSql = sqlite3_mprintf("PRAGMA %s.encoding", zDb); if( !zSql ){ return SQLITE_NOMEM; } - sqlite3_stmt *pStmt = 0; - int rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rc!=SQLITE_OK ){ return rc; @@ -544,7 +553,7 @@ static int getEncoding(sqlite3 *db, const char *zDb, int* piEncoding){ rc = SQLITE_CORRUPT; }else if( rc==SQLITE_ROW ){ if( sqlite3_column_type(pStmt, 0)==SQLITE_TEXT ){ - const char* z = sqlite3_column_text(pStmt, 0); + const char* z = (const char *)sqlite3_column_text(pStmt, 0); /* These strings match the literals in pragma.c. */ if( !strcmp(z, "UTF-16le") ){ *piEncoding = SQLITE_UTF16LE; @@ -671,10 +680,14 @@ static int interiorCursorCreate(RecoverInteriorCursor *pParent, /* Internal helper. Return the child page number at iChild. */ static unsigned interiorCursorChildPage(RecoverInteriorCursor *pCursor){ + const unsigned char *pPageHeader; /* Header of the current page. */ + const unsigned char *pCellOffsets; /* Offset to page's cell offsets. */ + unsigned iCellOffset; /* Offset of target cell. */ + assert( pCursor->iChild<pCursor->nChildren ); /* Rightmost child is in the header. */ - const unsigned char *pPageHeader = PageHeader(pCursor->pPage); + pPageHeader = PageHeader(pCursor->pPage); if( pCursor->iChild==pCursor->nChildren-1 ){ return decodeUnsigned32(pPageHeader + kiPageRightChildOffset); } @@ -684,9 +697,8 @@ static unsigned interiorCursorChildPage(RecoverInteriorCursor *pCursor){ * module ignores ordering). The offset is from the beginning of the * page, not from the page header. */ - const unsigned char *pCellOffsets = pPageHeader + kiPageInteriorHeaderBytes; - const unsigned iCellOffset = - decodeUnsigned16(pCellOffsets + pCursor->iChild*2); + pCellOffsets = pPageHeader + kiPageInteriorHeaderBytes; + iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iChild*2); if( iCellOffset<=pCursor->nPageSize-4 ){ return decodeUnsigned32(PageData(pCursor->pPage, iCellOffset)); } @@ -738,6 +750,9 @@ static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, DbPage **ppPage){ RecoverInteriorCursor *pCursor = *ppCursor; while( 1 ){ + int rc; + const unsigned char *pPageHeader; /* Header of found page. */ + /* Find a valid child page which isn't on the stack. */ while( pCursor->iChild<pCursor->nChildren ){ const unsigned iPage = interiorCursorChildPage(pCursor); @@ -756,7 +771,7 @@ static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, if( !pCursor->pParent ){ return SQLITE_DONE; } - int rc = interiorCursorNextPage(&pCursor->pParent, ppPage); + rc = interiorCursorNextPage(&pCursor->pParent, ppPage); if( rc!=SQLITE_ROW ){ return rc; } @@ -765,7 +780,7 @@ static int interiorCursorNextPage(RecoverInteriorCursor **ppCursor, * tree is uneven, or that a child was re-used (say as an overflow * page). Remove this cursor and let the caller handle the page. */ - const unsigned char *pPageHeader = PageHeader(*ppPage); + pPageHeader = PageHeader(*ppPage); if( pPageHeader[kiPageTypeOffset]!=kTableInteriorPage ){ *ppCursor = pCursor->pParent; pCursor->pParent = NULL; @@ -840,6 +855,13 @@ static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, unsigned iRecordOffset, unsigned nRecordBytes, unsigned *pnLocalRecordBytes, RecoverOverflow **ppOverflow){ + unsigned nLocalRecordBytes; /* Record bytes in the leaf page. */ + unsigned iNextPage; /* Next page number for record data. */ + unsigned nBytes; /* Maximum record bytes as of current page. */ + int rc; + RecoverOverflow *pFirstOverflow; /* First in linked list of pages. */ + RecoverOverflow *pLastOverflow; /* End of linked list. */ + /* Calculations from the "Table B-Tree Leaf Cell" part of section * 1.5 of http://www.sqlite.org/fileformat2.html . maxLocal and * minLocal to match naming in btree.c. @@ -859,7 +881,7 @@ static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, * does not fit into maxLocal, then a partially-full overflow page * will be required in any case, so store as little as possible locally. */ - unsigned nLocalRecordBytes = minLocal+((nRecordBytes-minLocal)%(nPageSize-4)); + nLocalRecordBytes = minLocal+((nRecordBytes-minLocal)%(nPageSize-4)); if( maxLocal<nLocalRecordBytes ){ nLocalRecordBytes = minLocal; } @@ -870,25 +892,24 @@ static int overflowMaybeCreate(DbPage *pPage, unsigned nPageSize, } /* First overflow page number is after the local bytes. */ - unsigned iNextPage = + iNextPage = decodeUnsigned32(PageData(pPage, iRecordOffset + nLocalRecordBytes)); - unsigned nBytes = nLocalRecordBytes; - - /* Ends of a linked list of overflow pages. */ - RecoverOverflow *pFirstOverflow = NULL; - RecoverOverflow *pLastOverflow = NULL; + nBytes = nLocalRecordBytes; /* While there are more pages to read, and more bytes are needed, * get another page. */ - int rc = SQLITE_OK; + pFirstOverflow = pLastOverflow = NULL; + rc = SQLITE_OK; while( iNextPage && nBytes<nRecordBytes ){ + RecoverOverflow *pOverflow; /* New overflow page for the list. */ + rc = sqlite3PagerAcquire(pPage->pPager, iNextPage, &pPage, 0); if( rc!=SQLITE_OK ){ break; } - RecoverOverflow *pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); + pOverflow = sqlite3_malloc(sizeof(RecoverOverflow)); if( !pOverflow ){ sqlite3PagerUnref(pPage); rc = SQLITE_NOMEM; @@ -959,6 +980,9 @@ static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, RecoverOverflow *pOverflow, unsigned iRequestOffset, unsigned nRequestBytes, unsigned char **ppBase, int *pbFree){ + unsigned nBase; /* Amount of data currently collected. */ + unsigned char *pBase; /* Buffer to collect record data into. */ + /* Skip to the page containing the start of the data. */ while( iRequestOffset>=nLocalRecordBytes && pOverflow ){ /* Factor out current page's contribution. */ @@ -978,7 +1002,7 @@ static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, /* TODO(shess): "assignment discards qualifiers from pointer target type" * Having ppBase be const makes sense, but sqlite3_free() takes non-const. */ - *ppBase = PageData(pPage, iRecordOffset + iRequestOffset); + *ppBase = (unsigned char *)PageData(pPage, iRecordOffset + iRequestOffset); *pbFree = 0; return SQLITE_OK; } @@ -993,8 +1017,8 @@ static int overflowGetSegment(DbPage *pPage, unsigned iRecordOffset, } /* Get a buffer to construct into. */ - unsigned nBase = 0; - unsigned char *pBase = sqlite3_malloc(nRequestBytes); + nBase = 0; + pBase = sqlite3_malloc(nRequestBytes); if( !pBase ){ return SQLITE_NOMEM; } @@ -1073,7 +1097,7 @@ struct RecoverLeafCursor { * those checks should be redundant. */ u64 nRecordBytes; /* Size of record data. */ - u64 nLocalRecordBytes; /* Amount of record data in-page. */ + unsigned nLocalRecordBytes; /* Amount of record data in-page. */ unsigned nRecordHeaderBytes; /* Size of record header data. */ unsigned char *pRecordHeader; /* Pointer to record header data. */ int bFreeRecordHeader; /* True if record header requires free. */ @@ -1094,6 +1118,8 @@ struct RecoverLeafCursor { * otherwise the caller is responsible for discarding it. */ static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){ + const unsigned char *pPageHeader; /* Header of *pPage */ + /* Release the current page. */ if( pCursor->pPage ){ sqlite3PagerUnref(pCursor->pPage); @@ -1104,7 +1130,7 @@ static int leafCursorLoadPage(RecoverLeafCursor *pCursor, DbPage *pPage){ /* If the page is an unexpected interior node, inject a new stack * layer and try again from there. */ - const unsigned char *pPageHeader = PageHeader(pPage); + pPageHeader = PageHeader(pPage); if( pPageHeader[kiPageTypeOffset]==kTableInteriorPage ){ RecoverInteriorCursor *pParent; int rc = interiorCursorCreate(pCursor->pParent, pPage, pCursor->nPageSize, @@ -1202,14 +1228,17 @@ static void leafCursorDestroy(RecoverLeafCursor *pCursor){ */ static int leafCursorCreate(Pager *pPager, unsigned nPageSize, u32 iRootPage, RecoverLeafCursor **ppCursor){ + DbPage *pPage; /* Reference to page at iRootPage. */ + RecoverLeafCursor *pCursor; /* Leaf cursor being constructed. */ + int rc; + /* Start out with the root page. */ - DbPage *pPage; - int rc = sqlite3PagerAcquire(pPager, iRootPage, &pPage, 0); + rc = sqlite3PagerAcquire(pPager, iRootPage, &pPage, 0); if( rc!=SQLITE_OK ){ return rc; } - RecoverLeafCursor *pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); + pCursor = sqlite3_malloc(sizeof(RecoverLeafCursor)); if( !pCursor ){ sqlite3PagerUnref(pPage); return SQLITE_NOMEM; @@ -1245,21 +1274,36 @@ static int ValidateError(){ /* Setup the cursor for reading the information from cell iCell. */ static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ + const unsigned char *pPageHeader; /* Header of current page. */ + const unsigned char *pCellOffsets; /* Pointer to page's cell offsets. */ + unsigned iCellOffset; /* Offset of current cell (iCell). */ + const unsigned char *pCell; /* Pointer to data at iCellOffset. */ + unsigned nCellMaxBytes; /* Maximum local size of iCell. */ + unsigned iEndOffset; /* End of iCell's in-page data. */ + u64 nRecordBytes; /* Expected size of cell, w/overflow. */ + u64 iRowid; /* iCell's rowid (in table). */ + unsigned nRead; /* Amount of cell read. */ + unsigned nRecordHeaderRead; /* Header data read. */ + u64 nRecordHeaderBytes; /* Header size expected. */ + unsigned nRecordCols; /* Columns read from header. */ + u64 nRecordColBytes; /* Bytes in payload for those columns. */ + unsigned i; + int rc; + assert( pCursor->iCell<pCursor->nCells ); leafCursorDestroyCellData(pCursor); /* Find the offset to the row. */ - const unsigned char *pPageHeader = PageHeader(pCursor->pPage); - const unsigned char *pCellOffsets = pPageHeader + knPageLeafHeaderBytes; - const unsigned iCellOffset = - decodeUnsigned16(pCellOffsets + pCursor->iCell*2); + pPageHeader = PageHeader(pCursor->pPage); + pCellOffsets = pPageHeader + knPageLeafHeaderBytes; + iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iCell*2); if( iCellOffset>=pCursor->nPageSize ){ return ValidateError(); } - const unsigned char *pCell = PageData(pCursor->pPage, iCellOffset); - const unsigned nCellMaxBytes = pCursor->nPageSize - iCellOffset; + pCell = PageData(pCursor->pPage, iCellOffset); + nCellMaxBytes = pCursor->nPageSize - iCellOffset; /* B-tree leaf cells lead with varint record size, varint rowid and * varint header size. @@ -1271,12 +1315,10 @@ static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ return ValidateError(); } - u64 nRecordBytes; - unsigned nRead = getVarint(pCell, &nRecordBytes); + nRead = getVarint(pCell, &nRecordBytes); assert( iCellOffset+nRead<=pCursor->nPageSize ); pCursor->nRecordBytes = nRecordBytes; - u64 iRowid; nRead += getVarint(pCell + nRead, &iRowid); assert( iCellOffset+nRead<=pCursor->nPageSize ); pCursor->iRowid = (i64)iRowid; @@ -1286,18 +1328,16 @@ static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ /* Start overflow setup here because nLocalRecordBytes is needed to * check cell overlap. */ - int rc = overflowMaybeCreate(pCursor->pPage, pCursor->nPageSize, - pCursor->iRecordOffset, pCursor->nRecordBytes, - &pCursor->nLocalRecordBytes, - &pCursor->pOverflow); + rc = overflowMaybeCreate(pCursor->pPage, pCursor->nPageSize, + pCursor->iRecordOffset, pCursor->nRecordBytes, + &pCursor->nLocalRecordBytes, + &pCursor->pOverflow); if( rc!=SQLITE_OK ){ return ValidateError(); } /* Check that no other cell starts within this cell. */ - unsigned i; - const unsigned iEndOffset = - pCursor->iRecordOffset + pCursor->nLocalRecordBytes; + iEndOffset = pCursor->iRecordOffset + pCursor->nLocalRecordBytes; for( i=0; i<pCursor->nCells; ++i ){ const unsigned iOtherOffset = decodeUnsigned16(pCellOffsets + i*2); if( iOtherOffset>iCellOffset && iOtherOffset<iEndOffset ){ @@ -1305,8 +1345,6 @@ static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ } } - unsigned nRecordHeaderRead = 0; - u64 nRecordHeaderBytes; nRecordHeaderRead = getVarint(pCell + nRead, &nRecordHeaderBytes); assert( nRecordHeaderBytes<=nRecordBytes ); pCursor->nRecordHeaderBytes = nRecordHeaderBytes; @@ -1321,14 +1359,14 @@ static int leafCursorCellDecode(RecoverLeafCursor *pCursor){ } /* Tally up the column count and size of data. */ - unsigned nRecordCols = 0; - u64 nRecordColBytes = 0; + nRecordCols = 0; + nRecordColBytes = 0; while( nRecordHeaderRead<nRecordHeaderBytes ){ + u64 iSerialType; /* Type descriptor for current column. */ if( !checkVarint(pCursor->pRecordHeader + nRecordHeaderRead, nRecordHeaderBytes - nRecordHeaderRead) ){ return ValidateError(); } - u64 iSerialType; nRecordHeaderRead += getVarint(pCursor->pRecordHeader + nRecordHeaderRead, &iSerialType); if( iSerialType==10 || iSerialType==11 ){ @@ -1367,6 +1405,13 @@ static unsigned leafCursorCellColumns(RecoverLeafCursor *pCursor){ static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, unsigned iCol, u64 *piColType, unsigned char **ppBase, int *pbFree){ + const unsigned char *pRecordHeader; /* Current cell's header. */ + u64 nRecordHeaderBytes; /* Bytes in pRecordHeader. */ + unsigned nRead; /* Bytes read from header. */ + u64 iColEndOffset; /* Offset to end of column in cell. */ + unsigned nColsSkipped; /* Count columns as procesed. */ + u64 iSerialType; /* Type descriptor for current column. */ + /* Implicit NULL for columns past the end. This case happens when * rows have not been updated since an ALTER TABLE added columns. * It is more convenient to address here than in callers. @@ -1381,7 +1426,7 @@ static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, } /* Must be able to decode header size. */ - const unsigned char *pRecordHeader = pCursor->pRecordHeader; + pRecordHeader = pCursor->pRecordHeader; if( !checkVarint(pRecordHeader, pCursor->nRecordHeaderBytes) ){ return SQLITE_CORRUPT; } @@ -1389,8 +1434,7 @@ static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, /* Rather than caching the header size and how many bytes it took, * decode it every time. */ - u64 nRecordHeaderBytes; - unsigned nRead = getVarint(pRecordHeader, &nRecordHeaderBytes); + nRead = getVarint(pRecordHeader, &nRecordHeaderBytes); assert( nRecordHeaderBytes==pCursor->nRecordHeaderBytes ); /* Scan forward to the indicated column. Scans to _after_ column @@ -1403,9 +1447,8 @@ static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, * iterate could be kept, if all clients forward iterate * (recoverColumn() may not). */ - u64 iColEndOffset = 0; - unsigned nColsSkipped = 0; - u64 iSerialType; + iColEndOffset = 0; + nColsSkipped = 0; while( nColsSkipped<=iCol && nRead<nRecordHeaderBytes ){ if( !checkVarint(pRecordHeader + nRead, nRecordHeaderBytes - nRead) ){ return SQLITE_CORRUPT; @@ -1436,12 +1479,14 @@ static int leafCursorCellColInfo(RecoverLeafCursor *pCursor, static int leafCursorNextValidCell(RecoverLeafCursor *pCursor){ while( 1 ){ + int rc; + /* Move to the next cell. */ pCursor->iCell++; /* No more cells, get the next leaf. */ if( pCursor->iCell>=pCursor->nCells ){ - int rc = leafCursorNextPage(pCursor); + rc = leafCursorNextPage(pCursor); if( rc!=SQLITE_ROW ){ return rc; } @@ -1449,7 +1494,7 @@ static int leafCursorNextValidCell(RecoverLeafCursor *pCursor){ } /* If the cell is valid, indicate that a row is available. */ - int rc = leafCursorCellDecode(pCursor); + rc = leafCursorCellDecode(pCursor); if( rc==SQLITE_OK ){ return SQLITE_ROW; } @@ -1538,37 +1583,41 @@ struct RecoverCursor { }; static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - FNENTRY(); - Recover *pRecover = (Recover*)pVTab; + u32 iRootPage; /* Root page of the backing table. */ + int iEncoding; /* UTF encoding for backing database. */ + unsigned nPageSize; /* Size of pages in backing database. */ + Pager *pPager; /* Backing database pager. */ + RecoverLeafCursor *pLeafCursor; /* Cursor to read table's leaf pages. */ + RecoverCursor *pCursor; /* Cursor to read rows from leaves. */ + int rc; + + FNENTRY(); - u32 iRootPage = 0; - int rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, - &iRootPage); + iRootPage = 0; + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, + &iRootPage); if( rc!=SQLITE_OK ){ return rc; } - int iEncoding = 0; + iEncoding = 0; rc = getEncoding(pRecover->db, pRecover->zDb, &iEncoding); if( rc!=SQLITE_OK ){ return rc; } - unsigned nPageSize; - Pager *pPager; rc = GetPager(pRecover->db, pRecover->zDb, &pPager, &nPageSize); if( rc!=SQLITE_OK ){ return rc; } - RecoverLeafCursor *pLeafCursor; rc = leafCursorCreate(pPager, nPageSize, iRootPage, &pLeafCursor); if( rc!=SQLITE_OK ){ return rc; } - RecoverCursor *pCursor = sqlite3_malloc(sizeof(RecoverCursor)); + pCursor = sqlite3_malloc(sizeof(RecoverCursor)); if( !pCursor ){ leafCursorDestroy(pLeafCursor); return SQLITE_NOMEM; @@ -1583,8 +1632,8 @@ static int recoverOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ } static int recoverClose(sqlite3_vtab_cursor *cur){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)cur; + FNENTRY(); if( pCursor->pLeafCursor ){ leafCursorDestroy(pCursor->pLeafCursor); pCursor->pLeafCursor = NULL; @@ -1603,21 +1652,24 @@ static int RecoverInvalidCell(){ * with the appropriate types of data. */ static int recoverValidateLeafCell(Recover *pRecover, RecoverCursor *pCursor){ + unsigned i; + /* If the row's storage has too many columns, skip it. */ if( leafCursorCellColumns(pCursor->pLeafCursor)>pRecover->nCols ){ return RecoverInvalidCell(); } /* Skip rows with unexpected types. */ - unsigned i; for( i=0; i<pRecover->nCols; ++i ){ + u64 iType; /* Storage type of column i. */ + int rc; + /* ROWID alias. */ if( (pRecover->pTypes[i]&MASK_ROWID) ){ continue; } - u64 iType; - int rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iType, NULL, NULL); + rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iType, NULL, NULL); assert( rc==SQLITE_OK ); if( rc!=SQLITE_OK || !SerialTypeIsCompatible(iType, pRecover->pTypes[i]) ){ return RecoverInvalidCell(); @@ -1628,11 +1680,12 @@ static int recoverValidateLeafCell(Recover *pRecover, RecoverCursor *pCursor){ } static int recoverNext(sqlite3_vtab_cursor *pVtabCursor){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; Recover *pRecover = (Recover*)pCursor->base.pVtab; int rc; + FNENTRY(); + /* Scan forward to the next cell with valid storage, then check that * the stored data matches the schema. */ @@ -1656,13 +1709,15 @@ static int recoverFilter( int idxNum, const char *idxStr, int argc, sqlite3_value **argv ){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; Recover *pRecover = (Recover*)pCursor->base.pVtab; + int rc; + + FNENTRY(); /* Load the first cell, and iterate forward if it's not valid. */ /* TODO(shess): What happens if no cells at all are valid? */ - int rc = leafCursorCellDecode(pCursor->pLeafCursor); + rc = leafCursorCellDecode(pCursor->pLeafCursor); if( rc!=SQLITE_OK || recoverValidateLeafCell(pRecover, pCursor)!=SQLITE_OK ){ return recoverNext(pVtabCursor); } @@ -1671,15 +1726,20 @@ static int recoverFilter( } static int recoverEof(sqlite3_vtab_cursor *pVtabCursor){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + FNENTRY(); return pCursor->bEOF; } static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)cur; Recover *pRecover = (Recover*)pCursor->base.pVtab; + u64 iColType; /* Storage type of column i. */ + unsigned char *pColData; /* Column i's data. */ + int shouldFree; /* Non-zero if pColData should be freed. */ + int rc; + + FNENTRY(); if( i>=pRecover->nCols ){ return SQLITE_ERROR; @@ -1691,11 +1751,10 @@ static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ return SQLITE_OK; } - u64 iColType; - unsigned char *pColData = NULL; - int shouldFree = 0; - int rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iColType, - &pColData, &shouldFree); + pColData = NULL; + shouldFree = 0; + rc = leafCursorCellColInfo(pCursor->pLeafCursor, i, &iColType, + &pColData, &shouldFree); if( rc!=SQLITE_OK ){ return rc; } @@ -1724,6 +1783,8 @@ static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ case 11 : assert( iColType!=11 ); break; default : { + u32 l = SerialTypeLength(iColType); + /* If pColData was already allocated, arrange to pass ownership. */ sqlite3_destructor_type pFn = SQLITE_TRANSIENT; if( shouldFree ){ @@ -1731,7 +1792,6 @@ static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ shouldFree = 0; } - u32 l = SerialTypeLength(iColType); if( SerialTypeIsBlob(iColType) ){ sqlite3_result_blob(ctx, pColData, l, pFn); }else{ @@ -1752,8 +1812,8 @@ static int recoverColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ } static int recoverRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){ - FNENTRY(); RecoverCursor *pCursor = (RecoverCursor*)pVtabCursor; + FNENTRY(); *pRowid = leafCursorCellRowid(pCursor->pLeafCursor); return SQLITE_OK; } @@ -1795,6 +1855,7 @@ int recoverVtableInit(sqlite3 *db){ */ static int findWord(const char *zText, const char **pzWordStart, const char **pzWordEnd){ + int r; while( ascii_isspace(*zText) ){ zText++; } @@ -1802,7 +1863,7 @@ static int findWord(const char *zText, while( ascii_isalnum(*zText) || *zText=='_' ){ zText++; } - int r = zText>*pzWordStart; /* In case pzWordStart==pzWordEnd */ + r = zText>*pzWordStart; /* In case pzWordStart==pzWordEnd */ *pzWordEnd = zText; return r; } @@ -1830,16 +1891,12 @@ static int findNameAndType(const char *parameter, const char **pzNameStart, const char **pzNameEnd, const char **pzTypeStart, const char **pzTypeEnd, unsigned char *pTypeMask){ - if( !findWord(parameter, pzNameStart, pzNameEnd) ){ - return SQLITE_MISUSE; - } - - /* Manifest typing, accept any storage type. */ - if( !findWord(*pzNameEnd, pzTypeStart, pzTypeEnd) ){ - *pzTypeEnd = *pzTypeStart = ""; - *pTypeMask = MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL; - return SQLITE_OK; - } + unsigned nNameLen; /* Length of found name. */ + const char *zEnd; /* Current end of parsed column information. */ + int bNotNull; /* Non-zero if NULL is not allowed for name. */ + int bStrict; /* Non-zero if column requires exact type match. */ + const char *zDummy; /* Dummy parameter, result unused. */ + unsigned i; /* strictMask is used for STRICT, strictMask|otherMask if STRICT is * not supplied. zReplace provides an alternate type to expose to @@ -1863,7 +1920,18 @@ static int findNameAndType(const char *parameter, { "BLOB", MASK_BLOB | MASK_NULL, 0, NULL, }, }; - unsigned i, nNameLen = *pzTypeEnd - *pzTypeStart; + if( !findWord(parameter, pzNameStart, pzNameEnd) ){ + return SQLITE_MISUSE; + } + + /* Manifest typing, accept any storage type. */ + if( !findWord(*pzNameEnd, pzTypeStart, pzTypeEnd) ){ + *pzTypeEnd = *pzTypeStart = ""; + *pTypeMask = MASK_INTEGER | MASK_FLOAT | MASK_BLOB | MASK_TEXT | MASK_NULL; + return SQLITE_OK; + } + + nNameLen = *pzTypeEnd - *pzTypeStart; for( i=0; i<ArraySize(kTypeInfo); ++i ){ if( ascii_strncasecmp(kTypeInfo[i].zName, *pzTypeStart, nNameLen)==0 ){ break; @@ -1873,8 +1941,8 @@ static int findNameAndType(const char *parameter, return SQLITE_MISUSE; } - const char *zEnd = *pzTypeEnd; - int bStrict = 0; + zEnd = *pzTypeEnd; + bStrict = 0; if( expectWord(zEnd, "STRICT", &zEnd) ){ /* TODO(shess): Ick. But I don't want another single-purpose * flag, either. @@ -1885,7 +1953,7 @@ static int findNameAndType(const char *parameter, bStrict = 1; } - int bNotNull = 0; + bNotNull = 0; if( expectWord(zEnd, "NOT", &zEnd) ){ if( expectWord(zEnd, "NULL", &zEnd) ){ bNotNull = 1; @@ -1896,7 +1964,6 @@ static int findNameAndType(const char *parameter, } /* Anything else is an error. */ - const char *zDummy; if( findWord(zEnd, &zDummy, &zDummy) ){ return SQLITE_MISUSE; } @@ -1923,13 +1990,15 @@ static int ParseColumnsAndGenerateCreate(unsigned nCols, char **pzCreateSql, unsigned char *pTypes, char **pzErr){ + unsigned i; char *zCreateSql = sqlite3_mprintf("CREATE TABLE x("); if( !zCreateSql ){ return SQLITE_NOMEM; } - unsigned i; for( i=0; i<nCols; i++ ){ + const char *zSep = (i < nCols - 1 ? ", " : ")"); + const char *zNotNull = ""; const char *zNameStart, *zNameEnd; const char *zTypeStart, *zTypeEnd; int rc = findNameAndType(pCols[i], @@ -1942,13 +2011,11 @@ static int ParseColumnsAndGenerateCreate(unsigned nCols, return rc; } - const char *zNotNull = ""; if( !(pTypes[i]&MASK_NULL) ){ zNotNull = " NOT NULL"; } /* Add name and type to the create statement. */ - const char *zSep = (i < nCols - 1 ? ", " : ")"); zCreateSql = sqlite3_mprintf("%z%.*s %.*s%s%s", zCreateSql, zNameEnd - zNameStart, zNameStart, @@ -1964,6 +2031,12 @@ static int ParseColumnsAndGenerateCreate(unsigned nCols, } /* Helper function for initializing the module. */ +/* argv[0] module name + * argv[1] db name for virtual table + * argv[2] virtual table name + * argv[3] backing table name + * argv[4] columns + */ /* TODO(shess): Since connect isn't supported, could inline into * recoverCreate(). */ @@ -1975,13 +2048,12 @@ static int recoverInit( sqlite3_vtab **ppVtab, /* OUT: New virtual table */ char **pzErr /* OUT: Error message, if any */ ){ - /* argv[0] module name - * argv[1] db name for virtual table - * argv[2] virtual table name - * argv[3] backing table name - * argv[4] columns - */ - const unsigned kTypeCol = 4; + const unsigned kTypeCol = 4; /* First argument with column type info. */ + Recover *pRecover; /* Virtual table structure being created. */ + char *zDot; /* Any dot found in "db.table" backing. */ + u32 iRootPage; /* Root page of backing table. */ + char *zCreateSql; /* Schema of created virtual table. */ + int rc; /* Require to be in the temp database. */ if( ascii_strcasecmp(argv[1], "temp")!=0 ){ @@ -1995,7 +2067,7 @@ static int recoverInit( return SQLITE_MISUSE; } - Recover *pRecover = sqlite3_malloc(sizeof(Recover)); + pRecover = sqlite3_malloc(sizeof(Recover)); if( !pRecover ){ return SQLITE_NOMEM; } @@ -2004,7 +2076,7 @@ static int recoverInit( pRecover->db = db; /* Parse out db.table, assuming main if no dot. */ - char *zDot = strchr(argv[3], '.'); + zDot = strchr(argv[3], '.'); if( !zDot ){ pRecover->zDb = sqlite3_strdup(db->aDb[0].zName); pRecover->zTable = sqlite3_strdup(argv[3]); @@ -2031,9 +2103,7 @@ static int recoverInit( * because there won't be a root page, but it would make more sense * to be explicit. */ - u32 iRootPage; - int rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, - &iRootPage); + rc = getRootPage(pRecover->db, pRecover->zDb, pRecover->zTable, &iRootPage); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("unable to find backing table"); recoverRelease(pRecover); @@ -2041,7 +2111,6 @@ static int recoverInit( } /* Parse the column definitions. */ - char *zCreateSql; rc = ParseColumnsAndGenerateCreate(pRecover->nCols, argv + kTypeCol, &zCreateSql, pRecover->pTypes, pzErr); if( rc!=SQLITE_OK ){ diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in index 62b9326..fb76659 100644 --- a/third_party/sqlite/src/src/sqlite.h.in +++ b/third_party/sqlite/src/src/sqlite.h.in @@ -6403,6 +6403,17 @@ int sqlite3_wal_checkpoint_v2( #define SQLITE_CHECKPOINT_RESTART 2 +/* Begin recover.patch for Chromium */ +/* +** Call to initialize the recover virtual-table modules (see recover.c). +** +** This could be loaded by default in main.c, but that would make the +** virtual table available to Web SQL. Breaking it out allows only +** selected users to enable it (currently sql/recovery.cc). +*/ +int recoverVtableInit(sqlite3 *db); +/* End recover.patch for Chromium */ + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/third_party/sqlite/src/tool/mksqlite3c.tcl b/third_party/sqlite/src/tool/mksqlite3c.tcl index fa99f2d..df2df07 100644 --- a/third_party/sqlite/src/tool/mksqlite3c.tcl +++ b/third_party/sqlite/src/tool/mksqlite3c.tcl @@ -293,6 +293,8 @@ foreach file { main.c notify.c + recover.c + fts3.c fts3_aux.c fts3_expr.c |