/ Check-in [4ad18091]
Login

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

Overview
Comment:If an IO error occurs while locking the database and checking the cache validity, unlock the database before returning. Ticket #3030. (CVS 5083)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 4ad1809192b616d1c12499825bcd0967dea76864
User & Date: danielk1977 2008-05-05 16:23:55
Context
2008-05-05
16:27
Do not segfault in the CLI if sqlite3_open() fails to create a database connection object. Ticket #3096. (CVS 5084) check-in: 0bec7ebf user: drh tags: trunk
16:23
If an IO error occurs while locking the database and checking the cache validity, unlock the database before returning. Ticket #3030. (CVS 5083) check-in: 4ad18091 user: danielk1977 tags: trunk
15:26
Avoid leaking page references after an IO error is encountered. (CVS 5082) check-in: 198c395b user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
3397
3398
3399
3400
3401
3402
3403

3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414

3415
3416
3417
3418
3419
3420
3421
....
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
....
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472

3473
3474

3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487

3488
3489
3490
3491
3492
3493
3494
....
3508
3509
3510
3511
3512
3513
3514
3515

3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
....
3532
3533
3534
3535
3536
3537
3538





3539
3540
3541
3542
3543
3544
3545
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.438 2008/05/01 17:16:53 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  if( pPager->state==PAGER_UNLOCK || isHot ){
    sqlite3_vfs *pVfs = pPager->pVfs;
    if( !MEMDB ){
      assert( pPager->nRef==0 );
      if( !pPager->noReadlock ){
        rc = pager_wait_on_lock(pPager, SHARED_LOCK);
        if( rc!=SQLITE_OK ){

          return pager_error(pPager, rc);
        }
        assert( pPager->state>=SHARED_LOCK );
      }
  
      /* If a journal file exists, and there is no RESERVED lock on the
      ** database file, then it either needs to be played back or deleted.
      */
      rc = hasHotJournal(pPager);
      if( rc<0 ){
        return SQLITE_IOERR_NOMEM;

      }
      if( rc==1 || isHot ){
        /* Get an EXCLUSIVE lock on the database file. At this point it is
        ** important that a RESERVED lock is not obtained on the way to the
        ** EXCLUSIVE lock. If it were, another process might open the
        ** database file, detect the RESERVED lock, and conclude that the
        ** database is safe to read while this process is still rolling it 
................................................................................
        ** Because the intermediate RESERVED lock is not requested, the
        ** second process will get to this point in the code and fail to
        ** obtain its own EXCLUSIVE lock on the database file.
        */
        if( pPager->state<EXCLUSIVE_LOCK ){
          rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
          if( rc!=SQLITE_OK ){
            pager_unlock(pPager);
            return pager_error(pPager, rc);
          }
          pPager->state = PAGER_EXCLUSIVE;
        }
 
        /* Open the journal for read/write access. This is because in 
        ** exclusive-access mode the file descriptor will be kept open and
        ** possibly used for a transaction later on. On some systems, the
................................................................................
          }else{
            /* If sqlite3OsAccess() returns a negative value, that means it
            ** failed a memory allocation */
            rc = SQLITE_IOERR_NOMEM;
          }
        }
        if( rc!=SQLITE_OK ){
          pager_unlock(pPager);
          switch( rc ){
            case SQLITE_NOMEM:
            case SQLITE_IOERR_UNLOCK:
            case SQLITE_IOERR_NOMEM:
              return rc;
            default:

              return SQLITE_BUSY;
          }

        }
        pPager->journalOpen = 1;
        pPager->journalStarted = 0;
        pPager->journalOff = 0;
        pPager->setMaster = 0;
        pPager->journalHdr = 0;
 
        /* Playback and delete the journal.  Drop the database write
        ** lock and reacquire the read lock.
        */
        rc = pager_playback(pPager, 1);
        if( rc!=SQLITE_OK ){
          return pager_error(pPager, rc);

        }
        assert(pPager->state==PAGER_SHARED || 
            (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
        );
      }

      if( pPager->pAll ){
................................................................................
        ** detected.  The chance of an undetected change is so small that
        ** it can be neglected.
        */
        char dbFileVers[sizeof(pPager->dbFileVers)];
        sqlite3PagerPagecount(pPager);

        if( pPager->errCode ){
          return pPager->errCode;

        }

        if( pPager->dbSize>0 ){
          IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
          rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
          if( rc!=SQLITE_OK ){
            return rc;
          }
        }else{
          memset(dbFileVers, 0, sizeof(dbFileVers));
        }

        if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
          pager_reset(pPager);
................................................................................
    }
    assert( pPager->exclusiveMode || pPager->state<=PAGER_SHARED );
    if( pPager->state==PAGER_UNLOCK ){
      pPager->state = PAGER_SHARED;
    }
  }






  return rc;
}

