/ Check-in [7654ae71]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Remove some assert() statements that can fail with corrupt databases.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7654ae71bd26ae5d713935624d2b6cb8d2e4633f
User & Date: dan 2012-01-12 14:25:55
Context
2012-01-12
15:05
Fix typos in a testcase() macro introduced by the previous commit. check-in: 0467742f user: dan tags: trunk
14:25
Remove some assert() statements that can fail with corrupt databases. check-in: 7654ae71 user: dan tags: trunk
2012-01-11
16:16
Remove code made unreachable by the enhancement of the previous check-in. check-in: 9e31a275 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

  4190   4190     pCur->validNKey = 0;
  4191   4191     if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
  4192   4192       return SQLITE_CORRUPT_BKPT;
  4193   4193     }
  4194   4194     return SQLITE_OK;
  4195   4195   }
  4196   4196   
  4197         -#ifndef NDEBUG
         4197  +#if 0
  4198   4198   /*
  4199   4199   ** Page pParent is an internal (non-leaf) tree page. This function 
  4200   4200   ** asserts that page number iChild is the left-child if the iIdx'th
  4201   4201   ** cell in page pParent. Or, if iIdx is equal to the total number of
  4202   4202   ** cells in pParent, that page number iChild is the right-child of
  4203   4203   ** the page.
  4204   4204   */
