Index: third_party/sqlite/patches/0006-Virtual-table-supporting-recovery-of-corrupted-datab.patch |
diff --git a/third_party/sqlite/recover.patch b/third_party/sqlite/patches/0006-Virtual-table-supporting-recovery-of-corrupted-datab.patch |
similarity index 62% |
rename from third_party/sqlite/recover.patch |
rename to third_party/sqlite/patches/0006-Virtual-table-supporting-recovery-of-corrupted-datab.patch |
index b601a445142929a41ca5eaf874e2c340dffaec4f..24fcbcf6759996d5b14a9dcd70cb8fe042195369 100644 |
--- a/third_party/sqlite/recover.patch |
+++ b/third_party/sqlite/patches/0006-Virtual-table-supporting-recovery-of-corrupted-datab.patch |
@@ -1,19 +1,43 @@ |
-Add new virtual table 'recover' to src/ and the amalgamation. |
+From 13a64d3c7fb541be8cb753c575837c1b84f5d9fd Mon Sep 17 00:00:00 2001 |
+From: Scott Hess <shess@chromium.org> |
+Date: Sat, 20 Jul 2013 11:42:21 -0700 |
+Subject: [PATCH 06/16] Virtual table supporting recovery of corrupted |
+ databases. |
-Since recover.c is in somewhat active development, it is possible that |
-the patch below will not reliably re-create the file. |
+"recover" implements a virtual table which uses the SQLite pager layer |
+to read table pages and pull out the data which is structurally sound |
+(at least at the storage layer). |
-shess@chromium.org |
+BUG=109482 |
-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.] |
+Since this implements a new feature for SQLite, the review URLs aren't |
+listed. This patch and the top of recover.c should be considered |
+authoritative. The history is mostly under |
+third_party/sqlite/src/src/{recover,recover-alt}.c . |
+--- |
+ third_party/sqlite/src/Makefile.in | 1 + |
+ third_party/sqlite/src/Makefile.linux-gcc | 4 + |
+ third_party/sqlite/src/main.mk | 4 +- |
+ third_party/sqlite/src/src/main.c | 8 + |
+ third_party/sqlite/src/src/recover.c | 2164 ++++++++++++++++++++++++++++ |
+ third_party/sqlite/src/src/sqlite.h.in | 11 + |
+ third_party/sqlite/src/test/recover.test | 147 ++ |
+ third_party/sqlite/src/test/recover0.test | 532 +++++++ |
+ third_party/sqlite/src/test/recover1.test | 429 ++++++ |
+ third_party/sqlite/src/test/recover2.test | 157 ++ |
+ third_party/sqlite/src/tool/mksqlite3c.tcl | 2 + |
+ 11 files changed, 3458 insertions(+), 1 deletion(-) |
+ create mode 100644 third_party/sqlite/src/src/recover.c |
+ create mode 100644 third_party/sqlite/src/test/recover.test |
+ create mode 100644 third_party/sqlite/src/test/recover0.test |
+ create mode 100644 third_party/sqlite/src/test/recover1.test |
+ create mode 100644 third_party/sqlite/src/test/recover2.test |
-diff --git Makefile.in Makefile.in |
-index f3239f3..216742c 100644 |
---- Makefile.in |
-+++ Makefile.in |
-@@ -251,6 +251,7 @@ SRC = \ |
+diff --git a/third_party/sqlite/src/Makefile.in b/third_party/sqlite/src/Makefile.in |
+index a2213e8..1389486 100644 |
+--- a/third_party/sqlite/src/Makefile.in |
++++ b/third_party/sqlite/src/Makefile.in |
+@@ -253,6 +253,7 @@ SRC = \ |
$(TOP)/src/prepare.c \ |
$(TOP)/src/printf.c \ |
$(TOP)/src/random.c \ |
@@ -21,47 +45,75 @@ index f3239f3..216742c 100644 |
$(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 |
+diff --git a/third_party/sqlite/src/Makefile.linux-gcc b/third_party/sqlite/src/Makefile.linux-gcc |
+index 554bf56..e631816 100644 |
+--- a/third_party/sqlite/src/Makefile.linux-gcc |
++++ b/third_party/sqlite/src/Makefile.linux-gcc |
+@@ -82,6 +82,10 @@ OPTS += -DSQLITE_MEMDEBUG=1 |
+ # TODO(shess) I can't see why I need this setting. |
+ OPTS += -DOS_UNIX=1 |
++# The recover virtual table is not generally enabled. Enable it for testing |
++# purposes. |
++OPTS += -DDEFAULT_ENABLE_RECOVER=1 |
++ |
+ #### The suffix to add to executable files. ".exe" for windows. |
+ # Nothing for unix. |
+ # |
+diff --git a/third_party/sqlite/src/main.mk b/third_party/sqlite/src/main.mk |
+index dc56b0d..2189fd6 100644 |
+--- a/third_party/sqlite/src/main.mk |
++++ b/third_party/sqlite/src/main.mk |
+@@ -65,7 +65,7 @@ LIBOBJ+= vdbe.o parse.o \ |
+ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ |
+ notify.o opcodes.o os.o os_unix.o os_win.o \ |
+ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ |
+- random.o resolve.o rowset.o rtree.o select.o status.o \ |
++ random.o recover.o resolve.o rowset.o rtree.o select.o status.o \ |
+ table.o threads.o tokenize.o trigger.o \ |
+ update.o userauth.o util.o vacuum.o \ |
+ vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \ |
+@@ -135,6 +135,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 \ |
+@@ -315,6 +316,7 @@ TESTSRC2 = \ |
+ $(TOP)/src/prepare.c \ |
+ $(TOP)/src/printf.c \ |
+ $(TOP)/src/random.c \ |
++ $(TOP)/src/recover.c \ |
+ $(TOP)/src/pcache.c \ |
+ $(TOP)/src/pcache1.c \ |
+ $(TOP)/src/select.c \ |
+diff --git a/third_party/sqlite/src/src/main.c b/third_party/sqlite/src/src/main.c |
+index fc03700..d15ab9bb 100644 |
+--- a/third_party/sqlite/src/src/main.c |
++++ b/third_party/sqlite/src/src/main.c |
+@@ -2644,6 +2644,14 @@ static int openDatabase( |
+ } |
+ #endif |
-+/* 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 |
++#ifdef DEFAULT_ENABLE_RECOVER |
++ /* Initialize recover virtual table for testing. */ |
++ extern int recoverVtableInit(sqlite3 *db); |
++ if( !db->mallocFailed && rc==SQLITE_OK ){ |
++ rc = recoverVtableInit(db); |
++ } |
++#endif |
+ |
- fts3.c |
- fts3_aux.c |
- fts3_expr.c |
-diff --git src/recover.c src/recover.c |
+ #ifdef SQLITE_ENABLE_ICU |
+ if( !db->mallocFailed && rc==SQLITE_OK ){ |
+ rc = sqlite3IcuInit(db); |
+diff --git a/third_party/sqlite/src/src/recover.c b/third_party/sqlite/src/src/recover.c |
new file mode 100644 |
-index 0000000..6430c8b |
+index 0000000..c996d53 |
--- /dev/null |
-+++ src/recover.c |
-@@ -0,0 +1,2130 @@ |
++++ b/third_party/sqlite/src/src/recover.c |
+@@ -0,0 +1,2164 @@ |
+/* |
+** 2012 Jan 11 |
+** |
@@ -707,6 +759,8 @@ index 0000000..6430c8b |
+/* Internal helper. Reset storage in preparation for iterating pPage. */ |
+static void interiorCursorSetPage(RecoverInteriorCursor *pCursor, |
+ DbPage *pPage){ |
++ const unsigned knMinCellLength = 2 + 4 + 1; |
++ unsigned nMaxChildren; |
+ assert( PageHeader(pPage)[kiPageTypeOffset]==kTableInteriorPage ); |
+ |
+ if( pCursor->pPage ){ |
@@ -717,12 +771,27 @@ index 0000000..6430c8b |
+ 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; |
++ |
++ /* Each child requires a 16-bit offset from an array after the header, |
++ * and each child contains a 32-bit page number and at least a varint |
++ * (min size of one byte). The final child page is in the header. So |
++ * the maximum value for nChildren is: |
++ * (nPageSize - kiPageInteriorHeaderBytes) / |
++ * (sizeof(uint16) + sizeof(uint32) + 1) + 1 |
++ */ |
++ /* TODO(shess): This count is very unlikely to be corrupted in |
++ * isolation, so seeing this could signal to skip the page. OTOH, I |
++ * can't offhand think of how to get here unless this or the page-type |
++ * byte is corrupted. Could be an overflow page, but it would require |
++ * a very large database. |
++ */ |
++ nMaxChildren = |
++ (pCursor->nPageSize - kiPageInteriorHeaderBytes) / knMinCellLength + 1; |
++ if (pCursor->nChildren > nMaxChildren) { |
++ pCursor->nChildren = nMaxChildren; |
++ } |
+} |
+ |
+static int interiorCursorCreate(RecoverInteriorCursor *pParent, |
@@ -1339,6 +1408,7 @@ index 0000000..6430c8b |
+/* 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 *pPageEnd; /* Byte after end 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. */ |
@@ -1361,6 +1431,10 @@ index 0000000..6430c8b |
+ /* Find the offset to the row. */ |
+ pPageHeader = PageHeader(pCursor->pPage); |
+ pCellOffsets = pPageHeader + knPageLeafHeaderBytes; |
++ pPageEnd = PageData(pCursor->pPage, pCursor->nPageSize); |
++ if( pCellOffsets + pCursor->iCell*2 + 2 > pPageEnd ){ |
++ return ValidateError(); |
++ } |
+ iCellOffset = decodeUnsigned16(pCellOffsets + pCursor->iCell*2); |
+ if( iCellOffset>=pCursor->nPageSize ){ |
+ return ValidateError(); |
@@ -1402,7 +1476,7 @@ index 0000000..6430c8b |
+ |
+ /* Check that no other cell starts within this cell. */ |
+ iEndOffset = pCursor->iRecordOffset + pCursor->nLocalRecordBytes; |
-+ for( i=0; i<pCursor->nCells; ++i ){ |
++ for( i=0; i<pCursor->nCells && pCellOffsets + i*2 + 2 <= pPageEnd; ++i ){ |
+ const unsigned iOtherOffset = decodeUnsigned16(pCellOffsets + i*2); |
+ if( iOtherOffset>iCellOffset && iOtherOffset<iEndOffset ){ |
+ return ValidateError(); |
@@ -1691,6 +1765,12 @@ index 0000000..6430c8b |
+ pCursor->pLeafCursor = pLeafCursor; |
+ pCursor->iEncoding = iEncoding; |
+ |
++ /* If no leaf pages were found, empty result set. */ |
++ /* TODO(shess): leafCursorNextValidCell() would return SQLITE_ROW or |
++ * SQLITE_DONE to indicate whether there is further data to consider. |
++ */ |
++ pCursor->bEOF = (pLeafCursor->pPage==NULL); |
++ |
+ *ppCursor = (sqlite3_vtab_cursor*)pCursor; |
+ return SQLITE_OK; |
+} |
@@ -1779,8 +1859,14 @@ index 0000000..6430c8b |
+ |
+ FNENTRY(); |
+ |
-+ /* Load the first cell, and iterate forward if it's not valid. */ |
-+ /* TODO(shess): What happens if no cells at all are valid? */ |
++ /* There were no valid leaf pages in the table. */ |
++ if( pCursor->bEOF ){ |
++ return SQLITE_OK; |
++ } |
++ |
++ /* Load the first cell, and iterate forward if it's not valid. If no cells at |
++ * all are valid, recoverNext() sets bEOF and returns appropriately. |
++ */ |
+ rc = leafCursorCellDecode(pCursor->pLeafCursor); |
+ if( rc!=SQLITE_OK || recoverValidateLeafCell(pRecover, pCursor)!=SQLITE_OK ){ |
+ return recoverNext(pVtabCursor); |
@@ -2192,3 +2278,1330 @@ index 0000000..6430c8b |
+ *ppVtab = (sqlite3_vtab *)pRecover; |
+ return SQLITE_OK; |
+} |
+diff --git a/third_party/sqlite/src/src/sqlite.h.in b/third_party/sqlite/src/src/sqlite.h.in |
+index f1d4e40..28b5ef1 100644 |
+--- a/third_party/sqlite/src/src/sqlite.h.in |
++++ b/third_party/sqlite/src/src/sqlite.h.in |
+@@ -7408,6 +7408,17 @@ int sqlite3_vtab_on_conflict(sqlite3 *); |
+ |
+ |
+ |
++/* Begin recover.patch for Chromium */ |
++/* |
++** Call to initialize the recover virtual-table modules (see recover.c). |
++** |
++** This could be loaded by default in main.c, but that would make the |
++** virtual table available to Web SQL. Breaking it out allows only |
++** selected users to enable it (currently sql/recovery.cc). |
++*/ |
++int recoverVtableInit(sqlite3 *db); |
++/* End recover.patch for Chromium */ |
++ |
+ /* |
+ ** Undo the hack that converts floating point types to integer for |
+ ** builds on processors without floating point support. |
+diff --git a/third_party/sqlite/src/test/recover.test b/third_party/sqlite/src/test/recover.test |
+new file mode 100644 |
+index 0000000..b5aa182 |
+--- /dev/null |
++++ b/third_party/sqlite/src/test/recover.test |
+@@ -0,0 +1,147 @@ |
++# 2012 January 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. |
++# |
++#*********************************************************************** |
++# This file implements regression tests for SQLite library. |
++# |
++# This file implements tests for the recover module, which can read |
++# through corrupt rows and pages. |
++# |
++# $Id$ |
++ |
++# TODO(shess): These all test that the module correctly reads good |
++# data. It would be good to implement tests of corrupt data. |
++ |
++set testdir [file dirname $argv0] |
++source $testdir/tester.tcl |
++ |
++db eval { |
++ DROP TABLE IF EXISTS altered; |
++ CREATE TABLE altered ( |
++ c TEXT |
++ ); |
++ INSERT INTO altered VALUES ('a'); |
++ INSERT INTO altered VALUES ('b'); |
++ INSERT INTO altered VALUES ('c'); |
++ ALTER TABLE altered ADD COLUMN i INTEGER NOT NULL DEFAULT 10; |
++ INSERT INTO altered VALUES ('d', 5); |
++} |
++ |
++# SQLite will fill the earlier rows with the default. |
++do_test recover-alter-1.0 { |
++ execsql {SELECT c, i FROM altered ORDER BY rowid} |
++} {a 10 b 10 c 10 d 5} |
++ |
++# recover sees NULL for those rows. |
++do_test recover-alter-1.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.altered_recover; |
++ CREATE VIRTUAL TABLE temp.altered_recover USING recover( |
++ altered, |
++ c TEXT, |
++ i INTEGER |
++ ); |
++ } |
++ execsql {SELECT c, i FROM altered_recover ORDER BY rowid} |
++} {a {} b {} c {} d 5} |
++ |
++# Can skip those NULL columns like if they contained a real NULL. |
++do_test recover-alter-1.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.altered_recover; |
++ CREATE VIRTUAL TABLE temp.altered_recover USING recover( |
++ altered, |
++ c TEXT, |
++ i INTEGER NOT NULL |
++ ); |
++ } |
++ execsql {SELECT c, i FROM altered_recover ORDER BY rowid} |
++} {d 5} |
++ |
++if {0} { |
++# It would be neat if this could work. I tried putting "DEFAULT ..." |
++# in the schema exposed by the recover table, but it doesn't do the |
++# trick. |
++do_test recover-alter-1.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.altered_recover; |
++ CREATE VIRTUAL TABLE temp.altered_recover USING recover( |
++ altered, |
++ c TEXT, |
++ i INTEGER NOT NULL DEFAULT 10 |
++ ); |
++ } |
++ execsql {SELECT c, i FROM altered_recover ORDER BY rowid} |
++} {a 10 b 10 c 10 d 5} |
++} |
++ |
++# Helper function to generate an arbitrarily-sized table. |
++proc generate {table base count} { |
++ db eval "DROP TABLE IF EXISTS $table" |
++ db transaction immediate { |
++ db eval "CREATE TABLE $table (t TEXT,n INT)" |
++ for {set i 0} {$i<$count} {incr i} { |
++ set t [concat $base $i] |
++ db eval [concat {INSERT INTO} $table {VALUES ($t, $i)}] |
++ } |
++ } |
++} |
++ |
++# Leaf-only database parses. |
++do_test recover-leaf-1.0 { |
++ db close |
++ sqlite3 db test.db |
++ generate "leaf" "Leaf-node-generating line " 10 |
++ |
++ db eval { |
++ DROP TABLE IF EXISTS temp.leaf_recover; |
++ CREATE VIRTUAL TABLE temp.leaf_recover USING recover( |
++ leaf, |
++ t TEXT, |
++ n INTEGER |
++ ); |
++ } |
++ execsql {SELECT t, n FROM leaf_recover ORDER BY rowid} |
++} {{Leaf-node-generating line 0} 0 {Leaf-node-generating line 1} 1 {Leaf-node-generating line 2} 2 {Leaf-node-generating line 3} 3 {Leaf-node-generating line 4} 4 {Leaf-node-generating line 5} 5 {Leaf-node-generating line 6} 6 {Leaf-node-generating line 7} 7 {Leaf-node-generating line 8} 8 {Leaf-node-generating line 9} 9} |
++ |
++# Single level of interior node. |
++do_test recover-interior-1.0 { |
++ db close |
++ sqlite3 db test.db |
++ generate "interior" "Interior-node-generating line " 100 |
++ |
++ db eval { |
++ DROP TABLE IF EXISTS temp.interior_recover; |
++ CREATE VIRTUAL TABLE temp.interior_recover USING recover( |
++ interior, |
++ t TEXT, |
++ n INTEGER |
++ ); |
++ } |
++ execsql {SELECT t, n FROM interior_recover WHERE (rowid%10)=0 ORDER BY rowid} |
++} {{Interior-node-generating line 9} 9 {Interior-node-generating line 19} 19 {Interior-node-generating line 29} 29 {Interior-node-generating line 39} 39 {Interior-node-generating line 49} 49 {Interior-node-generating line 59} 59 {Interior-node-generating line 69} 69 {Interior-node-generating line 79} 79 {Interior-node-generating line 89} 89 {Interior-node-generating line 99} 99} |
++ |
++# Multiple levels of interior node. |
++do_test recover-interior-2.0 { |
++ db close |
++ sqlite3 db test.db |
++ generate "interior2" "Interior-node-generating line " 5000 |
++ |
++ db eval { |
++ DROP TABLE IF EXISTS temp.interior2_recover; |
++ CREATE VIRTUAL TABLE temp.interior2_recover USING recover( |
++ interior2, |
++ t TEXT, |
++ n INTEGER |
++ ); |
++ } |
++ execsql {SELECT t, n FROM interior2_recover WHERE (rowid%500)=0 ORDER BY rowid} |
++} {{Interior-node-generating line 499} 499 {Interior-node-generating line 999} 999 {Interior-node-generating line 1499} 1499 {Interior-node-generating line 1999} 1999 {Interior-node-generating line 2499} 2499 {Interior-node-generating line 2999} 2999 {Interior-node-generating line 3499} 3499 {Interior-node-generating line 3999} 3999 {Interior-node-generating line 4499} 4499 {Interior-node-generating line 4999} 4999} |
++ |
++finish_test |
+diff --git a/third_party/sqlite/src/test/recover0.test b/third_party/sqlite/src/test/recover0.test |
+new file mode 100644 |
+index 0000000..aac2ed9 |
+--- /dev/null |
++++ b/third_party/sqlite/src/test/recover0.test |
+@@ -0,0 +1,532 @@ |
++# 2012 January 4 {} |
++# |
++# 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. |
++# |
++#*********************************************************************** |
++# This file implements regression tests for SQLite library. |
++# |
++# Test recover module syntax. |
++# |
++# $Id$ |
++ |
++# TODO(shess): Test with attached databases. |
++ |
++# TODO(shess): Handle column mismatches? As things stand, the code |
++# only needs to pull the root page, so that may not be completely |
++# feasible. |
++ |
++set testdir [file dirname $argv0] |
++source $testdir/tester.tcl |
++ |
++db eval { |
++ DROP TABLE IF EXISTS backing; |
++ CREATE TABLE backing (t TEXT); |
++ |
++ DROP TABLE IF EXISTS backing2; |
++ CREATE TABLE backing2 (id INTEGER PRIMARY KEY, t TEXT); |
++} |
++ |
++# Baseline create works. |
++do_test recover-syntax-0.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t TEXT |
++ ); |
++ } |
++} {0 {}} |
++ |
++# Can specify database. |
++do_test recover-syntax-0.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ main.backing, |
++ t TEXT |
++ ); |
++ } |
++} {0 {}} |
++ |
++# Can specify sqlite_master. |
++do_test recover-syntax-0.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ sqlite_master, |
++ type TEXT, |
++ name TEXT, |
++ tbl_name TEXT, |
++ rootpage INTEGER, |
++ sql TEXT |
++ ); |
++ } |
++} {0 {}} |
++ |
++# Fails if virtual table is not in the temp database. |
++do_test recover-syntax-1.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE syntax USING recover( |
++ backing, |
++ t TEXT |
++ ); |
++ } |
++} {1 {recover table must be in temp database}} |
++ |
++# Fails if mentions missing table. |
++do_test recover-syntax-2.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ snacking, |
++ t TEXT |
++ ); |
++ } |
++} {1 {unable to find backing table}} |
++ |
++# Fails if mentions missing database. |
++do_test recover-syntax-2.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ db.backing, |
++ t TEXT |
++ ); |
++ } |
++} {1 {unable to find backing table}} |
++ |
++# Fails if mentions garbage backing. |
++do_test recover-syntax-2.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ main.backing excess, |
++ t TEXT |
++ ); |
++ } |
++} {1 {unable to find backing table}} |
++ |
++# Database only fails. |
++do_test recover-syntax-2.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ main., |
++ t TEXT |
++ ); |
++ } |
++} {1 {ill-formed table specifier}} |
++ |
++# Table only fails. |
++do_test recover-syntax-2.4 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax;} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ .backing, |
++ t TEXT |
++ ); |
++ } |
++} {1 {ill-formed table specifier}} |
++ |
++# Manifest typing. |
++do_test recover-syntax-3.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t {} 0 {} 0} |
++ |
++# ANY as an alternative for manifest typing. |
++do_test recover-syntax-3.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t ANY |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t {} 0 {} 0} |
++ |
++# ANY NOT NULL |
++do_test recover-syntax-3.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t ANY NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t {} 1 {} 0} |
++ |
++# ANY STRICT is not sensible. |
++do_test recover-syntax-3.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v ANY STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++# TEXT column by type works. |
++do_test recover-syntax-4.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t TEXT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t TEXT 0 {} 0} |
++ |
++# TEXT NOT NULL |
++do_test recover-syntax-4.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t TEXT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t TEXT 1 {} 0} |
++ |
++# TEXT STRICT |
++do_test recover-syntax-4.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t TEXT STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t TEXT 0 {} 0} |
++ |
++# TEXT STRICT NOT NULL |
++do_test recover-syntax-4.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ t TEXT STRICT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 t TEXT 1 {} 0} |
++ |
++# INTEGER |
++do_test recover-syntax-5.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ i INTEGER |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 i INTEGER 0 {} 0} |
++ |
++# INTEGER NOT NULL |
++do_test recover-syntax-5.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ i INTEGER NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 i INTEGER 1 {} 0} |
++ |
++# INTEGER STRICT |
++do_test recover-syntax-5.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ i INTEGER STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 i INTEGER 0 {} 0} |
++ |
++# INTEGER STRICT NOT NULL |
++do_test recover-syntax-5.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ i INTEGER STRICT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 i INTEGER 1 {} 0} |
++ |
++# BLOB |
++do_test recover-syntax-6.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ b BLOB |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 b BLOB 0 {} 0} |
++ |
++# BLOB NOT NULL |
++do_test recover-syntax-6.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ b BLOB NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 b BLOB 1 {} 0} |
++ |
++# BLOB STRICT |
++do_test recover-syntax-6.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ b BLOB STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 b BLOB 0 {} 0} |
++ |
++# BLOB STRICT NOT NULL |
++do_test recover-syntax-6.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ b BLOB STRICT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 b BLOB 1 {} 0} |
++ |
++# FLOAT |
++do_test recover-syntax-7.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f FLOAT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f FLOAT 0 {} 0} |
++ |
++# FLOAT NOT NULL |
++do_test recover-syntax-7.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f FLOAT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f FLOAT 1 {} 0} |
++ |
++# FLOAT STRICT |
++do_test recover-syntax-7.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f FLOAT STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f FLOAT 0 {} 0} |
++ |
++# FLOAT STRICT NOT NULL |
++do_test recover-syntax-7.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f FLOAT STRICT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f FLOAT 1 {} 0} |
++ |
++# NUMERIC |
++do_test recover-syntax-8.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f NUMERIC |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f NUMERIC 0 {} 0} |
++ |
++# NUMERIC NOT NULL |
++do_test recover-syntax-8.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f NUMERIC NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f NUMERIC 1 {} 0} |
++ |
++# NUMERIC STRICT |
++do_test recover-syntax-8.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f NUMERIC STRICT |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f NUMERIC 0 {} 0} |
++ |
++# NUMERIC STRICT NOT NULL |
++do_test recover-syntax-8.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ f NUMERIC STRICT NOT NULL |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 f NUMERIC 1 {} 0} |
++ |
++# ROWID |
++do_test recover-syntax-9.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing2, |
++ id ROWID, |
++ v |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 id INTEGER 1 {} 0 1 v {} 0 {} 0} |
++ |
++# ROWID NOT NULL (is default) |
++do_test recover-syntax-9.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing2, |
++ id ROWID NOT NULL, |
++ v |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 id INTEGER 1 {} 0 1 v {} 0 {} 0} |
++ |
++# ROWID STRICT |
++do_test recover-syntax-9.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing2, |
++ id ROWID STRICT, |
++ v |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 id INTEGER 1 {} 0 1 v {} 0 {} 0} |
++ |
++# ROWID STRICT NOT NULL (is default) |
++do_test recover-syntax-9.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ execsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing2, |
++ id ROWID STRICT NOT NULL, |
++ v |
++ ); |
++ PRAGMA table_info(syntax); |
++ } |
++} {0 id INTEGER 1 {} 0 1 v {} 0 {} 0} |
++ |
++# Invalid type info is not ignored. |
++do_test recover-syntax-10.0 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v GARBAGE |
++ ); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++# Extraneous type info is not ignored. |
++do_test recover-syntax-10.1 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v INTEGER GARBAGE |
++ ); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++# Extraneous type info is not ignored. |
++do_test recover-syntax-10.2 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v INTEGER NOT NULL GARBAGE |
++ ); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++# Multiple types don't work. |
++do_test recover-syntax-10.3 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v INTEGER FLOAT BLOB |
++ ); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++# Multiple types don't work. |
++do_test recover-syntax-10.4 { |
++ db eval {DROP TABLE IF EXISTS temp.syntax} |
++ catchsql { |
++ CREATE VIRTUAL TABLE temp.syntax USING recover( |
++ backing, |
++ v INTEGER NOT NULL TEXT |
++ ); |
++ } |
++} {1 {unable to parse column 0}} |
++ |
++finish_test |
+diff --git a/third_party/sqlite/src/test/recover1.test b/third_party/sqlite/src/test/recover1.test |
+new file mode 100644 |
+index 0000000..1d90f09 |
+--- /dev/null |
++++ b/third_party/sqlite/src/test/recover1.test |
+@@ -0,0 +1,429 @@ |
++# 2012 January 4 {} |
++# |
++# 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. |
++# |
++#*********************************************************************** |
++# This file implements regression tests for SQLite library. |
++# |
++# Use tables to test leaf-node reading, and also type checking. |
++# |
++# $Id$ |
++ |
++set testdir [file dirname $argv0] |
++source $testdir/tester.tcl |
++ |
++# A really basic table with manifest typing and a row of each type. |
++db close |
++sqlite3 db test.db |
++db eval { |
++ DROP TABLE IF EXISTS types; |
++ CREATE TABLE types (rowtype TEXT, value); |
++ INSERT INTO types VALUES ("NULL", NULL); |
++ INSERT INTO types VALUES ("INTEGER", 17); |
++ INSERT INTO types VALUES ("FLOAT", 3.1415927); |
++ INSERT INTO types VALUES ("TEXT", "This is text"); |
++ INSERT INTO types VALUES ("BLOB", CAST("This is a blob" AS BLOB)); |
++ |
++ -- Same contents, with an alias for rowid. Testing separately |
++ -- because it changes the structure of the data (the alias column is |
++ -- serialized as NULL). |
++ DROP TABLE IF EXISTS types2; |
++ CREATE TABLE types2 (id INTEGER PRIMARY KEY, rowtype TEXT, value); |
++ INSERT INTO types2 (id, rowtype, value) |
++ SELECT rowid, rowtype, value FROM types; |
++} |
++ |
++# Baseline results. |
++do_test recover-types-0.0 { |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types} |
++} {1 NULL {} null 2 INTEGER 17 integer 3 FLOAT 3.1415927 real 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# With no restrictions, recover table shows identical results. |
++do_test recover-types-0.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 2 INTEGER 17 integer 3 FLOAT 3.1415927 real 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Restrict by INTEGER |
++do_test recover-types-1.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value INTEGER |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 2 INTEGER 17 integer} |
++ |
++# Restrict by INTEGER NOT NULL |
++do_test recover-types-1.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value INTEGER NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {2 INTEGER 17 integer} |
++ |
++# Restrict by FLOAT |
++do_test recover-types-2.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value FLOAT |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 2 INTEGER 17.0 real 3 FLOAT 3.1415927 real} |
++ |
++# Restrict by FLOAT NOT NULL |
++do_test recover-types-2.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value FLOAT NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {2 INTEGER 17.0 real 3 FLOAT 3.1415927 real} |
++ |
++# Restrict by FLOAT STRICT |
++do_test recover-types-2.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value FLOAT STRICT |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 3 FLOAT 3.1415927 real} |
++ |
++# Restrict by FLOAT STRICT NOT NULL |
++do_test recover-types-2.3 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value FLOAT STRICT NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {3 FLOAT 3.1415927 real} |
++ |
++# Restrict by TEXT |
++do_test recover-types-3.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value TEXT |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Restrict by TEXT NOT NULL |
++do_test recover-types-3.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value TEXT NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Restrict by TEXT STRICT |
++do_test recover-types-3.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value TEXT STRICT |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 4 TEXT {This is text} text} |
++ |
++# Restrict by TEXT STRICT NOT NULL |
++do_test recover-types-3.3 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value TEXT STRICT NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {4 TEXT {This is text} text} |
++ |
++# Restrict by BLOB |
++do_test recover-types-4.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value BLOB |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 5 BLOB {This is a blob} blob} |
++ |
++# Restrict by BLOB NOT NULL |
++do_test recover-types-4.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value BLOB NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {5 BLOB {This is a blob} blob} |
++ |
++# Manifest typing. |
++do_test recover-types-5.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 2 INTEGER 17 integer 3 FLOAT 3.1415927 real 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Should get same results specifying manifest typing explicitly. |
++do_test recover-types-5.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value ANY |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {1 NULL {} null 2 INTEGER 17 integer 3 FLOAT 3.1415927 real 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Same results, skipping the NULL row. |
++do_test recover-types-5.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types_recover; |
++ CREATE VIRTUAL TABLE temp.types_recover USING recover( |
++ types, |
++ rowtype TEXT, |
++ value ANY NOT NULL |
++ ); |
++ } |
++ execsql {SELECT rowid, rowtype, value, TYPEOF(value) FROM types_recover} |
++} {2 INTEGER 17 integer 3 FLOAT 3.1415927 real 4 TEXT {This is text} text 5 BLOB {This is a blob} blob} |
++ |
++# Test ROWID values. |
++do_test recover-types-6.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types2_recover; |
++ CREATE VIRTUAL TABLE temp.types2_recover USING recover( |
++ types2, |
++ id ROWID, |
++ rowtype TEXT, |
++ value |
++ ); |
++ } |
++ execsql {SELECT rowid, id, rowtype, value, TYPEOF(value) FROM types2_recover} |
++} {1 1 NULL {} null 2 2 INTEGER 17 integer 3 3 FLOAT 3.1415927 real 4 4 TEXT {This is text} text 5 5 BLOB {This is a blob} blob} |
++ |
++# ROWID NOT NULL is identical. |
++do_test recover-types-6.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS temp.types2_recover; |
++ CREATE VIRTUAL TABLE temp.types2_recover USING recover( |
++ types2, |
++ id ROWID NOT NULL, |
++ rowtype TEXT, |
++ value |
++ ); |
++ } |
++ execsql {SELECT rowid, id, rowtype, value, TYPEOF(value) FROM types2_recover} |
++} {1 1 NULL {} null 2 2 INTEGER 17 integer 3 3 FLOAT 3.1415927 real 4 4 TEXT {This is text} text 5 5 BLOB {This is a blob} blob} |
++ |
++# Check that each of the possible integer sizes is being decoded. |
++# TODO(shess): It would be neat to ACTUALLY test these things. As-is, |
++# this should exercise the code paths, but one needs logging or a |
++# debugger to verify that things are stored as expected. |
++do_test recover-types-7.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS integers; |
++ CREATE TABLE integers (value); |
++ |
++ -- encoded directly in type info. |
++ INSERT INTO integers VALUES (0); |
++ INSERT INTO integers VALUES (1); |
++ |
++ -- 8-bit signed. |
++ INSERT INTO integers VALUES (2); |
++ INSERT INTO integers VALUES (-2); |
++ INSERT INTO integers VALUES (127); |
++ INSERT INTO integers VALUES (-128); |
++ |
++ -- 16-bit signed. |
++ INSERT INTO integers VALUES (12345); |
++ INSERT INTO integers VALUES (-12345); |
++ INSERT INTO integers VALUES (32767); |
++ INSERT INTO integers VALUES (-32768); |
++ |
++ -- 24-bit signed. |
++ INSERT INTO integers VALUES (1234567); |
++ INSERT INTO integers VALUES (-1234567); |
++ INSERT INTO integers VALUES (8388607); |
++ INSERT INTO integers VALUES (-8388608); |
++ |
++ -- 32-bit signed. |
++ INSERT INTO integers VALUES (1234567890); |
++ INSERT INTO integers VALUES (-1234567890); |
++ INSERT INTO integers VALUES (2147483647); |
++ INSERT INTO integers VALUES (-2147483648); |
++ |
++ -- 48-bit signed. |
++ INSERT INTO integers VALUES (123456789012345); |
++ INSERT INTO integers VALUES (-123456789012345); |
++ INSERT INTO integers VALUES (140737488355327); |
++ INSERT INTO integers VALUES (-140737488355328); |
++ |
++ -- 64-bit signed. |
++ INSERT INTO integers VALUES (9223372036854775807); |
++ INSERT INTO integers VALUES (-9223372036854775808); |
++ |
++ DROP TABLE IF EXISTS integers_recover; |
++ CREATE VIRTUAL TABLE temp.integers_recover USING recover( |
++ integers, |
++ value INTEGER |
++ ); |
++ } |
++ execsql {SELECT rowid, value FROM integers_recover} |
++} {1 0 2 1 3 2 4 -2 5 127 6 -128 7 12345 8 -12345 9 32767 10 -32768 11 1234567 12 -1234567 13 8388607 14 -8388608 15 1234567890 16 -1234567890 17 2147483647 18 -2147483648 19 123456789012345 20 -123456789012345 21 140737488355327 22 -140737488355328 23 9223372036854775807 24 -9223372036854775808} |
++ |
++# If UTF16 support is disabled, ignore the rest of the tests. |
++# |
++ifcapable {!utf16} { |
++ finish_test |
++ return |
++} |
++ |
++# Baseline UTF-8. |
++file delete -force test.db |
++sqlite3 db test.db; |
++db eval { |
++ PRAGMA encoding = 'UTF-8'; |
++} |
++ |
++do_test recover-encoding-1.0 { |
++ execsql { |
++ DROP TABLE IF EXISTS e; |
++ CREATE TABLE e (v TEXT); |
++ INSERT INTO e VALUES('Mjollnir'); |
++ INSERT INTO e VALUES('Mjölnir'); |
++ INSERT INTO e VALUES('Mjǫlnir'); |
++ INSERT INTO e VALUES('Mjölner'); |
++ INSERT INTO e VALUES('Mjølner'); |
++ INSERT INTO e VALUES('ハンマー'); |
++ PRAGMA encoding; |
++ |
++ DROP TABLE IF EXISTS e_recover; |
++ CREATE VIRTUAL TABLE temp.e_recover USING recover( |
++ e, |
++ v TEXT |
++ ); |
++ SELECT rowid, v FROM e_recover ORDER BY rowid; |
++ } |
++} {UTF-8 1 Mjollnir 2 Mjölnir 3 Mjǫlnir 4 Mjölner 5 Mjølner 6 ハンマー} |
++ |
++# Reset the database to UTF-16LE. |
++file delete -force test.db |
++sqlite3 db test.db; |
++db eval { |
++ PRAGMA encoding = 'UTF-16LE'; |
++} |
++ |
++do_test recover-encoding-2.0 { |
++ execsql { |
++ DROP TABLE IF EXISTS e; |
++ CREATE TABLE e (v TEXT); |
++ INSERT INTO e VALUES('Mjollnir'); |
++ INSERT INTO e VALUES('Mjölnir'); |
++ INSERT INTO e VALUES('Mjǫlnir'); |
++ INSERT INTO e VALUES('Mjölner'); |
++ INSERT INTO e VALUES('Mjølner'); |
++ INSERT INTO e VALUES('ハンマー'); |
++ PRAGMA encoding; |
++ |
++ DROP TABLE IF EXISTS e_recover; |
++ CREATE VIRTUAL TABLE temp.e_recover USING recover( |
++ e, |
++ v TEXT |
++ ); |
++ SELECT rowid, v FROM e_recover ORDER BY rowid; |
++ } |
++} {UTF-16le 1 Mjollnir 2 Mjölnir 3 Mjǫlnir 4 Mjölner 5 Mjølner 6 ハンマー} |
++ |
++# Reset the database to UTF-16BE. |
++file delete -force test.db |
++sqlite3 db test.db; |
++db eval { |
++ PRAGMA encoding = 'UTF-16BE'; |
++} |
++ |
++do_test recover-encoding-3.0 { |
++ execsql { |
++ DROP TABLE IF EXISTS e; |
++ CREATE TABLE e (v TEXT); |
++ INSERT INTO e VALUES('Mjollnir'); |
++ INSERT INTO e VALUES('Mjölnir'); |
++ INSERT INTO e VALUES('Mjǫlnir'); |
++ INSERT INTO e VALUES('Mjölner'); |
++ INSERT INTO e VALUES('Mjølner'); |
++ INSERT INTO e VALUES('ハンマー'); |
++ PRAGMA encoding; |
++ |
++ DROP TABLE IF EXISTS e_recover; |
++ CREATE VIRTUAL TABLE temp.e_recover USING recover( |
++ e, |
++ v TEXT |
++ ); |
++ SELECT rowid, v FROM e_recover ORDER BY rowid; |
++ } |
++} {UTF-16be 1 Mjollnir 2 Mjölnir 3 Mjǫlnir 4 Mjölner 5 Mjølner 6 ハンマー} |
++ |
++finish_test |
+diff --git a/third_party/sqlite/src/test/recover2.test b/third_party/sqlite/src/test/recover2.test |
+new file mode 100644 |
+index 0000000..8aa4e04 |
+--- /dev/null |
++++ b/third_party/sqlite/src/test/recover2.test |
+@@ -0,0 +1,157 @@ |
++# 2012 January 4 {} |
++# |
++# 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. |
++# |
++#*********************************************************************** |
++# This file implements regression tests for SQLite library. |
++# |
++# This file implements tests for how the recover module handles cell |
++# overflow. |
++# |
++# $Id$ |
++ |
++set testdir [file dirname $argv0] |
++source $testdir/tester.tcl |
++ |
++set lorem "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam sagittis gravida odio vitae ultrices. Nulla facilisi. Maecenas pulvinar, tellus ut bibendum semper, nibh tellus auctor nulla, in dignissim nisi ipsum id arcu. Nullam tincidunt arcu malesuada turpis faucibus in adipiscing enim mattis. Fusce augue magna, scelerisque sollicitudin egestas in, cursus eu sapien. Pellentesque tempor risus at lectus convallis a convallis orci ornare. Integer tristique aliquam leo vel interdum. |
++ |
++Phasellus quis dictum nisi. Curabitur at enim non felis pharetra imperdiet. Duis tempus massa eu leo varius porta. Vestibulum sodales nulla at purus tincidunt ultrices. Nam euismod posuere nibh, nec sodales magna luctus ac. Ut commodo hendrerit mauris vitae gravida. In interdum justo ut sem eleifend convallis. Donec cursus molestie elementum. Suspendisse at nisl tellus, vel consequat mauris. Nullam non justo nibh. |
++ |
++Maecenas varius sollicitudin libero, nec feugiat turpis facilisis eget. Quisque et sem risus. Aenean a magna quis purus hendrerit mattis eu vel lorem. Aenean fringilla diam eget tortor lacinia sed mollis eros feugiat. Quisque ac purus sapien. Nullam quis tellus vel magna convallis tincidunt. Donec eget ligula at libero tincidunt congue ut ut lacus. Integer dignissim aliquet congue. Pellentesque sed risus vitae lorem porta viverra ac eu risus. Vivamus congue suscipit odio pulvinar aliquet. Aliquam porttitor nunc non sapien auctor et vehicula augue molestie. |
++ |
++Aliquam et dui ac sem tempus dictum. Fusce arcu nulla, viverra sit amet suscipit quis, malesuada at felis. Fusce ut diam felis. Fusce id ligula non eros fermentum sodales in nec quam. Donec tempor bibendum arcu ac adipiscing. Praesent nisl lectus, tempor ut vehicula eget, mattis a justo. Mauris condimentum luctus eros a varius. Morbi mollis elit eget velit convallis eu sodales odio egestas. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus interdum, metus sit amet varius varius, lectus eros semper risus, sed sagittis ipsum libero in sapien. Nam lacinia nulla vitae magna facilisis scelerisque. Lorem ipsum dolor sit amet, consectetur adipiscing elit. |
++ |
++Donec gravida dignissim eleifend. Aliquam vel tincidunt tortor. Curabitur massa ante, blandit a auctor at, ullamcorper sed nisl. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse ut felis a eros egestas ultricies et quis mi. Vivamus ut risus massa. Donec nec ornare erat. Aliquam ornare, lorem a rhoncus aliquam, tellus diam tincidunt tellus, a mattis nunc ante ac arcu. Curabitur nec metus id risus commodo ullamcorper eu ut tortor." |
++ |
++# Create a database which needs a multiple overflow pages to test the |
++# transition from main page to overflow, and then overflow to |
++# overflow. |
++do_test recover-overflow-1.0 { |
++ db eval { |
++ DROP TABLE IF EXISTS overflow; |
++ CREATE TABLE overflow (value TEXT); |
++ INSERT INTO overflow VALUES ($lorem); |
++ |
++ DROP TABLE IF EXISTS overflow_recover; |
++ CREATE VIRTUAL TABLE temp.overflow_recover USING recover( |
++ overflow, |
++ value TEXT |
++ ); |
++ } |
++ |
++ # Should have root page, leaf page, and 2 overflow pages, because |
++ # length(value) is more than 2x page_size. |
++ execsql { |
++ PRAGMA page_count; |
++ PRAGMA page_size; |
++ SELECT rowid, TYPEOF(value), length(value), value FROM overflow_recover; |
++ } |
++} [list 4 1024 1 text [string length $lorem] $lorem] |
++ |
++# No overflow. [1024-35 == 990, overhead of 1-byte rowid, 2-byte |
++# record length, 1-byte header length, 2-byte field type.] |
++set substr [string range $lorem 0 985] |
++do_test recover-overflow-1.1 { |
++ db eval { |
++ DROP TABLE IF EXISTS overflow; |
++ CREATE TABLE overflow (value TEXT); |
++ INSERT INTO overflow VALUES ($substr); |
++ |
++ DROP TABLE IF EXISTS overflow_recover; |
++ CREATE VIRTUAL TABLE temp.overflow_recover USING recover( |
++ overflow, |
++ value TEXT |
++ ); |
++ } |
++ |
++ # Trim to remove excess pages from prior tests. |
++ db eval {VACUUM} |
++ |
++ execsql { |
++ PRAGMA page_count; |
++ PRAGMA page_size; |
++ SELECT rowid, TYPEOF(value), length(value), value FROM overflow_recover; |
++ } |
++} [list 2 1024 1 text [string length $substr] $substr] |
++ |
++# One byte of overflow. |
++set substr [string range $lorem 0 986] |
++do_test recover-overflow-1.2 { |
++ db eval { |
++ DROP TABLE IF EXISTS overflow; |
++ CREATE TABLE overflow (value TEXT); |
++ INSERT INTO overflow VALUES ($substr); |
++ |
++ DROP TABLE IF EXISTS overflow_recover; |
++ CREATE VIRTUAL TABLE temp.overflow_recover USING recover( |
++ overflow, |
++ value TEXT |
++ ); |
++ } |
++ |
++ # Trim to remove excess pages from prior tests. |
++ db eval {VACUUM} |
++ |
++ execsql { |
++ PRAGMA page_count; |
++ PRAGMA page_size; |
++ SELECT rowid, TYPEOF(value), length(value), value FROM overflow_recover; |
++ } |
++} [list 3 1024 1 text [string length $substr] $substr] |
++ |
++# One full overflow page, plus maxLocal in-leaf. [985+1020] |
++set substr [string range $lorem 0 2005] |
++do_test recover-overflow-1.3 { |
++ db eval { |
++ DROP TABLE IF EXISTS overflow; |
++ CREATE TABLE overflow (value TEXT); |
++ INSERT INTO overflow VALUES ($substr); |
++ |
++ DROP TABLE IF EXISTS overflow_recover; |
++ CREATE VIRTUAL TABLE temp.overflow_recover USING recover( |
++ overflow, |
++ value TEXT |
++ ); |
++ } |
++ |
++ # Trim to remove excess pages from prior tests. |
++ db eval {VACUUM} |
++ |
++ execsql { |
++ PRAGMA page_count; |
++ PRAGMA page_size; |
++ SELECT rowid, TYPEOF(value), length(value), value FROM overflow_recover; |
++ } |
++} [list 3 1024 1 text [string length $substr] $substr] |
++ |
++# Overflow to a second overflow page. |
++set substr [string range $lorem 0 2006] |
++do_test recover-overflow-1.4 { |
++ db eval { |
++ DROP TABLE IF EXISTS overflow; |
++ CREATE TABLE overflow (value TEXT); |
++ INSERT INTO overflow VALUES ($substr); |
++ |
++ DROP TABLE IF EXISTS overflow_recover; |
++ CREATE VIRTUAL TABLE temp.overflow_recover USING recover( |
++ overflow, |
++ value TEXT |
++ ); |
++ } |
++ |
++ # Trim to remove excess pages from prior tests. |
++ db eval {VACUUM} |
++ |
++ execsql { |
++ PRAGMA page_count; |
++ PRAGMA page_size; |
++ SELECT rowid, TYPEOF(value), length(value), value FROM overflow_recover; |
++ } |
++} [list 4 1024 1 text [string length $substr] $substr] |
++ |
++finish_test |
+diff --git a/third_party/sqlite/src/tool/mksqlite3c.tcl b/third_party/sqlite/src/tool/mksqlite3c.tcl |
+index 0e97923..9e2b0fe 100644 |
+--- a/third_party/sqlite/src/tool/mksqlite3c.tcl |
++++ b/third_party/sqlite/src/tool/mksqlite3c.tcl |
+@@ -316,6 +316,8 @@ foreach file { |
+ main.c |
+ notify.c |
+ |
++ recover.c |
++ |
+ fts3.c |
+ fts3_aux.c |
+ fts3_expr.c |
+-- |
+2.2.1 |
+ |