/*
** Allocate a PgHdr object.   Either create a new one or reuse
** an existing one that is not otherwise in use.
**







|







 







>










|
>







 







|
|







 







<
<
<
|
|
<
<
>
|

>












|
>







 







|
>






|







 







>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
....
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
....
3461
3462
3463
3464
3465
3466
3467



3468
3469


3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
....
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
....
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.439 2008/05/05 16:23:55 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  if( pPager->state==PAGER_UNLOCK || isHot ){
    sqlite3_vfs *pVfs = pPager->pVfs;
    if( !MEMDB ){
      assert( pPager->nRef==0 );
      if( !pPager->noReadlock ){
        rc = pager_wait_on_lock(pPager, SHARED_LOCK);
        if( rc!=SQLITE_OK ){
          assert( pPager->state==PAGER_UNLOCK );
          return pager_error(pPager, rc);
        }
        assert( pPager->state>=SHARED_LOCK );
      }
  
      /* If a journal file exists, and there is no RESERVED lock on the
      ** database file, then it either needs to be played back or deleted.
      */
      rc = hasHotJournal(pPager);
      if( rc<0 ){
        rc = SQLITE_IOERR_NOMEM;
        goto failed;
      }
      if( rc==1 || isHot ){
        /* Get an EXCLUSIVE lock on the database file. At this point it is
        ** important that a RESERVED lock is not obtained on the way to the
        ** EXCLUSIVE lock. If it were, another process might open the
        ** database file, detect the RESERVED lock, and conclude that the
        ** database is safe to read while this process is still rolling it 
................................................................................
        ** Because the intermediate RESERVED lock is not requested, the
        ** second process will get to this point in the code and fail to
        ** obtain its own EXCLUSIVE lock on the database file.
        */
        if( pPager->state<EXCLUSIVE_LOCK ){
          rc = sqlite3OsLock(pPager->fd, EXCLUSIVE_LOCK);
          if( rc!=SQLITE_OK ){
            rc = pager_error(pPager, rc);
            goto failed;
          }
          pPager->state = PAGER_EXCLUSIVE;
        }
 
        /* Open the journal for read/write access. This is because in 
        ** exclusive-access mode the file descriptor will be kept open and
        ** possibly used for a transaction later on. On some systems, the
................................................................................
          }else{
            /* If sqlite3OsAccess() returns a negative value, that means it
            ** failed a memory allocation */
            rc = SQLITE_IOERR_NOMEM;
          }
        }
        if( rc!=SQLITE_OK ){



          if( rc!=SQLITE_NOMEM && rc!=SQLITE_IOERR_UNLOCK 
           && rc!=SQLITE_IOERR_NOMEM 


          ){
            rc = SQLITE_BUSY;
          }
          goto failed;
        }
        pPager->journalOpen = 1;
        pPager->journalStarted = 0;
        pPager->journalOff = 0;
        pPager->setMaster = 0;
        pPager->journalHdr = 0;
 
        /* Playback and delete the journal.  Drop the database write
        ** lock and reacquire the read lock.
        */
        rc = pager_playback(pPager, 1);
        if( rc!=SQLITE_OK ){
          rc = pager_error(pPager, rc);
          goto failed;
        }
        assert(pPager->state==PAGER_SHARED || 
            (pPager->exclusiveMode && pPager->state>PAGER_SHARED)
        );
      }

      if( pPager->pAll ){
................................................................................
        ** detected.  The chance of an undetected change is so small that
        ** it can be neglected.
        */
        char dbFileVers[sizeof(pPager->dbFileVers)];
        sqlite3PagerPagecount(pPager);

        if( pPager->errCode ){
          rc = pPager->errCode;
          goto failed;
        }

        if( pPager->dbSize>0 ){
          IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
          rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
          if( rc!=SQLITE_OK ){
            goto failed;
          }
        }else{
          memset(dbFileVers, 0, sizeof(dbFileVers));
        }

        if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
          pager_reset(pPager);
................................................................................
    }
    assert( pPager->exclusiveMode || pPager->state<=PAGER_SHARED );
    if( pPager->state==PAGER_UNLOCK ){
      pPager->state = PAGER_SHARED;
    }
  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }
  return rc;
}

