Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a change-counter bug similar to #3584. This one is much more obscure though, requiring a transient IO or malloc error to occur while running in exclusive mode. (CVS 6189) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
9f07d2d9226b73c4dc311fd142e0aaba |
User & Date: | danielk1977 2009-01-16 16:40:14.000 |
Context
2009-01-16
| ||
23:47 | Remove a harmless UMR that occurs inside some debugging code. (CVS 6190) (check-in: 191c399fc6 user: drh tags: trunk) | |
16:40 | Fix a change-counter bug similar to #3584. This one is much more obscure though, requiring a transient IO or malloc error to occur while running in exclusive mode. (CVS 6189) (check-in: 9f07d2d922 user: danielk1977 tags: trunk) | |
16:23 | Revert (6187). (CVS 6188) (check-in: a353c1ab37 user: danielk1977 tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
14 15 16 17 18 19 20 | ** 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. ** | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** 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.554 2009/01/16 16:40:14 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" /* ** Macros for troubleshooting. Normally turned off */ |
︙ | ︙ | |||
1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 | ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the ** assertion that the transaction counter was modified. */ assert( pPager->fd->pMethods==0 || sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK ); 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, zMaster[0]!='\0'); | > > > > > > > > > > > | 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 | ** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the ** assertion that the transaction counter was modified. */ assert( pPager->fd->pMethods==0 || sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK ); /* If this playback is happening automatically as a result of an IO or ** malloc error that occured after the change-counter was updated but ** before the transaction was committed, then the change-counter ** modification may just have been reverted. If this happens in exclusive ** mode, then subsequent transactions performed by the connection will not ** update the change-counter at all. This may lead to cache inconsistency ** problems for other processes at some point in the future. So, just ** in case this has happened, clear the changeCountDone flag now. */ pPager->changeCountDone = 0; 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, zMaster[0]!='\0'); |
︙ | ︙ |
Changes to test/malloc.test.
︙ | ︙ | |||
12 13 14 15 16 17 18 | # This file attempts to check the behavior of the SQLite library in # an out-of-memory situation. When compiled with -DSQLITE_DEBUG=1, # the SQLite library accepts a special command (sqlite3_memdebug_fail N C) # which causes the N-th malloc to fail. This special feature is used # to see what happens in the library if a malloc were to really fail # due to an out-of-memory situation. # | | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # This file attempts to check the behavior of the SQLite library in # an out-of-memory situation. When compiled with -DSQLITE_DEBUG=1, # the SQLite library accepts a special command (sqlite3_memdebug_fail N C) # which causes the N-th malloc to fail. This special feature is used # to see what happens in the library if a malloc were to really fail # due to an out-of-memory situation. # # $Id: malloc.test,v 1.74 2009/01/16 16:40:14 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Only run these tests if memory debugging is turned on. # |
︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | do_malloc_test 31 -sqlprep { PRAGMA journal_mode = persist; PRAGMA journal_size_limit = 1024; CREATE TABLE t1(a PRIMARY KEY, b); } -sqlbody { INSERT INTO t1 VALUES(1, 2); } # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} set sqlite_open_file_count } {0} puts open-file-count=$sqlite_open_file_count finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 | do_malloc_test 31 -sqlprep { PRAGMA journal_mode = persist; PRAGMA journal_size_limit = 1024; CREATE TABLE t1(a PRIMARY KEY, b); } -sqlbody { INSERT INTO t1 VALUES(1, 2); } # When written, this test provoked an obscure change-counter bug. # # If, when running in exclusive mode, a malloc() failure occurs # after the database file change-counter has been written but # before the transaction has been committed, then the transaction # is automatically rolled back. However, internally the # Pager.changeCounterDone flag was being left set. This means # that if the same connection attempts another transaction following # the malloc failure and rollback, the change counter will not # be updated. This could corrupt another processes cache. # do_malloc_test 32 -tclprep { # Build a small database containing an indexed table. # db eval { BEGIN; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); INSERT INTO t1 VALUES(3, 'three'); COMMIT; PRAGMA locking_mode = exclusive; } # Open a second database connection. Load the table (but not index) # into the second connections pager cache. # sqlite3 db2 test.db db2 eval { SELECT b FROM t1 } } -tclbody { # Running in exclusive mode, perform a database transaction that # modifies both the database table and index. For iterations where # the malloc failure occurs after updating the change counter but # before committing the transaction, this should result in the # transaction being rolled back but the changeCounterDone flag # left set. # db eval { UPDATE t1 SET a = a + 3 } } -cleanup { # Perform another transaction using the first connection. Unlock # the database after doing so. If this is one of the right iterations, # then this should result in the database contents being updated but # the change-counter left as it is. # db eval { PRAGMA locking_mode = normal; UPDATE t1 SET a = a + 3; } # Now do an integrity check with the second connection. The second # connection still has the database table in its cache. If this is # one of the magic iterations and the change counter was not modified, # then it won't realize that the cached data is out of date. Since # the cached data won't match the up to date index data read from # the database file, the integrity check should fail. # set zRepeat "transient" if {$::iRepeat} {set zRepeat "persistent"} do_test malloc-32.$zRepeat.${::n}.integrity { execsql {PRAGMA integrity_check} db2 } {ok} db2 close } # Ensure that no file descriptors were leaked. do_test malloc-99.X { catch {db close} set sqlite_open_file_count } {0} puts open-file-count=$sqlite_open_file_count finish_test |