/ Check-in [10ea8287]
Login

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

Overview
Comment:Do not write pages to disk to free memory after an IO error occurs. (CVS 5132)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 10ea8287d090ae610416b4754c0838f13b51fd78
User & Date: danielk1977 2008-05-15 08:34:54
Context
2008-05-15
09:07
Fix a test script bug - not all database handles were being closed by ioerr5.test. (CVS 5133) check-in: 47652e7b user: danielk1977 tags: trunk
08:34
Do not write pages to disk to free memory after an IO error occurs. (CVS 5132) check-in: 10ea8287 user: danielk1977 tags: trunk
2008-05-14
16:18
Version 3.5.9 (CVS 5131) check-in: b6129f4c user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.446 2008/05/13 13:27:34 drh Exp $
           21  +** @(#) $Id: pager.c,v 1.447 2008/05/15 08:34:54 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include <assert.h>
    26     26   #include <string.h>
    27     27   
    28     28   /*
................................................................................
  2866   2866   ** This routine clears the needSync field of every page current held in
  2867   2867   ** memory.
  2868   2868   */
  2869   2869   static int syncJournal(Pager *pPager){
  2870   2870     PgHdr *pPg;
  2871   2871     int rc = SQLITE_OK;
  2872   2872   
  2873         -
  2874   2873     /* Sync the journal before modifying the main database
  2875   2874     ** (assuming there is a journal and it needs to be synced.)
  2876   2875     */
  2877   2876     if( pPager->needSync ){
  2878   2877       if( !pPager->tempFile ){
  2879   2878         int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
  2880   2879         assert( pPager->journalOpen );
................................................................................
  3178   3177     pPg = pPager->lru.pFirstSynced;
  3179   3178   
  3180   3179     /* If we could not find a page that does not require an fsync()
  3181   3180     ** on the journal file then fsync the journal file.  This is a
  3182   3181     ** very slow operation, so we work hard to avoid it.  But sometimes
  3183   3182     ** it can't be helped.
  3184   3183     */
  3185         -  if( pPg==0 && pPager->lru.pFirst){
  3186         -    int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
  3187         -    int rc = syncJournal(pPager);
  3188         -    if( rc!=0 ){
  3189         -      return rc;
  3190         -    }
  3191         -    if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
  3192         -      /* If in full-sync mode, write a new journal header into the
  3193         -      ** journal file. This is done to avoid ever modifying a journal
  3194         -      ** header that is involved in the rollback of pages that have
  3195         -      ** already been written to the database (in case the header is
  3196         -      ** trashed when the nRec field is updated).
  3197         -      */
  3198         -      pPager->nRec = 0;
  3199         -      assert( pPager->journalOff > 0 );
  3200         -      assert( pPager->doNotSync==0 );
  3201         -      rc = writeJournalHdr(pPager);
         3184  +  if( pPg==0 && pPager->lru.pFirst ){
         3185  +    if( !pPager->errCode ){
         3186  +      int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
         3187  +      int rc = syncJournal(pPager);
  3202   3188         if( rc!=0 ){
  3203   3189           return rc;
         3190  +      }
         3191  +      if( pPager->fullSync && 0==(iDc&SQLITE_IOCAP_SAFE_APPEND) ){
         3192  +        /* If in full-sync mode, write a new journal header into the
         3193  +        ** journal file. This is done to avoid ever modifying a journal
         3194  +        ** header that is involved in the rollback of pages that have
         3195  +        ** already been written to the database (in case the header is
         3196  +        ** trashed when the nRec field is updated).
         3197  +        */
         3198  +        pPager->nRec = 0;
         3199  +        assert( pPager->journalOff > 0 );
         3200  +        assert( pPager->doNotSync==0 );
         3201  +        rc = writeJournalHdr(pPager);
         3202  +        if( rc!=0 ){
         3203  +          return rc;
         3204  +        }
  3204   3205         }
  3205   3206       }
  3206   3207       pPg = pPager->lru.pFirst;
  3207   3208     }
  3208   3209   
  3209   3210     assert( pPg->nRef==0 );
  3210   3211   
  3211   3212     /* Write the page to the database file if it is dirty.
  3212   3213     */
  3213         -  if( pPg->dirty ){
         3214  +  if( pPg->dirty && !pPager->errCode ){
  3214   3215       int rc;
  3215   3216       assert( pPg->needSync==0 );
  3216   3217       makeClean(pPg);
  3217   3218       pPg->dirty = 1;
  3218   3219       pPg->pDirty = 0;
  3219   3220       rc = pager_write_pagelist( pPg );
  3220   3221       pPg->dirty = 0;
  3221   3222       if( rc!=SQLITE_OK ){
  3222   3223         return rc;
  3223   3224       }
  3224   3225     }
  3225         -  assert( pPg->dirty==0 );
         3226  +  assert( pPg->dirty==0 || pPager->errCode );
  3226   3227   
  3227   3228     /* If the page we are recycling is marked as alwaysRollback, then
  3228   3229     ** set the global alwaysRollback flag, thus disabling the
  3229   3230     ** sqlite3PagerDontRollback() optimization for the rest of this transaction.
  3230   3231     ** It is necessary to do this because the page marked alwaysRollback
  3231   3232     ** might be reloaded at a later time but at that point we won't remember
  3232   3233     ** that is was marked alwaysRollback.  This means that all pages must

Added test/ioerr5.test.

            1  +# 2008 May 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  +# This file tests that if sqlite3_release_memory() is called to reclaim
           13  +# memory from a pager that is in the error-state, SQLite does not 
           14  +# incorrectly write dirty pages out to the database (not safe to do
           15  +# once the pager is in error state).
           16  +#
           17  +# $Id: ioerr5.test,v 1.1 2008/05/15 08:34:54 danielk1977 Exp $
           18  +
           19  +set testdir [file dirname $argv0]
           20  +source $testdir/tester.tcl
           21  +
           22  +ifcapable !memorymanage||!shared_cache {
           23  +  finish_test
           24  +  return
           25  +}
           26  +
           27  +db close
           28  +
           29  +set ::enable_shared_cache [sqlite3_enable_shared_cache 1]
           30  +set ::soft_limit [sqlite3_soft_heap_limit 1048576]
           31  +
           32  +# This procedure prepares, steps and finalizes an SQL statement via the
           33  +# UTF-16 APIs. The text representation of an SQLite error code is returned
           34  +# ("SQLITE_OK", "SQLITE_IOERR" etc.). The actual results returned by the
           35  +# SQL statement, if it is a SELECT, are not available.
           36  +#
           37  +# This can be useful for testing because it forces SQLite to make an extra 
           38  +# call to sqlite3_malloc() when translating from the supplied UTF-16 to
           39  +# the UTF-8 encoding used internally.
           40  +#
           41  +proc dosql16 {zSql {db db}} {
           42  +  set sql [encoding convertto unicode $zSql]
           43  +  append sql "\00\00"
           44  +  set stmt [sqlite3_prepare16 $db $sql -1 {}]
           45  +  sqlite3_step $stmt
           46  +  set rc [sqlite3_finalize $stmt]
           47  +}
           48  +
           49  +proc compilesql16 {zSql {db db}} {
           50  +  set sql [encoding convertto unicode $zSql]
           51  +  append sql "\00\00"
           52  +  set stmt [sqlite3_prepare16 $db $sql -1 {}]
           53  +  set rc [sqlite3_finalize $stmt]
           54  +}
           55  +
           56  +# Open two database connections (handle db and db2) to database "test.db".
           57  +#
           58  +proc opendatabases {} {
           59  +  catch {db close}
           60  +  catch {db2 close}
           61  +  sqlite3 db test.db
           62  +  sqlite3 db2 test.db
           63  +  db2 cache size 0
           64  +  db cache size 0
           65  +  execsql {
           66  +    pragma page_size=512;
           67  +    pragma auto_vacuum=2;
           68  +    pragma cache_size=16;
           69  +  }
           70  +}
           71  +
           72  +# Open two database connections and create a single table in the db.
           73  +#
           74  +do_test ioerr5-1.0 {
           75  +  opendatabases
           76  +  execsql { CREATE TABLE A(Id INTEGER, Name TEXT) }
           77  +} {}
           78  +
           79  +foreach locking_mode {normal exclusive} {
           80  +  for {set iFail 1} {$iFail<200} {incr iFail} {
           81  +    sqlite3_soft_heap_limit 1048576
           82  +    opendatabases
           83  +    execsql { pragma locking_mode=exclusive }
           84  +    set nRow [db one {SELECT count(*) FROM a}]
           85  +  
           86  +    # Dirty (at least) one of the pages in the cache.
           87  +    do_test ioerr5-$locking_mode-$iFail.1 {
           88  +      execsql {
           89  +        BEGIN EXCLUSIVE;
           90  +        INSERT INTO a VALUES(1, 'ABCDEFGHIJKLMNOP');
           91  +      }
           92  +    } {}
           93  +  
           94  +    # Now try to commit the transaction. Cause an IO error to occur
           95  +    # within this operation, which moves the pager into the error state.
           96  +    #
           97  +    set ::sqlite_io_error_persist 1
           98  +    set ::sqlite_io_error_pending $iFail
           99  +    do_test ioerr5-$locking_mode-$iFail.2 {
          100  +      set rc [catchsql {COMMIT}]
          101  +      list
          102  +    } {}
          103  +    set ::sqlite_io_error_hit 0
          104  +    set ::sqlite_io_error_persist 0
          105  +    set ::sqlite_io_error_pending 0
          106  +  
          107  +    # Read the contents of the database file into a Tcl variable.
          108  +    #
          109  +    set fd [open test.db]
          110  +    fconfigure $fd -translation binary -encoding binary
          111  +    set zDatabase [read $fd]
          112  +    close $fd
          113  +
          114  +    # Set a very low soft-limit and then try to compile an SQL statement 
          115  +    # from UTF-16 text. To do this, SQLite will need to reclaim memory
          116  +    # from the pager that is in error state. Including that associated
          117  +    # with the dirty page.
          118  +    #
          119  +    do_test ioerr5-$locking_mode-$iFail.3 {
          120  +      sqlite3_soft_heap_limit 1024
          121  +      compilesql16 "SELECT 10"
          122  +      set bt [btree_from_db db]
          123  +      array set stats [btree_pager_stats $bt]
          124  +      set stats(page)
          125  +    } {0}
          126  +
          127  +    # Ensure that nothing was written to the database while reclaiming
          128  +    # memory from the pager in error state.
          129  +    #
          130  +    do_test ioerr5-$locking_mode-$iFail.4 {
          131  +      set fd [open test.db]
          132  +      fconfigure $fd -translation binary -encoding binary
          133  +      set zDatabase2 [read $fd]
          134  +      close $fd
          135  +      expr {$zDatabase eq $zDatabase2}
          136  +    } {1}
          137  +  
          138  +    if {$rc eq [list 0 {}]} {
          139  +      do_test ioerr5-$locking_mode-$iFail.3 {
          140  +        execsql { SELECT count(*) FROM a }
          141  +      } [expr $nRow+1]
          142  +      break
          143  +    }
          144  +  }
          145  +}
          146  +
          147  +sqlite3_enable_shared_cache $::enable_shared_cache
          148  +sqlite3_soft_heap_limit $::soft_limit
          149  +
          150  +finish_test
          151  +