/ Check-in [481fd3a8]
Login

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

Overview
Comment:Add tests for malloc() failure when the atomic-write optimization is being used. Also fix things so that rollback can occur without a malloc(), even if there is a master journal file. (CVS 4330)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 481fd3a89e50e329596d96565170e3d9977aae29
User & Date: danielk1977 2007-08-30 08:08:17
Context
2007-08-30
08:27
Fix a bug in a test file causing malloc5.test to crash. (CVS 4331) check-in: ab09967b user: danielk1977 tags: trunk
08:08
Add tests for malloc() failure when the atomic-write optimization is being used. Also fix things so that rollback can occur without a malloc(), even if there is a master journal file. (CVS 4330) check-in: 481fd3a8 user: danielk1977 tags: trunk
02:26
Test case fixes for fulltest. (CVS 4329) check-in: cddce495 user: drh 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
...
868
869
870
871
872
873
874
875
876
877





878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896




897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
....
1488
1489
1490
1491
1492
1493
1494




1495
1496
1497
1498
1499
1500
1501
....
1523
1524
1525
1526
1527
1528
1529

1530
1531
1532
1533
1534
1535
1536
1537
1538

1539
1540
1541
1542
1543
1544
1545
....
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
....
1694
1695
1696
1697
1698
1699
1700

1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711

1712
1713
1714
1715
1716
1717
1718
....
1775
1776
1777
1778
1779
1780
1781




1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
** 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.380 2007/08/29 12:31:27 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
#define pager_pagehash(X)  0
#define CHECK_PAGE(x)
#endif

/*
** When this is called the journal file for pager pPager must be open.
** The master journal file name is read from the end of the file and 
** written into memory obtained from sqlite3_malloc(). *pzMaster is
** set to point at the memory and SQLITE_OK returned. The caller must
** sqlite3_free() *pzMaster.





**
** If no master journal file name is present *pzMaster is set to 0 and
** SQLITE_OK returned.
*/
static int readMasterJournal(sqlite3_file *pJrnl, char **pzMaster){
  int rc;
  u32 len;
  i64 szJ;
  u32 cksum;
  int i;
  unsigned char aMagic[8]; /* A buffer to hold the magic header */

  *pzMaster = 0;

  rc = sqlite3OsFileSize(pJrnl, &szJ);
  if( rc!=SQLITE_OK || szJ<16 ) return rc;

  rc = read32bits(pJrnl, szJ-16, &len);
  if( rc!=SQLITE_OK ) return rc;





  rc = read32bits(pJrnl, szJ-12, &cksum);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8);
  if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;

  *pzMaster = (char *)sqlite3MallocZero(len+1);
  if( !*pzMaster ){
    return SQLITE_NOMEM;
  }
  rc = sqlite3OsRead(pJrnl, *pzMaster, len, szJ-16-len);
  if( rc!=SQLITE_OK ){
    sqlite3_free(*pzMaster);
    *pzMaster = 0;
    return rc;
  }


  /* See if the checksum matches the master journal name */
  for(i=0; i<len; i++){
    cksum -= (*pzMaster)[i];
  }
  if( cksum ){
    /* If the checksum doesn't add up, then one or more of the disk sectors
    ** containing the master journal filename is corrupted. This means
    ** definitely roll back, so just return SQLITE_OK and report a (nul)
    ** master-journal filename.
    */
    sqlite3_free(*pzMaster);
    *pzMaster = 0;
  }else{
    (*pzMaster)[len] = '\0';
  }
   
  return SQLITE_OK;
}

/*
** Seek the journal file descriptor to the next sector boundary where a
................................................................................
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
** This routine checks if it is possible to delete the master journal file,
** and does so if it is.




**
** The master journal file contains the names of all child journals.
** To tell if a master journal can be deleted, check to each of the
** children.  If all children are either missing or do not refer to
** a different master journal, then this master journal can be deleted.
*/
static int pager_delmaster(Pager *pPager, const char *zMaster){
................................................................................

  rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
  if( rc!=SQLITE_OK ) goto delmaster_out;

  if( nMasterJournal>0 ){
    char *zJournal;
    char *zMasterPtr = 0;


    /* Load the entire master journal file into space obtained from
    ** sqlite3_malloc() and pointed to by zMasterJournal. 
    */
    zMasterJournal = (char *)sqlite3_malloc(nMasterJournal);
    if( !zMasterJournal ){
      rc = SQLITE_NOMEM;
      goto delmaster_out;
    }

    rc = sqlite3OsRead(pMaster, zMasterJournal, nMasterJournal, 0);
    if( rc!=SQLITE_OK ) goto delmaster_out;

    zJournal = zMasterJournal;
    while( (zJournal-zMasterJournal)<nMasterJournal ){
      if( sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS) ){
        /* One of the journals pointed to by the master journal exists.
................................................................................
        int c;
        int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
        rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
        if( rc!=SQLITE_OK ){
          goto delmaster_out;
        }

        rc = readMasterJournal(pJournal, &zMasterPtr);
        sqlite3OsClose(pJournal);
        if( rc!=SQLITE_OK ){
          goto delmaster_out;
        }

        c = zMasterPtr!=0 && strcmp(zMasterPtr, zMaster)==0;
        sqlite3_free(zMasterPtr);
        if( c ){
          /* We have a match. Do not delete the master journal file. */
          goto delmaster_out;
        }
      }
      zJournal += (strlen(zJournal)+1);
    }