/*
** Allocate a PgHdr object.   Either create a new one or reuse
** an existing one that is not otherwise in use.
**

Changes to test/tester.tcl.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
680
681
682
683
684
685
686
687
688
689
690


691
692
693
694
695
696
697
698





















699
700
701

702
703
704
705
706
707
708
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.117 2008/05/05 15:26:51 danielk1977 Exp $

#
# What for user input before continuing.  This gives an opportunity
# to connect profiling tools to the process.
#
for {set i 0} {$i<[llength $argv]} {incr i} {
  if {[regexp {^-+pause$} [lindex $argv $i] all value]} {
................................................................................
      # One of two things must have happened. either
      #   1.  We never hit the IO error and the SQL returned OK
      #   2.  An IO error was hit and the SQL failed
      #
      expr { ($s && !$r && !$q) || (!$s && $r && $q) }
    } {1}

    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} {
      # Check that no page references were leaked. There should be 
      # a single reference if there is still an active transaction, 
      # or zero otherwise.


      do_test $testname.$n.4 {
        set bt [btree_from_db db]
        db_enter db
        array set stats [btree_pager_stats $bt]
        db_leave db
        set stats(ref)
      } [expr {[sqlite3_get_autocommit db]?0:1}]
    }






















    # If an IO error occured, then the checksum of the database should
    # be the same as before the script that caused the IO error was run.

    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
      do_test $testname.$n.5 {
        catch {db close}
        set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
        cksum
      } $checksum
    }







|







 







<
|
|
|
>
>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
680
681
682
683
684
685
686

687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements some common TCL routines used for regression
# testing the SQLite library
#
# $Id: tester.tcl,v 1.118 2008/05/05 16:23:55 danielk1977 Exp $

#
# What for user input before continuing.  This gives an opportunity
# to connect profiling tools to the process.
#
for {set i 0} {$i<[llength $argv]} {incr i} {
  if {[regexp {^-+pause$} [lindex $argv $i] all value]} {
................................................................................
      # One of two things must have happened. either
      #   1.  We never hit the IO error and the SQL returned OK
      #   2.  An IO error was hit and the SQL failed
      #
      expr { ($s && !$r && !$q) || (!$s && $r && $q) }
    } {1}


    # Check that no page references were leaked. There should be 
    # a single reference if there is still an active transaction, 
    # or zero otherwise.
    #
    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-ckrefcount)} {
      do_test $testname.$n.4 {
        set bt [btree_from_db db]
        db_enter db
        array set stats [btree_pager_stats $bt]
        db_leave db
        set stats(ref)
      } [expr {[sqlite3_get_autocommit db]?0:1}]
    }

    # If there is an open database handle and no open transaction, 
    # and the pager is not running in exclusive-locking mode,
    # check that the pager is in "unlocked" state. Theoretically,
    # if a call to xUnlock() failed due to an IO error the underlying
    # file may still be locked.
    #
    ifcapable pragma {
      if { [info commands db] ne ""
        && [db one {pragma locking_mode}] eq "normal"
        && [sqlite3_get_autocommit db]
      } {
        do_test $testname.$n.5 {
          set bt [btree_from_db db]
          db_enter db
          array set stats [btree_pager_stats $bt]
          db_leave db
          set stats(state)
        } 0
      }
    }

    # If an IO error occured, then the checksum of the database should
    # be the same as before the script that caused the IO error was run.
    #
    if {$::go && $::sqlite_io_error_hardhit && $::ioerropts(-cksum)} {
      do_test $testname.$n.5 {
        catch {db close}
        set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
        cksum
      } $checksum
    }