SQLite

Check-in [ce6f56ece8]
Login

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

Overview
Comment:Fix an obscure pager refcnt leak that occurs following a transient I/O error. (CVS 3829)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ce6f56ece8eab743d20cd2c0a84b4a266c4da2ff
User & Date: drh 2007-04-09 12:45:03.000
Context
2007-04-09
13:18
Perparations for the release of 3.3.15. (CVS 3830) (check-in: 8d0dea873d user: drh tags: trunk)
12:45
Fix an obscure pager refcnt leak that occurs following a transient I/O error. (CVS 3829) (check-in: ce6f56ece8 user: drh tags: trunk)
11:20
Have the pager call xReiniter() instead of xDestructor() to restore btree level state after rolling back a page. (CVS 3828) (check-in: 4e8941333e user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.353 2007/04/07 15:03:17 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
** 2004 April 6
**
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.354 2007/04/09 12:45:03 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952

3953
3954
3955
3956
3957
3958
3959
      put4byte(&pPage->aData[4], 0);
      put4byte(&pPage1->aData[32], pPage->pgno);
      TRACE(("FREE-PAGE: %d new trunk page replacing %d\n",
              pPage->pgno, pTrunk->pgno));
    }else{
      /* Add the newly freed page as a leaf on the current trunk */
      rc = sqlite3PagerWrite(pTrunk->pDbPage);
      if( rc ) return rc;
      put4byte(&pTrunk->aData[4], k+1);
      put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
#ifndef SQLITE_SECURE_DELETE
      sqlite3PagerDontWrite(pBt->pPager, pPage->pgno);
#endif

      TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
    }
    releasePage(pTrunk);
  }
  return rc;
}








|
|
|

|

>







3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
      put4byte(&pPage->aData[4], 0);
      put4byte(&pPage1->aData[32], pPage->pgno);
      TRACE(("FREE-PAGE: %d new trunk page replacing %d\n",
              pPage->pgno, pTrunk->pgno));
    }else{
      /* Add the newly freed page as a leaf on the current trunk */
      rc = sqlite3PagerWrite(pTrunk->pDbPage);
      if( rc==SQLITE_OK ){
        put4byte(&pTrunk->aData[4], k+1);
        put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
#ifndef SQLITE_SECURE_DELETE
        sqlite3PagerDontWrite(pBt->pPager, pPage->pgno);
#endif
      }
      TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
    }
    releasePage(pTrunk);
  }
  return rc;
}

4831
4832
4833
4834
4835
4836
4837

4838
4839
4840
4841
4842
4843
4844
4845

4846
4847
4848
4849
4850
4851
4852
  for(i=0; i<k; i++){
    MemPage *pNew;
    if( i<nOld ){
      pNew = apNew[i] = apOld[i];
      pgnoNew[i] = pgnoOld[i];
      apOld[i] = 0;
      rc = sqlite3PagerWrite(pNew->pDbPage);

      if( rc ) goto balance_cleanup;
    }else{
      assert( i>0 );
      rc = allocateBtreePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
      if( rc ) goto balance_cleanup;
      apNew[i] = pNew;
    }
    nNew++;

    zeroPage(pNew, pageFlags);
  }

  /* Free any old pages that were not reused as new pages.
  */
  while( i<nOld ){
    rc = freePage(apOld[i]);







>






<
|
>







4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845

4846
4847
4848
4849
4850
4851
4852
4853
4854
  for(i=0; i<k; i++){
    MemPage *pNew;
    if( i<nOld ){
      pNew = apNew[i] = apOld[i];
      pgnoNew[i] = pgnoOld[i];
      apOld[i] = 0;
      rc = sqlite3PagerWrite(pNew->pDbPage);
      nNew++;
      if( rc ) goto balance_cleanup;
    }else{
      assert( i>0 );
      rc = allocateBtreePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1], 0);
      if( rc ) goto balance_cleanup;
      apNew[i] = pNew;

      nNew++;
    }
    zeroPage(pNew, pageFlags);
  }

  /* Free any old pages that were not reused as new pages.
  */
  while( i<nOld ){
    rc = freePage(apOld[i]);
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
                 ** initialized before use.  Adding an extra initialization
                 ** to silence the compiler slows down the code. */
    int notUsed;
    unsigned char *tempCell = 0;
    assert( !pPage->leafData );
    getTempCursor(pCur, &leafCur);
    rc = sqlite3BtreeNext(&leafCur, &notUsed);
    if( rc!=SQLITE_OK ){
      if( rc!=SQLITE_NOMEM ){
        rc = SQLITE_CORRUPT_BKPT; 
      }
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerWrite(leafCur.pPage->pDbPage);
    }
    if( rc==SQLITE_OK ){
      TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
         pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
      dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));