................................................................................
  }

  /* Read the master journal name from the journal, if it is present.
  ** If a master journal file name is specified, but the file is not
  ** present on disk, then the journal is not hot and does not need to be
  ** played back.
  */

  rc = readMasterJournal(pPager->jfd, &zMaster);
  assert( rc!=SQLITE_DONE );
  if( rc!=SQLITE_OK 
   || (zMaster && !sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS)) 
  ){
    sqlite3_free(zMaster);
    zMaster = 0;
    if( rc==SQLITE_DONE ) rc = SQLITE_OK;
    goto end_playback;
  }
  pPager->journalOff = 0;


  /* This loop terminates either when the readJournalHdr() call returns
  ** SQLITE_DONE or an IO error occurs. */
  while( 1 ){

    /* Read the next journal header from the journal file.  If there are
    ** not enough bytes left in the journal file for a complete header, or
................................................................................
      }
    }
  }
  /*NOTREACHED*/
  assert( 0 );

end_playback:




  if( rc==SQLITE_OK ){
    rc = pager_end_transaction(pPager);
  }
  if( zMaster ){
    /* If there was a master journal and this routine will return success,
    ** see if it is possible to delete the master journal.
    */
    if( rc==SQLITE_OK ){
      rc = pager_delmaster(pPager, zMaster);
    }
    sqlite3_free(zMaster);
  }

  /* The Pager.sectorSize variable may have been updated while rolling
  ** back a journal created by a process with a different sector size
  ** value. Reset it to the correct value for this process.
  */
  setSectorSize(pPager);







|







 







|
|
|
>
>
>
>
>

|


|







|






>
>
>
>







<
<
<
<
|

<
<


>



|
|






<
|
<
<







 







>
>
>
>







 







>




|




>







 







|





|
<







 







>
|


|

<





>







 







>
>
>
>



|



<
|
<
<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912




913
914


915
916
917
918
919
920
921
922
923
924
925
926
927
928

929


930
931
932
933
934
935
936
....
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
....
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
....
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569

1570
1571
1572
1573
1574
1575
1576
....
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712

1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
....
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799

1800


1801
1802
1803
1804
1805
1806
1807
** 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.381 2007/08/30 08:08:17 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
#define pager_pagehash(X)  0
#define CHECK_PAGE(x)
#endif

/*
** When this is called the journal file for pager pPager must be open.
** The master journal file name is read from the end of the file and 
** written into memory supplied by the caller. 
**
** zMaster must point to a buffer of at least nMaster bytes allocated by
** the caller. This should be sqlite3_vfs.mxPathname+1 (to ensure there is
** enough space to write the master journal name). If the master journal
** name in the journal is longer than nMaster bytes (including a
** nul-terminator), then this is handled as if no master journal name
** were present in the journal.
**
** If no master journal file name is present zMaster[0] is set to 0 and
** SQLITE_OK returned.
*/
static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, int nMaster){
  int rc;
  u32 len;
  i64 szJ;
  u32 cksum;
  int i;
  unsigned char aMagic[8]; /* A buffer to hold the magic header */

  zMaster[0] = '\0';

  rc = sqlite3OsFileSize(pJrnl, &szJ);
  if( rc!=SQLITE_OK || szJ<16 ) return rc;

  rc = read32bits(pJrnl, szJ-16, &len);
  if( rc!=SQLITE_OK ) return rc;

  if( len>=nMaster ){
    return SQLITE_OK;
  }

  rc = read32bits(pJrnl, szJ-12, &cksum);
  if( rc!=SQLITE_OK ) return rc;

  rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8);
  if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;





  rc = sqlite3OsRead(pJrnl, zMaster, len, szJ-16-len);
  if( rc!=SQLITE_OK ){


    return rc;
  }
  zMaster[len] = '\0';

  /* See if the checksum matches the master journal name */
  for(i=0; i<len; i++){
    cksum -= zMaster[i];
   }
  if( cksum ){
    /* If the checksum doesn't add up, then one or more of the disk sectors
    ** containing the master journal filename is corrupted. This means
    ** definitely roll back, so just return SQLITE_OK and report a (nul)
    ** master-journal filename.
    */

    zMaster[0] = '\0';


  }
   
  return SQLITE_OK;
}

