* If the pager is still in an error state, do not proceed. The error
* state will be cleared at some point in the future when all page
* references are dropped and the cache can be discarded.
*/
if (null != errCode && errCode != SqlJetErrorCode.FULL) {
throw new SqlJetException(errCode);
}
if (SqlJetPagerState.UNLOCK == state || isErrorReset)
try {
boolean isHotJournal = false;
assert (!memDb);
assert (pageCache.getRefCount() == 0);
if (!noReadlock) {
try {
waitOnLock(SqlJetLockType.SHARED);
} catch (SqlJetException e) {
assert (state == SqlJetPagerState.UNLOCK);
error(e);
throw e;
}
} else if (state == SqlJetPagerState.UNLOCK) {
state = SqlJetPagerState.SHARED;
}
assert (SqlJetPagerState.SHARED == state);
/*
* If a journal file exists, and there is no RESERVED lock on
* the database file, then it either needs to be played back or
* deleted.
*/
if (!isErrorReset) {
isHotJournal = hasHotJournal();
}
if (isErrorReset || isHotJournal) {
/*
* Get an EXCLUSIVE lock on the database file. At this point
* it is important that a RESERVED lock is not obtained on
* the way to the EXCLUSIVE lock. If it were, another
* process might open the database file, detect the RESERVED
* lock, and conclude that the database is safe to read
* while this process is still rolling it back.
*
* Because the intermediate RESERVED lock is not requested,
* the second process will get to this point in the code and
* fail to obtain its own EXCLUSIVE lock on the database
* file.
*/
if (SqlJetPagerState.EXCLUSIVE.compareTo(state) > 0) {
try {
fd.lock(SqlJetLockType.EXCLUSIVE);
} catch (SqlJetException e) {
error(e);
throw e;
}
state = SqlJetPagerState.EXCLUSIVE;
}
/*
* Open the journal for read/write access. This is because
* in exclusive-access mode the file descriptor will be kept
* open and possibly used for a transaction later on. On
* some systems, the OsTruncate() call used in
* exclusive-access mode also requires a read/write file
* handle.
*/
if (null == jfd) {
if (fileSystem.access(journal, SqlJetFileAccesPermission.EXISTS)) {
assert (!tempFile);
jfd = fileSystem.open(journal, SqlJetFileType.MAIN_JOURNAL, SqlJetUtility
.of(SqlJetFileOpenPermission.READWRITE));
if (null != jfd) {
try {
final Set<SqlJetFileOpenPermission> p = jfd.getPermissions();
if (p.contains(SqlJetFileOpenPermission.READONLY))
throw new SqlJetException(SqlJetErrorCode.CANTOPEN);
} catch (SqlJetException e) {
jfd.close();
throw e;
}
}
} else {
/*
* If the journal does not exist, it usually means
* that some other connection managed to get in and
* roll it back before this connection obtained the
* exclusive lock above. Or, it may mean that the
* pager was in the error-state when this function
* was called and the journal file does not exist.
*/
endTransaction(false);
}
journalOpen = true;
journalStarted = false;
journalOff = 0;
setMaster = false;
journalHdr = 0;
/*
* Playback and delete the journal. Drop the database
* write lock and reacquire the read lock. Purge the
* cache before playing back the hot-journal so that we
* don't end up with an inconsistent cache.
*/
try {
pageCache.clear();
} finally {
if (null != jfd) {
try {
playback(true);
} catch (SqlJetException e) {
error(e);
throw e;
}
}
}
assert (SqlJetPagerState.SHARED == state || (SqlJetPagerLockingMode.EXCLUSIVE == lockingMode && SqlJetPagerState.SHARED
.compareTo(state) < 0));
}
}
if (pageCache.getPageCount() > 0) {
/*
* The shared-lock has just been acquired on the database
* file and there are already pages in the cache (from a
* previous read or write transaction). Check to see if the
* database has been modified. If the database has changed,
* flush the cache.
*
* Database changes is detected by looking at 15 bytes
* beginning at offset 24 into the file. The first 4 of
* these 16 bytes are a 32-bit counter that is incremented
* with each change. The other bytes change randomly with
* each file change when a codec is in use.
*
* There is a vanishingly small chance that a change will
* not be detected. The chance of an undetected change is so
* small that it can be neglected.
*/
ISqlJetMemoryPointer dbFileVers = SqlJetUtility.allocatePtr(this.dbFileVers.remaining());
getPageCount();
if (null != errCode) {
throw new SqlJetException(errCode);
}
assert (dbSizeValid);
if (dbSize > 0) {
PAGERTRACE("CKVERS %s %d\n", PAGERID(), dbFileVers.remaining());