|  | 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; | 
|  | +} |