/*
** Seek the journal file descriptor to the next sector boundary where a
................................................................................
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
** This routine checks if it is possible to delete the master journal file,
** and does so if it is.
**
** Argument zMaster may point to Pager.pTmpSpace. So that buffer is not 
** available for use within this function.
**
**
** The master journal file contains the names of all child journals.
** To tell if a master journal can be deleted, check to each of the
** children.  If all children are either missing or do not refer to
** a different master journal, then this master journal can be deleted.
*/
static int pager_delmaster(Pager *pPager, const char *zMaster){
................................................................................

  rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
  if( rc!=SQLITE_OK ) goto delmaster_out;

  if( nMasterJournal>0 ){
    char *zJournal;
    char *zMasterPtr = 0;
    int nMasterPtr = pPager->pVfs->mxPathname+1;

    /* Load the entire master journal file into space obtained from
    ** sqlite3_malloc() and pointed to by zMasterJournal. 
    */
    zMasterJournal = (char *)sqlite3_malloc(nMasterJournal + nMasterPtr);
    if( !zMasterJournal ){
      rc = SQLITE_NOMEM;
      goto delmaster_out;
    }
    zMasterPtr = &zMasterJournal[nMasterJournal];
    rc = sqlite3OsRead(pMaster, zMasterJournal, nMasterJournal, 0);
    if( rc!=SQLITE_OK ) goto delmaster_out;

    zJournal = zMasterJournal;
    while( (zJournal-zMasterJournal)<nMasterJournal ){
      if( sqlite3OsAccess(pVfs, zJournal, SQLITE_ACCESS_EXISTS) ){
        /* One of the journals pointed to by the master journal exists.
................................................................................
        int c;
        int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
        rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
        if( rc!=SQLITE_OK ){
          goto delmaster_out;
        }

        rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
        sqlite3OsClose(pJournal);
        if( rc!=SQLITE_OK ){
          goto delmaster_out;
        }

        c = zMasterPtr[0]!=0 && strcmp(zMasterPtr, zMaster)==0;

        if( c ){
          /* We have a match. Do not delete the master journal file. */
          goto delmaster_out;
        }
      }
      zJournal += (strlen(zJournal)+1);
    }
................................................................................
  }

  /* Read the master journal name from the journal, if it is present.
  ** If a master journal file name is specified, but the file is not
  ** present on disk, then the journal is not hot and does not need to be
  ** played back.
  */
  zMaster = pPager->pTmpSpace;
  rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  assert( rc!=SQLITE_DONE );
  if( rc!=SQLITE_OK 
   || (zMaster[0] && !sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS)) 
  ){

    zMaster = 0;
    if( rc==SQLITE_DONE ) rc = SQLITE_OK;
    goto end_playback;
  }
  pPager->journalOff = 0;
  zMaster = 0;

  /* This loop terminates either when the readJournalHdr() call returns
  ** SQLITE_DONE or an IO error occurs. */
  while( 1 ){

    /* Read the next journal header from the journal file.  If there are
    ** not enough bytes left in the journal file for a complete header, or
................................................................................
      }
    }
  }
  /*NOTREACHED*/
  assert( 0 );

end_playback:
  if( rc==SQLITE_OK ){
    zMaster = pPager->pTmpSpace;
    rc = readMasterJournal(pPager->jfd, zMaster, pPager->pVfs->mxPathname+1);
  }
  if( rc==SQLITE_OK ){
    rc = pager_end_transaction(pPager);
  }
  if( rc==SQLITE_OK && zMaster[0] ){
    /* If there was a master journal and this routine will return success,
    ** see if it is possible to delete the master journal.
    */

    rc = pager_delmaster(pPager, zMaster);


  }

  /* The Pager.sectorSize variable may have been updated while rolling
  ** back a journal created by a process with a different sector size
  ** value. Reset it to the correct value for this process.
  */
  setSectorSize(pPager);

Added test/mallocD.test.













































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 2007 Aug 29
#
# 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: mallocD.test,v 1.1 2007/08/30 08:08:17 danielk1977 Exp $

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

sqlite3_simulate_device -char atomic

set PREP { 
  PRAGMA page_size = 1024;
  CREATE TABLE abc(a, b, c);
}

do_malloc_test mallocD-1 -sqlprep $PREP -sqlbody { 
  INSERT INTO abc VALUES(1, 2, 3);
}

do_malloc_test mallocD-2 -sqlprep $PREP -sqlbody {
  BEGIN;
  INSERT INTO abc VALUES(1, 2, 3);
  INSERT INTO abc VALUES(4, 5, 6);
  ROLLBACK;
}

do_malloc_test mallocD-3 -sqlprep $PREP -sqlbody {
  BEGIN;
  INSERT INTO abc VALUES(1, 2, 3);
  INSERT INTO abc VALUES(4, 5, randstr(1500,1500));
  COMMIT;
}

do_malloc_test mallocD-4 -sqlprep $PREP -sqlbody {
  ATTACH 'test2.db' AS aux;
  BEGIN;
  CREATE TABLE aux.def(d, e, f);
  INSERT INTO abc VALUES(4, 5, 6);
  COMMIT;
}

sqlite3_simulate_device -char {}

finish_test