Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | When the final connection disconnects from a wal mode database, check that the database file has not been moved or unlinked before deleting the wal and shm files. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
4761db83b6d3d57f281370899403c102 |
User & Date: | dan 2018-02-07 16:14:41 |
References
2018-02-07
| ||
18:45 | Fix typo in comment. Skip tests added by check-in [4761db83b6] when running on Windows. (check-in: 468a389c user: mistachkin tags: trunk) | |
Context
2018-02-07
| ||
18:02 | In extensions rtree, fts3 and fts5, ensure that when dynamic buffers are bound to persistent SQL statements using SQLITE_STATIC, the binding is replaced with an SQL NULL before the buffer is freed. Otherwise, a user may obtain a pointer to the persistent statement using sqlite3_next_stmt() and attempt to access the freed buffer using sqlite3_expanded_sql() or similar. (check-in: 2a5f813b user: dan tags: trunk) | |
16:14 | When the final connection disconnects from a wal mode database, check that the database file has not been moved or unlinked before deleting the wal and shm files. (check-in: 4761db83 user: dan tags: trunk) | |
2018-02-05
| ||
21:02 | Adjust the previous check-in, which modified the Win32 VFS, so that it works with SQLITE_OMIT_WAL. (check-in: 36c2e67e user: mistachkin tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 | PgHdr *pNext; for(p=pPager->pMmapFreelist; p; p=pNext){ pNext = p->pDirty; sqlite3_free(p); } } /* ** Shutdown the page cache. Free all memory and close all files. ** ** If a transaction was in progress when this routine is called, that ** transaction is rolled back. All outstanding pages are invalidated ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ | > > > > > > > > > > > > > > > > > > > > > > > > | < > > | < | > | > > > | > | 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 | PgHdr *pNext; for(p=pPager->pMmapFreelist; p; p=pNext){ pNext = p->pDirty; sqlite3_free(p); } } /* Verify that the database file has not be deleted or renamed out from ** under the pager. Return SQLITE_OK if the database is still were it ought ** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error ** code from sqlite3OsAccess()) if the database has gone missing. */ static int databaseIsUnmoved(Pager *pPager){ int bHasMoved = 0; int rc; if( pPager->tempFile ) return SQLITE_OK; if( pPager->dbSize==0 ) return SQLITE_OK; assert( pPager->zFilename && pPager->zFilename[0] ); rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved); if( rc==SQLITE_NOTFOUND ){ /* If the HAS_MOVED file-control is unimplemented, assume that the file ** has not been moved. That is the historical behavior of SQLite: prior to ** version 3.8.3, it never checked */ rc = SQLITE_OK; }else if( rc==SQLITE_OK && bHasMoved ){ rc = SQLITE_READONLY_DBMOVED; } return rc; } /* ** Shutdown the page cache. Free all memory and close all files. ** ** If a transaction was in progress when this routine is called, that ** transaction is rolled back. All outstanding pages are invalidated ** and their memory is freed. Any attempt to use a page associated ** with this page cache after this function returns will likely ** result in a coredump. ** ** This function always succeeds. If a transaction is active an attempt ** is made to roll it back. If an error occurs during the rollback ** a hot journal may be left in the filesystem but no error is returned ** to the caller. */ int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ u8 *pTmp = (u8*)pPager->pTmpSpace; assert( db || pagerUseWal(pPager)==0 ); assert( assert_pager_state(pPager) ); disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pagerFreeMapHdrs(pPager); /* pPager->errCode = 0; */ pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL { u8 *a = 0; assert( db || pPager->pWal==0 ); if( db && 0==(db->flags & SQLITE_NoCkptOnClose) && SQLITE_OK==databaseIsUnmoved(pPager) ){ a = pTmp; } sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a); pPager->pWal = 0; } #endif pager_reset(pPager); if( MEMDB ){ pager_unlock(pPager); }else{ /* If it is open, sync the journal file before calling UnlockAndRollback. ** If this is not done, then an unsynced portion of the open journal |
︙ | ︙ | |||
4963 4964 4965 4966 4967 4968 4969 | /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ *ppPager = pPager; return SQLITE_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < | 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 | /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */ *ppPager = pPager; return SQLITE_OK; } /* ** This function is called after transitioning from PAGER_UNLOCK to ** PAGER_SHARED state. It tests if there is a hot journal present in ** the file-system for the given pager. A hot journal is one that ** needs to be played back. According to this function, a hot-journal ** file exists if the following criteria are met: |
︙ | ︙ |
Changes to test/nockpt.test.
︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 | } {1 2 3 4 5 6 7 8 9} do_execsql_test 1.13 { PRAGMA main.journal_mode } {wal} do_test 1.14 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1} do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete} do_test 1.15 { file exists test.db-wal } {0} finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 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 | } {1 2 3 4 5 6 7 8 9} do_execsql_test 1.13 { PRAGMA main.journal_mode } {wal} do_test 1.14 { sqlite3_db_config db NO_CKPT_ON_CLOSE 1 } {1} do_execsql_test 1.14 { PRAGMA main.journal_mode = delete } {delete} do_test 1.15 { file exists test.db-wal } {0} #------------------------------------------------------------------------- # Test an unusual scenario: # # 1. A wal mode db is opened and written. Then sqlite3_close_v2() used # to close the db handle while there is still an unfinalized # statement (so the db handle stays open). # # 2. The db, wal and *-shm files are deleted from the file system. # # 3. Another connection creates a new wal mode db at the same file-system # location as the previous one. # # 4. The statement left unfinalized in (1) is finalized. # # The test is to ensure that the connection left open in step (1) does # not try to delete the wal file from the file-system as part of step # 4. # reset_db db close # Open a connection on a wal database. Write to it a bit. Then prepare # a statement and call sqlite3_close_v2() (so that the statement handle # holds the db connection open). # set ::db1 [sqlite3_open_v2 test.db SQLITE_OPEN_READWRITE ""] do_test 2.0 { lindex [ sqlite3_exec $::db1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x PRIMARY KEY, y UNIQUE, z); INSERT INTO t1 VALUES(1, 2, 3); PRAGMA wal_checkpoint; }] 0 } {0} set ::stmt [sqlite3_prepare $::db1 "SELECT * FROM t1" -1 dummy] sqlite3_close_v2 $::db1 # Delete the database, wal and shm files. # forcedelete test.db test.db-wal test.db-shm # Open and populate a new database file at the same file-system location # as the one just deleted. Contrive a partial checkpoint on it. # sqlite3 db test.db sqlite3 db2 test.db do_execsql_test 2.1 { PRAGMA journal_mode = wal; CREATE TABLE y1(a PRIMARY KEY, b UNIQUE, c); INSERT INTO y1 VALUES('a', 'b', 'c'); INSERT INTO y1 VALUES('d', 'e', 'f'); } {wal} do_execsql_test -db db2 2.2 { BEGIN; SELECT * FROM y1; } {a b c d e f} do_execsql_test 2.3 { UPDATE y1 SET c='g' WHERE a='d'; PRAGMA wal_checkpoint; } {0 11 10} do_execsql_test -db db2 2.4 { COMMIT } # Finalize the statement handle, causing the first connection to be # closed. Test that this has not corrupted the database file by # deleting the new wal file from the file-system. If it has, this # test should fail with an IO or corruption error. # do_test 2.5 { sqlite3_finalize $::stmt sqlite3 db3 test.db execsql { PRAGMA integrity_check; SELECT * FROM y1; } db3 } {ok a b c d e g} finish_test |