/* ** Call sqlite3WalOpen() to open the WAL handle. If the pager is in ** exclusive-locking mode when this function is called, take an EXCLUSIVE ** lock on the database file and use heap-memory to store the wal-index ** in. Otherwise, use the normal shared-memory. */ static int pagerOpenWal(Pager *pPager) { int rc = SQLITE.OK; assert(pPager.pWal == 0 && pPager.tempFile == 0); assert(pPager.eLock == SHARED_LOCK || pPager.eLock == EXCLUSIVE_LOCK || pPager.noReadlock); /* If the pager is already in exclusive-mode, the WAL module will use ** heap-memory for the wal-index instead of the VFS shared-memory ** implementation. Take the exclusive lock now, before opening the WAL ** file, to make sure this is safe. */ if (pPager.exclusiveMode) { rc = pagerExclusiveLock(pPager); } /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), return an error code. */ if (rc == SQLITE.OK) { rc = sqlite3WalOpen(pPager.pVfs, pPager.fd, pPager.zWal, pPager.exclusiveMode, &pPager.pWal pPager.journalSizeLimit, &pPager.pWal ); } return(rc); }
/* ** This function is called when the user invokes "PRAGMA wal_checkpoint", ** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint() ** or wal_blocking_checkpoint() API functions. ** ** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART. */ int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt) { int rc = SQLITE.OK; if (pPager.pWal) { rc = sqlite3WalCheckpoint(pPager.pWal, eMode, pPager.xBusyHandler, pPager.pBusyHandlerArg, pPager.ckptSyncFlags, pPager.pageSize, (u8 *)pPager.pTmpSpace, pnLog, pnCkpt ); } return(rc); }
/* ** Attempt to take an exclusive lock on the database file. If a PENDING lock ** is obtained instead, immediately release it. */ static int pagerExclusiveLock(Pager *pPager) { int rc; /* Return code */ assert(pPager.eLock == SHARED_LOCK || pPager.eLock == EXCLUSIVE_LOCK); rc = pagerLockDb(pPager, EXCLUSIVE_LOCK); if (rc != SQLITE.OK) { /* If the attempt to grab the exclusive lock failed, release the ** pending lock that may have been obtained instead. */ pagerUnlockDb(pPager, SHARED_LOCK); } return(rc); }
/* ** Check if the *-wal file that corresponds to the database opened by pPager ** exists if the database is not empy, or verify that the *-wal file does ** not exist (by deleting it) if the database file is empty. ** ** If the database is not empty and the *-wal file exists, open the pager ** in WAL mode. If the database is empty or if no *-wal file exists and ** if no error occurs, make sure Pager.journalMode is not set to ** PAGER_JOURNALMODE_WAL. ** ** Return SQLITE.OK or an error code. ** ** The caller must hold a SHARED lock on the database file to call this ** function. Because an EXCLUSIVE lock on the db file is required to delete ** a WAL on a none-empty database, this ensures there is no race condition ** between the xAccess() below and an xDelete() being executed by some ** other connection. */ static int pagerOpenWalIfPresent(Pager *pPager) { int rc = SQLITE.OK; Debug.Assert(pPager.eState == PAGER_OPEN); Debug.Assert(pPager.eLock >= SHARED_LOCK || pPager.noReadlock); if (!pPager.tempFile) { int isWal; /* True if WAL file exists */ Pgno nPage; /* Size of the database file */ rc = pagerPagecount(pPager, &nPage); if (rc) { return(rc); } if (nPage == 0) { rc = sqlite3OsDelete(pPager.pVfs, pPager.zWal, 0); isWal = 0; } else { rc = sqlite3OsAccess( pPager.pVfs, pPager.zWal, SQLITE_ACCESS_EXISTS, &isWal ); } if (rc == SQLITE.OK) { if (isWal) { testcase(sqlite3PcachePagecount(pPager.pPCache) == 0); rc = sqlite3PagerOpenWal(pPager, 0); } else if (pPager.journalMode == PAGER_JOURNALMODE_WAL) { pPager.journalMode = PAGER_JOURNALMODE_DELETE; } } } return(rc); }
/* ** This function is called to close the connection to the log file prior ** to switching from WAL to rollback mode. ** ** Before closing the log file, this function attempts to take an ** EXCLUSIVE lock on the database file. If this cannot be obtained, an ** error (SQLITE_BUSY) is returned and the log connection is not closed. ** If successful, the EXCLUSIVE lock is not released before returning. */ int sqlite3PagerCloseWal(Pager *pPager) { int rc = SQLITE.OK; assert(pPager.journalMode == PAGER_JOURNALMODE_WAL); /* If the log file is not already open, but does exist in the file-system, ** it may need to be checkpointed before the connection can switch to ** rollback mode. Open it now so this can happen. */ if (!pPager.pWal) { int logexists = 0; rc = pagerLockDb(pPager, SHARED_LOCK); if (rc == SQLITE.OK) { rc = sqlite3OsAccess( pPager.pVfs, pPager.zWal, SQLITE_ACCESS_EXISTS, &logexists ); } if (rc == SQLITE.OK && logexists) { rc = pagerOpenWal(pPager); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if (rc == SQLITE.OK && pPager.pWal) { rc = pagerExclusiveLock(pPager); if (rc == SQLITE.OK) { rc = sqlite3WalClose(pPager.pWal, pPager.ckptSyncFlags, pPager.pageSize, (u8 *)pPager.pTmpSpace); pPager.pWal = 0; } } return(rc); }
/* ** Begin a read transaction on the WAL. ** ** This routine used to be called "pagerOpenSnapshot()" because it essentially ** makes a snapshot of the database at the current point in time and preserves ** that snapshot for use by the reader in spite of concurrently changes by ** other writers or checkpointers. */ static int pagerBeginReadTransaction(Pager *pPager) { int rc; /* Return code */ int changed = 0; /* True if cache must be reset */ assert(pagerUseWal(pPager)); assert(pPager.eState == PAGER_OPEN || pPager.eState == PAGER_READER); /* sqlite3WalEndReadTransaction() was not called for the previous ** transaction in locking_mode=EXCLUSIVE. So call it now. If we ** are in locking_mode=NORMAL and EndRead() was previously called, ** the duplicate call is harmless. */ sqlite3WalEndReadTransaction(pPager.pWal); rc = sqlite3WalBeginReadTransaction(pPager.pWal, &changed); if (rc != SQLITE.OK || changed) { pager_reset(pPager); } return(rc); }
/* ** The caller must be holding a SHARED lock on the database file to call ** this function. ** ** If the pager passed as the first argument is open on a real database ** file (not a temp file or an in-memory database), and the WAL file ** is not already open, make an attempt to open it now. If successful, ** return SQLITE.OK. If an error occurs or the VFS used by the pager does ** not support the xShmXXX() methods, return an error code. *pbOpen is ** not modified in either case. ** ** If the pager is open on a temp-file (or in-memory database), or if ** the WAL file is already open, set *pbOpen to 1 and return SQLITE.OK ** without doing anything. */ int sqlite3PagerOpenWal( Pager *pPager, /* Pager object */ int *pbOpen /* OUT: Set to true if call is a no-op */ ) { int rc = SQLITE.OK; /* Return code */ assert(assert_pager_state(pPager)); assert(pPager.eState == PAGER_OPEN || pbOpen); assert(pPager.eState == PAGER_READER || !pbOpen); assert(pbOpen == 0 || *pbOpen == 0); assert(pbOpen != 0 || (!pPager.tempFile && !pPager.pWal)); if (!pPager.tempFile && !pPager.pWal) { if (!sqlite3PagerWalSupported(pPager)) { return(SQLITE_CANTOPEN); } /* Close any rollback journal previously open */ sqlite3OsClose(pPager.jfd); rc = pagerOpenWal(pPager); if (rc == SQLITE.OK) { pPager.journalMode = PAGER_JOURNALMODE_WAL; pPager.eState = PAGER_OPEN; } } else { *pbOpen = 1; } return(rc); }
/* ** Return true if the underlying VFS for the given pager supports the ** primitives necessary for write-ahead logging. */ int sqlite3PagerWalSupported(Pager *pPager) { const sqlite3_io_methods *pMethods = pPager.fd->pMethods; return(pPager.exclusiveMode || (pMethods->iVersion >= 2 && pMethods->xShmMap)); }
int sqlite3PagerWalCallback(Pager *pPager) { return(sqlite3WalCallback(pPager.pWal)); }