<
<
<
<
<







5417
5418
5419
5420
5421
5422
5423





5424
5425
5426
5427
5428
5429
5430
                 ** initialized before use.  Adding an extra initialization
                 ** to silence the compiler slows down the code. */
    int notUsed;
    unsigned char *tempCell = 0;
    assert( !pPage->leafData );
    getTempCursor(pCur, &leafCur);
    rc = sqlite3BtreeNext(&leafCur, &notUsed);





    if( rc==SQLITE_OK ){
      rc = sqlite3PagerWrite(leafCur.pPage->pDbPage);
    }
    if( rc==SQLITE_OK ){
      TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
         pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
      dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
Changes to src/test1.c.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.235 2007/04/06 21:42:22 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>








|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing all sorts of SQLite interfaces.  This code
** is not included in the SQLite library.  It is used for automated
** testing of the SQLite library.
**
** $Id: test1.c,v 1.236 2007/04/09 12:45:03 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
#include <stdlib.h>
#include <string.h>

3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
  return TCL_OK;
}


/*
** Usage:   sqlite3_pager_refcounts  DB
**
** Return a list of numbers when are the PagerRefcount for each
** pager on each database.
*/
static int test_pager_refcounts(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3 *db;
  int i;
  int v, *a;
  Tcl_Obj *pResult;

  if( objc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " <STMT>", 0);
    return TCL_ERROR;
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
  pResult = Tcl_NewObj();
  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt==0 ){
      v = -1;







|
|














|







3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
  return TCL_OK;
}


/*
** Usage:   sqlite3_pager_refcounts  DB
**
** Return a list of numbers which are the PagerRefcount for all
** pagers on each database connection.
*/
static int test_pager_refcounts(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  sqlite3 *db;
  int i;
  int v, *a;
  Tcl_Obj *pResult;

  if( objc!=2 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " DB", 0);
    return TCL_ERROR;
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
  pResult = Tcl_NewObj();
  for(i=0; i<db->nDb; i++){
    if( db->aDb[i].pBt==0 ){
      v = -1;
Changes to test/ioerr2.test.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: ioerr2.test,v 1.2 2007/04/09 11:20:54 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test ioerr2-1.1 {
  execsql {
    PRAGMA cache_size = 10;







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# This file implements regression tests for SQLite library.  The
# focus of this file is testing for correct handling of I/O errors
# such as writes failing because the disk is full.
# 
# The tests in this file use special facilities that are only
# available in the SQLite test fixture.
#
# $Id: ioerr2.test,v 1.3 2007/04/09 12:45:03 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test ioerr2-1.1 {
  execsql {
    PRAGMA cache_size = 10;
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64



65
66
67
68
69
70
71
  # pager must be in error state. In this case open a new database
  # connection. Otherwise, try a ROLLBACK, in case a transaction 
  # is still active.
  set rc [catch {execsql {PRAGMA integrity_check}} msg]
  if {$rc && $msg eq "disk I/O error"} {
    db close
    sqlite3 db test.db

  } else {
    if {$rc || $msg ne "ok"} {
      error $msg
    }
    catch {execsql ROLLBACK}
  }

  # Check that the database checksum is still $::cksum, and that
  # the integrity-check passes.
  set ck [execsql {SELECT md5sum(a, b) FROM t1}]
  do_test ${testname}.cksum [list set ck $ck] $::cksum
  integrity_check ${testname}.integrity



}

check_db ioerr2-2

set sql {
  PRAGMA cache_size = 10;
  PRAGMA default_cache_size = 10;







>












>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  # pager must be in error state. In this case open a new database
  # connection. Otherwise, try a ROLLBACK, in case a transaction 
  # is still active.
  set rc [catch {execsql {PRAGMA integrity_check}} msg]
  if {$rc && $msg eq "disk I/O error"} {
    db close
    sqlite3 db test.db
    set refcnt 0
  } else {
    if {$rc || $msg ne "ok"} {
      error $msg
    }
    catch {execsql ROLLBACK}
  }

  # Check that the database checksum is still $::cksum, and that
  # the integrity-check passes.
  set ck [execsql {SELECT md5sum(a, b) FROM t1}]
  do_test ${testname}.cksum [list set ck $ck] $::cksum
  integrity_check ${testname}.integrity
  do_test ${testname}.refcnt {
    lindex [sqlite3_pager_refcounts db] 0
  } 0
}

check_db ioerr2-2

set sql {
  PRAGMA cache_size = 10;
  PRAGMA default_cache_size = 10;