Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a memory leak that can occur in os_unix.c if an IO error occurs within the xUnlock method. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
6c5c04eea1f0e8d61883ee8675c249fb |
User & Date: | dan 2010-06-16 10:55:43.000 |
Context
2010-06-16
| ||
12:30 | Add extra test cases to pager1.test. (check-in: ad3209572d user: dan tags: trunk) | |
10:55 | Fix a memory leak that can occur in os_unix.c if an IO error occurs within the xUnlock method. (check-in: 6c5c04eea1 user: dan tags: trunk) | |
2010-06-15
| ||
19:07 | Rationalize a common pattern in tcl test cases into proc do_multiclient_test. (check-in: efe4456498 user: dan tags: trunk) | |
Changes
Changes to src/os_unix.c.
︙ | ︙ | |||
730 731 732 733 734 735 736 737 738 739 740 741 742 743 | #endif }; /* ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** ** The mutex entered using the unixEnterMutex() function must be held ** when this function is called. */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | 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 | #endif }; /* ** A lists of all unixInodeInfo objects. */ static unixInodeInfo *inodeList = 0; /* ** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. ** If all such file descriptors are closed without error, the list is ** cleared and SQLITE_OK returned. ** ** Otherwise, if an error occurs, then successfully closed file descriptor ** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. ** not deleted and SQLITE_IOERR_CLOSE returned. */ static int closePendingFds(unixFile *pFile){ int rc = SQLITE_OK; unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *pError = 0; UnixUnusedFd *p; UnixUnusedFd *pNext; for(p=pInode->pUnused; p; p=pNext){ pNext = p->pNext; if( close(p->fd) ){ pFile->lastErrno = errno; rc = SQLITE_IOERR_CLOSE; p->pNext = pError; pError = p; }else{ sqlite3_free(p); } } pInode->pUnused = pError; return rc; } /* ** Release a unixInodeInfo structure previously allocated by findInodeInfo(). ** ** The mutex entered using the unixEnterMutex() function must be held ** when this function is called. */ static void releaseInodeInfo(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; assert( unixMutexHeld() ); if( pInode ){ pInode->nRef--; if( pInode->nRef==0 ){ assert( pInode->pShmNode==0 ); closePendingFds(pFile); if( pInode->pPrev ){ assert( pInode->pPrev->pNext==pInode ); pInode->pPrev->pNext = pInode->pNext; }else{ assert( inodeList==pInode ); inodeList = pInode->pNext; } |
︙ | ︙ | |||
1148 1149 1150 1151 1152 1153 1154 | end_lock: unixLeaveMutex(); OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); return rc; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | end_lock: unixLeaveMutex(); OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), rc==SQLITE_OK ? "ok" : "failed")); return rc; } /* ** Add the file descriptor used by file handle pFile to the corresponding ** pUnused list. */ static void setPendingFd(unixFile *pFile){ unixInodeInfo *pInode = pFile->pInode; UnixUnusedFd *p = pFile->pUnused; |
︙ | ︙ | |||
1447 1448 1449 1450 1451 1452 1453 | /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->pUnused list. It will be automatically closed ** when the last lock is cleared. */ setPendingFd(pFile); } | | | 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 | /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->pUnused list. It will be automatically closed ** when the last lock is cleared. */ setPendingFd(pFile); } releaseInodeInfo(pFile); rc = closeUnixFile(id); unixLeaveMutex(); } return rc; } /************** End of the posix advisory lock implementation ***************** |
︙ | ︙ | |||
2062 2063 2064 2065 2066 2067 2068 | */ static int semClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; semUnlock(id, NO_LOCK); assert( pFile ); unixEnterMutex(); | | | 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 | */ static int semClose(sqlite3_file *id) { if( id ){ unixFile *pFile = (unixFile*)id; semUnlock(id, NO_LOCK); assert( pFile ); unixEnterMutex(); releaseInodeInfo(pFile); unixLeaveMutex(); closeUnixFile(id); } return SQLITE_OK; } #endif /* OS_VXWORKS */ |
︙ | ︙ | |||
2529 2530 2531 2532 2533 2534 2535 | /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->aPending. It will be automatically closed when ** the last lock is cleared. */ setPendingFd(pFile); } | | | 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 | /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pInode->aPending. It will be automatically closed when ** the last lock is cleared. */ setPendingFd(pFile); } releaseInodeInfo(pFile); sqlite3_free(pFile->lockingContext); rc = closeUnixFile(id); unixLeaveMutex(); } return rc; } |
︙ | ︙ |
Changes to src/test_vfs.c.
︙ | ︙ | |||
72 73 74 75 76 77 78 | #define TESTVFS_SHMLOCK_MASK 0x00000010 #define TESTVFS_SHMMAP_MASK 0x00000020 #define TESTVFS_SHMBARRIER_MASK 0x00000040 #define TESTVFS_SHMCLOSE_MASK 0x00000080 #define TESTVFS_OPEN_MASK 0x00000100 #define TESTVFS_SYNC_MASK 0x00000200 | > | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #define TESTVFS_SHMLOCK_MASK 0x00000010 #define TESTVFS_SHMMAP_MASK 0x00000020 #define TESTVFS_SHMBARRIER_MASK 0x00000040 #define TESTVFS_SHMCLOSE_MASK 0x00000080 #define TESTVFS_OPEN_MASK 0x00000100 #define TESTVFS_SYNC_MASK 0x00000200 #define TESTVFS_DELETE_MASK 0x00000400 #define TESTVFS_ALL_MASK 0x000007FF #define TESTVFS_MAX_PAGES 256 /* ** A shared-memory buffer. There is one of these objects for each shared ** memory region opened by clients. If two clients open the same file, |
︙ | ︙ | |||
453 454 455 456 457 458 459 | /* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ | > > > > > > > > > > | > > | 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 | /* ** Delete the file located at zPath. If the dirSync argument is true, ** ensure the file-system modifications are synced to disk before ** returning. */ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ int rc = SQLITE_OK; Testvfs *p = (Testvfs *)pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ tvfsExecTcl(p, "xDelete", Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync); } return rc; } /* ** Test for access permissions. Return true if the requested permission ** is available, or false otherwise. */ static int tvfsAccess( |
︙ | ︙ | |||
839 840 841 842 843 844 845 846 847 848 849 850 851 852 | } vfsmethod [] = { { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; if( objc!=3 ){ | > | 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 | } vfsmethod [] = { { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xDelete", TESTVFS_DELETE_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; if( objc!=3 ){ |
︙ | ︙ |
Changes to test/malloc_common.tcl.
︙ | ︙ | |||
105 106 107 108 109 110 111 112 113 114 | eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec } } #------------------------------------------------------------------------- # Procedures to save and restore the current file-system state: # # faultsim_save_and_close # faultsim_restore_and_reopen # | > > | | | < < | < | < < | | > > | | | < < < | < < < | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | eval do_one_faultsim_test "$name-$f" $FAULTSIM($f) $testspec } } #------------------------------------------------------------------------- # Procedures to save and restore the current file-system state: # # faultsim_save # faultsim_save_and_close # faultsim_restore_and_reopen # faultsim_delete_and_reopen # proc faultsim_save {} { foreach f [glob -nocomplain sv_test.db*] { file delete -force $f } foreach f [glob -nocomplain test.db*] { set f2 "sv_$f" file copy -force $f $f2 } } proc faultsim_save_and_close {} { faultsim_save catch { db close } return "" } proc faultsim_restore_and_reopen {} { catch { db close } foreach f [glob -nocomplain test.db*] { file delete -force $f } foreach f2 [glob -nocomplain sv_test.db*] { set f [string range $f2 3 end] file copy -force $f2 $f } sqlite3 db test.db sqlite3_extended_result_codes db 1 sqlite3_db_config_lookaside db 0 0 0 } proc faultsim_integrity_check {{db db}} { set ic [$db eval { PRAGMA integrity_check }] if {$ic != "ok"} { error "Integrity check: $ic" } } proc faultsim_delete_and_reopen {{file test.db}} { catch { db close } foreach f [glob -nocomplain test.db*] { file delete -force $f } sqlite3 db test.db } # The following procs are used as [do_one_faultsim_test] callbacks when # injecting OOM faults into test cases. # |
︙ | ︙ |
Changes to test/pager1.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl do_multiclient_test tn { # Create and populate a database table using connection [db]. Check # that connections [db2] and [db3] can see the schema and content. # do_test pager1-$tn.1 { | > > > > > > > > > > > > > > > | 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 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl # # pager1-1.*: Test inter-process locking (clients in multiple processes). # # pager1-2.*: Test intra-process locking (multiple clients in this process). # # pager1-3.*: Savepoint related tests. # proc do_execsql_test {testname sql result} { uplevel do_test $testname [list "execsql {$sql}"] [list $result] } proc do_catchsql_test {testname sql result} { uplevel do_test $testname [list "catchsql {$sql}"] [list $result] } do_multiclient_test tn { # Create and populate a database table using connection [db]. Check # that connections [db2] and [db3] can see the schema and content. # do_test pager1-$tn.1 { |
︙ | ︙ | |||
147 148 149 150 151 152 153 154 155 | do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} do_test pager1-$tn.26 { sql1 { COMMIT } } {} do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | do_test pager1-$tn.25 { sql1 { UPDATE t1 SET a = a+10 } } {} do_test pager1-$tn.26 { sql1 { COMMIT } } {} do_test pager1-$tn.27 { sql1 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.27 { sql2 { SELECT * FROM t1 } } {21 one 22 two 23 three} do_test pager1-$tn.28 { sql3 { SELECT * FROM t1 } } {21 one 22 two 23 three} } do_test pager1-3.1 { faultsim_delete_and_reopen execsql { CREATE TABLE t1(a PRIMARY KEY, b); CREATE TABLE counter( i CHECK (i<5), u CHECK (u<10) ); INSERT INTO counter VALUES(0, 0); CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN UPDATE counter SET i = i+1; END; CREATE TRIGGER tr2 AFTER UPDATE ON t1 BEGIN UPDATE counter SET u = u+1; END; } execsql { SELECT * FROM counter } } {0 0} do_execsql_test pager1-3.2 { BEGIN; INSERT INTO t1 VALUES(1, randomblob(1500)); INSERT INTO t1 VALUES(2, randomblob(1500)); INSERT INTO t1 VALUES(3, randomblob(1500)); SELECT * FROM counter; } {3 0} do_catchsql_test pager1-3.3 { INSERT INTO t1 SELECT a+3, randomblob(1500) FROM t1 } {1 {constraint failed}} do_execsql_test pager1-3.4 { SELECT * FROM counter } {3 0} do_execsql_test pager1-3.5 { SELECT a FROM t1 } {1 2 3} do_execsql_test pager1-3.6 { COMMIT } {} finish_test |
Added test/pagerfault.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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | # 2010 June 15 # # 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } db func a_string a_string #------------------------------------------------------------------------- # Test fault-injection while rolling back a hot-journal file. # do_test pagerfault-1-pre1 { execsql { PRAGMA journal_mode = DELETE; PRAGMA cache_size = 10; CREATE TABLE t1(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; BEGIN; INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; } faultsim_save_and_close } {} do_faultsim_test pagerfault-1 -prep { faultsim_restore_and_reopen } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 4} faultsim_integrity_check if {[db one { SELECT count(*) FROM t1 }] != 4} { error "Database content appears incorrect" } } #------------------------------------------------------------------------- # Test fault-injection while rolling back hot-journals that were created # as part of a multi-file transaction. # do_test pagerfault-2-pre1 { testvfs tstvfs -default 1 tstvfs filter xDelete tstvfs script xDeleteCallback proc xDeleteCallback {method file args} { set file [file tail $file] if { [string match *mj* $file] } { faultsim_save } } faultsim_delete_and_reopen db func a_string a_string execsql { ATTACH 'test.db2' AS aux; PRAGMA journal_mode = DELETE; PRAGMA main.cache_size = 10; PRAGMA aux.cache_size = 10; CREATE TABLE t1(a UNIQUE, b UNIQUE); CREATE TABLE aux.t2(a UNIQUE, b UNIQUE); INSERT INTO t1 VALUES(a_string(200), a_string(300)); INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1; INSERT INTO t2 SELECT * FROM t1; BEGIN; INSERT INTO t1 SELECT a_string(201), a_string(301) FROM t1; INSERT INTO t1 SELECT a_string(202), a_string(302) FROM t1; INSERT INTO t1 SELECT a_string(203), a_string(303) FROM t1; INSERT INTO t1 SELECT a_string(204), a_string(304) FROM t1; REPLACE INTO t2 SELECT * FROM t1; COMMIT; } db close tstvfs delete } {} do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep { faultsim_restore_and_reopen } -body { execsql { ATTACH 'test.db2' AS aux; SELECT count(*) FROM t2; SELECT count(*) FROM t1; } } -test { faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}} faultsim_integrity_check catchsql { ATTACH 'test.db2' AS aux } if {[db one { SELECT count(*) FROM t1 }] != 4 || [db one { SELECT count(*) FROM t2 }] != 4 } { error "Database content appears incorrect" } } finish_test |
Changes to test/permutations.test.
︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 | walfault.test } test_suite "coverage-pager" -description { Coverage tests for file pager.c. } -files { pager1.test } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the permutation test suites: # | > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | walfault.test } test_suite "coverage-pager" -description { Coverage tests for file pager.c. } -files { pager1.test pagerfault.test } lappend ::testsuitelist xxx #------------------------------------------------------------------------- # Define the permutation test suites: # |
︙ | ︙ |