................................................................................
  4223   4223   ** the largest cell index.
  4224   4224   */
  4225   4225   static void moveToParent(BtCursor *pCur){
  4226   4226     assert( cursorHoldsMutex(pCur) );
  4227   4227     assert( pCur->eState==CURSOR_VALID );
  4228   4228     assert( pCur->iPage>0 );
  4229   4229     assert( pCur->apPage[pCur->iPage] );
         4230  +
         4231  +  /* UPDATE: It is actually possible for the condition tested by the assert
         4232  +  ** below to be untrue if the database file is corrupt. This can occur if
         4233  +  ** one cursor has modified page pParent while a reference to it is held 
         4234  +  ** by a second cursor. Which can only happen if a single page is linked
         4235  +  ** into more than one b-tree structure in a corrupt database.  */
         4236  +#if 0
  4230   4237     assertParentIndex(
  4231   4238       pCur->apPage[pCur->iPage-1], 
  4232   4239       pCur->aiIdx[pCur->iPage-1], 
  4233   4240       pCur->apPage[pCur->iPage]->pgno
  4234   4241     );
         4242  +#endif
         4243  +  testcase( iIdx>pParent->nCell );
         4244  +
  4235   4245     releasePage(pCur->apPage[pCur->iPage]);
  4236   4246     pCur->iPage--;
  4237   4247     pCur->info.nSize = 0;
  4238   4248     pCur->validNKey = 0;
  4239   4249   }
  4240   4250   
  4241   4251   /*
................................................................................
  4697   4707       return SQLITE_OK;
  4698   4708     }
  4699   4709     pCur->skipNext = 0;
  4700   4710   
  4701   4711     pPage = pCur->apPage[pCur->iPage];
  4702   4712     idx = ++pCur->aiIdx[pCur->iPage];
  4703   4713     assert( pPage->isInit );
  4704         -  assert( idx<=pPage->nCell );
         4714  +
         4715  +  /* If the database file is corrupt, it is possible for the value of idx 
         4716  +  ** to be invalid here. This can only occur if a second cursor modifies
         4717  +  ** the page while cursor pCur is holding a reference to it. Which can
         4718  +  ** only happen if the database is corrupt in such a way as to link the
         4719  +  ** page into more than one b-tree structure. */
         4720  +  testcase( idx>pPage->nCell );
  4705   4721   
  4706   4722     pCur->info.nSize = 0;
  4707   4723     pCur->validNKey = 0;
  4708   4724     if( idx>=pPage->nCell ){
  4709   4725       if( !pPage->leaf ){
  4710   4726         rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
  4711   4727         if( rc ) return rc;

Added test/corruptF.test.

            1  +# 2012 January 12
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +set testprefix corruptF
           16  +
           17  +# Do not use a codec for tests in this file, as the database file is
           18  +# manipulated directly using tcl scripts (using the [hexio_write] command).
           19  +#
           20  +do_not_use_codec
           21  +
           22  +proc str {i} { format %08d $i }
           23  +
           24  +# Create a 6 page database containing a single table - t1. Table t1 
           25  +# consists of page 2 (the root page) and pages 5 and 6 (leaf pages). 
           26  +# Database pages 3 and 4 are on the free list.
           27  +#
           28  +proc create_test_db {} {
           29  +  catch { db close }
           30  +  forcedelete test.db
           31  +  sqlite3 db test.db
           32  +  db func str str
           33  +  execsql {
           34  +    PRAGMA auto_vacuum = 0;
           35  +    PRAGMA page_size = 1024;
           36  +    CREATE TABLE t1(x);         /* root page = 2 */
           37  +    CREATE TABLE t2(x);         /* root page = 3 */
           38  +    CREATE TABLE t3(x);         /* root page = 4 */
           39  +
           40  +    INSERT INTO t1 VALUES(str(1));
           41  +    INSERT INTO t1 SELECT str(rowid+1) FROM t1;
           42  +    INSERT INTO t1 SELECT str(rowid+2) FROM t1;
           43  +    INSERT INTO t1 SELECT str(rowid+4) FROM t1;
           44  +    INSERT INTO t1 SELECT str(rowid+8) FROM t1;
           45  +    INSERT INTO t1 SELECT str(rowid+16) FROM t1;
           46  +    INSERT INTO t1 SELECT str(rowid+32) FROM t1;
           47  +    INSERT INTO t1 SELECT str(rowid+64) FROM t1;
           48  +    DROP TABLE t2;
           49  +    DROP TABLE t3;
           50  +  }
           51  +  db close
           52  +}
           53  +
           54  +do_test 1.1 { create_test_db } {}
           55  +
           56  +# Check the db is as we expect. 6 pages in total, with 3 and 4 on the free
           57  +# list. Page 3 is the free list trunk and page 4 is a leaf.
           58  +#
           59  +do_test 1.2 { file size test.db } [expr 6*1024]
           60  +do_test 1.3 { hexio_read test.db 32 4 } 00000003
           61  +do_test 1.4 { hexio_read test.db [expr 2*1024] 12 } 000000000000000100000004
           62  +
           63  +# Change the free-list entry to page 6 and reopen the db file.
           64  +do_test 1.5 { 
           65  +  hexio_write test.db [expr 2*1024 + 8] 00000006 
           66  +  sqlite3 db test.db
           67  +} {}
           68  +
           69  +# Now create a new table in the database file. The root of the new table
           70  +# is page 6, which is also the right-most leaf page in table t1.
           71  +#
           72  +do_execsql_test 1.6 { 
           73  +  CREATE TABLE t4(x);
           74  +  SELECT * FROM sqlite_master;
           75  +} {
           76  +  table t1 t1 2 {CREATE TABLE t1(x)} 
           77  +  table t4 t4 6 {CREATE TABLE t4(x)}
           78  +}
           79  +
           80  +# At one point this was causing an assert to fail.
           81  +#
           82  +# This statement opens a cursor on table t1 and does a full table scan. As
           83  +# each row is visited, it is copied into table t4. There is no temporary
           84  +# table.
           85  +#
           86  +# When the t1 cursor reaches page 6 (which is both the right-most leaf of
           87  +# t1 and the root of t4), it continues to iterate through the keys within
           88  +# it (which at this point are keys that have been inserted into t4). And
           89  +# for each row visited, another row is inserted into page 6 - it being the
           90  +# root page of t4. Eventually, page 6 becomes full and the height of the
           91  +# b-tree for table t4 increased. From the point of view of the t1 cursor,
           92  +# this unexpectedly reduces the number of keys on page 6 in the middle of
           93  +# its iteration, which causes an assert() to fail.
           94  +#
           95  +db_save_and_close
           96  +if 1 {
           97  +for {set i 0} {$i < 128} {incr i} {
           98  +  db_restore_and_reopen
           99  +  do_test 1.7.$i { 
          100  +    set res [
          101  +      catchsql { INSERT INTO t4 SELECT x FROM t1 WHERE rowid>$i }
          102  +    ]
          103  +    if {$res == "0 {}" || $res == "1 {database disk image is malformed}"} {
          104  +      set res ""
          105  +    }
          106  +    set res
          107  +  } {}
          108  +}
          109  +}
          110  +
          111  +do_test 2.1 { create_test_db } {}
          112  +do_test 2.2 { file size test.db } [expr 6*1024]
          113  +do_test 2.3 { hexio_read test.db 32 4 } 00000003
          114  +do_test 2.4 { hexio_read test.db [expr 2*1024] 12 } 000000000000000100000004
          115  +
          116  +# Change the free-list entry to page 5 and reopen the db file.
          117  +do_test 2.5 { 
          118  +  hexio_write test.db [expr 2*1024 + 8] 00000005 
          119  +  sqlite3 db test.db
          120  +} {}
          121  +
          122  +# Now create a new table in the database file. The root of the new table
          123  +# is page 5, which is also the right-most leaf page in table t1.
          124  +#
          125  +do_execsql_test 2.6 { 
          126  +  CREATE TABLE t4(x);
          127  +  SELECT * FROM sqlite_master;
          128  +} {
          129  +  table t1 t1 2 {CREATE TABLE t1(x)} 
          130  +  table t4 t4 5 {CREATE TABLE t4(x)}
          131  +}
          132  +
          133  +db_save_and_close
          134  +for {set i 127} {$i >= 0} {incr i -1} {
          135  +  db_restore_and_reopen
          136  +  do_test 2.7.$i { 
          137  +    set res [
          138  +      catchsql { 
          139  +        INSERT INTO t4 SELECT x FROM t1 WHERE rowid<$i ORDER BY rowid DESC 
          140  +      }
          141  +    ]
          142  +    if {$res == "0 {}" || $res == "1 {database disk image is malformed}"} {
          143  +      set res ""
          144  +    }
          145  +    set res
          146  +  } {}
          147  +}
          148  +
          149  +finish_test
          150  +