]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add comments describing UNKNOWN_LOCK to pager.c. Improve some other comments i the...
authordan <dan@noemail.net>
Thu, 5 Aug 2010 18:53:26 +0000 (18:53 +0000)
committerdan <dan@noemail.net>
Thu, 5 Aug 2010 18:53:26 +0000 (18:53 +0000)
FossilOrigin-Name: 54eff6de9d8d87f33192c192ca91907c4c090988

manifest
manifest.uuid
src/pager.c

index 4b915c190959a6cb9f34508d3f2e689e86fe5dac..510ddb550145dad2ef6984b52a5aafa9f6c15025 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\strunk\schanges\swith\sexperimental\sbranch.
-D 2010-08-05T16:22:50
+C Add\scomments\sdescribing\sUNKNOWN_LOCK\sto\spager.c.\sImprove\ssome\sother\scomments\si\sthe\ssame\sfile.
+D 2010-08-05T18:53:27
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -156,7 +156,7 @@ F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
 F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e
 F src/os_unix.c ae5ca8a6031380708f3fec7be325233d49944914
 F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7
-F src/pager.c 7232578996c9ecd7b57bb9952a81549f2f133a4b
+F src/pager.c 07bda904e5b3e6fe6c9425a205fcaf1dca5a444c
 F src/pager.h 80726162dc3942f59ab27b738fb667b9ba0a89d5
 F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58
 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -842,7 +842,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 800f496929fb0d45d16c573c5dca0197ac922e2b ca479f3de2927ccc05dc76d10e40c00b8e0c88d1
-R 5a6390c995b69a7f924deee65f1b7dc5
+P acd26b8b746980c344db017a0e96dbd92c89acdf
+R c079a70d95550574b0e39982e236e749
 U dan
-Z 434bd8bfb5691e86b993c0ccfbfc4d4c
+Z 83b26f553c42e8e1edd2dad0b7de50dd
index c5356f714b130c64236255a1cbd6e5a517ba1242..8f9281da3f34138b86f860314e75776c469a2062 100644 (file)
@@ -1 +1 @@
-acd26b8b746980c344db017a0e96dbd92c89acdf
\ No newline at end of file
+54eff6de9d8d87f33192c192ca91907c4c090988
\ No newline at end of file
index cbbce3ff0a441fffc509bf6e426a0202a5724747..f5a2c4c4b35a63239eec79c8a541d9006cdd8a0e 100644 (file)
@@ -163,12 +163,23 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 **    rollback (non-WAL) mode are met. Unless the pager is (or recently
 **    was) in exclusive-locking mode, a user-level read transaction is 
 **    open. The database size is known in this state.
+**
+**    A connection running with locking_mode=normal enters this state when
+**    it opens a read-transaction on the database and returns to state
+**    NONE after the read-transaction is completed. However a connection
+**    running in locking_mode=exclusive (including temp databases) remains in
+**    this state even after the read-transaction is closed. The only way
+**    a locking_mode=exclusive connection can transition from READER to NONE
+**    is via the ERROR state (see below).
 ** 
-**    * A read transaction may be active.
+**    * A read transaction may be active (but a write-transaction cannot).
 **    * A SHARED or greater lock is held on the database file.
 **    * The dbSize variable may be trusted (even if a user-level read 
 **      transaction is not active). The dbOrigSize and dbFileSize variables
 **      may not be trusted at this point.
+**    * If the database is a WAL database, then the WAL connection is open.
+**    * Even if a read-transaction is not open, it is guaranteed that 
+**      there is no hot-journal in the file-system.
 **
 **  WRITER_INITIAL:
 **
@@ -290,62 +301,55 @@ int sqlite3PagerTrace=1;  /* True to enable tracing */
 #define PAGER_WRITER_FINISHED       5
 #define PAGER_ERROR                 6
 
-
 /*
-** The page cache as a whole is always in one of the following
-** states:
-**
-**   PAGER_UNLOCK        The page cache is not currently reading or 
-**                       writing the database file.  There is no
-**                       data held in memory.  This is the initial
-**                       state.
-**
-**   PAGER_SHARED        The page cache is reading the database.
-**                       Writing is not permitted.  There can be
-**                       multiple readers accessing the same database
-**                       file at the same time.
-**
-**   PAGER_RESERVED      This process has reserved the database for writing
-**                       but has not yet made any changes.  Only one process
-**                       at a time can reserve the database.  The original
-**                       database file has not been modified so other
-**                       processes may still be reading the on-disk
-**                       database file.
-**
-**   PAGER_EXCLUSIVE     The page cache is writing the database.
-**                       Access is exclusive.  No other processes or
-**                       threads can be reading or writing while one
-**                       process is writing.
-**
-**   PAGER_SYNCED        The pager moves to this state from PAGER_EXCLUSIVE
-**                       after all dirty pages have been written to the
-**                       database file and the file has been synced to
-**                       disk. All that remains to do is to remove or
-**                       truncate the journal file and the transaction 
-**                       will be committed.
-**
-** The page cache comes up in PAGER_UNLOCK.  The first time a
-** sqlite3PagerGet() occurs, the state transitions to PAGER_SHARED.
-** After all pages have been released using sqlite_page_unref(),
-** the state transitions back to PAGER_UNLOCK.  The first time
-** that sqlite3PagerWrite() is called, the state transitions to
-** PAGER_RESERVED.  (Note that sqlite3PagerWrite() can only be
-** called on an outstanding page which means that the pager must
-** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
-** PAGER_RESERVED means that there is an open rollback journal.
-** The transition to PAGER_EXCLUSIVE occurs before any changes
-** are made to the database file, though writes to the rollback
-** journal occurs with just PAGER_RESERVED.  After an sqlite3PagerRollback()
-** or sqlite3PagerCommitPhaseTwo(), the state can go back to PAGER_SHARED,
-** or it can stay at PAGER_EXCLUSIVE if we are in exclusive access mode.
+** The Pager.eLock variable is almost always set to one of the 
+** following locking-states, according to the lock currently held on
+** the database file: NO_LOCK, SHARED_LOCK, RESERVED_LOCK or EXCLUSIVE_LOCK.
+** This variable is kept up to date as locks are taken and released by
+** the pagerLockDb() and pagerUnlockDb() wrappers.
+**
+** If the VFS xLock() or xUnlock() returns an error other than SQLITE_BUSY
+** (i.e. one of the SQLITE_IOERR subtypes), it is not clear whether or not
+** the operation was successful. In these circumstances pagerLockDb() and
+** pagerUnlockDb() take a conservative approach - eLock is always updated
+** when unlocking the file, and only updated when locking the file if the
+** VFS call is successful. This way, the Pager.eLock variable may be set
+** to a less exclusive (lower) value than the lock that is actually held
+** at the system level, but it is never set to a more exclusive value.
+**
+** This is usually safe. If an xUnlock fails or appears to fail, there may 
+** be a few redundant xLock() calls or a lock may be held for longer than
+** required, but nothing really goes wrong.
+**
+** The exception is when the database file is unlocked as the pager moves
+** from ERROR to NONE state. At this point there may be a hot-journal file 
+** in the file-system that needs to be rolled back (as part of a NONE->SHARED
+** transition, by the same pager or any other). If the call to xUnlock()
+** fails at this point and the pager is left holding an EXCLUSIVE lock, this
+** can confuse the call to xCheckReservedLock() call made later as part
+** of hot-journal detection.
+**
+** xCheckReservedLock() is defined as returning true "if there is a RESERVED 
+** lock held by this process or any others". So xCheckReservedLock may 
+** return true because the caller itself is holding an EXCLUSIVE lock (but
+** doesn't know it because of a previous error in xUnlock). If this happens
+** a hot-journal may be mistaken for a journal being created by an active
+** transaction in another process, causing SQLite to read from the database
+** without rolling it back.
+**
+** To work around this, if a call to xUnlock() fails when unlocking the
+** database in the ERROR state, Pager.eLock is set to UNKNOWN_LOCK. It
+** is only changed back to a real locking state after a successful call
+** to xLock(EXCLUSIVE). Also, the code to do the NONE->SHARED state transition
+** omits the check for a hot-journal if Pager.eLock is set to UNKNOWN_LOCK 
+** lock. Instead, it assumes a hot-journal exists and obtains an EXCLUSIVE
+** lock on the database file before attempting to roll it back. See function
+** PagerSharedLock() for more detail.
+**
+** Pager.eLock may only be set to UNKNOWN_LOCK when the pager is in 
+** PAGER_NONE state.
 */
-#define PAGER_UNLOCK      0
-#define PAGER_SHARED      1   /* same as SHARED_LOCK */
-#define PAGER_RESERVED    2   /* same as RESERVED_LOCK */
-#define PAGER_EXCLUSIVE   4   /* same as EXCLUSIVE_LOCK */
-#define PAGER_SYNCED      5
-
-#define UNKNOWN_LOCK      (EXCLUSIVE_LOCK+1)
+#define UNKNOWN_LOCK                (EXCLUSIVE_LOCK+1)
 
 /*
 ** A macro used for invoking the codec if there is one
@@ -398,14 +402,11 @@ struct PagerSavepoint {
 ** A open page cache is an instance of the following structure.
 **
 ** errCode
+**   The Pager.errCode variable is only ever non-zero when the condition
+**   (Pager.eState==PAGER_ERROR) is true. 
 **
-**   Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
-**   or SQLITE_FULL. Once one of the first three errors occurs, it persists
-**   and is returned as the result of every major pager API call.  The
-**   SQLITE_FULL return code is slightly different. It persists only until the
-**   next successful rollback is performed on the pager cache. Also,
-**   SQLITE_FULL does not affect the sqlite3PagerGet() and sqlite3PagerLookup()
-**   APIs, they may still be used successfully.
+**   Pager.errCode may be set to SQLITE_FULL, SQLITE_IOERR or one of the
+**   SQLITE_IOERR_XXX sub-codes.
 **
 ** dbSize, dbOrigSize, dbFileSize
 **
@@ -418,10 +419,7 @@ struct PagerSavepoint {
 **   out from the cache to the actual file on disk. Or if the image has been
 **   truncated by an incremental-vacuum operation. The Pager.dbOrigSize variable
 **   contains the number of pages in the database image when the current
-**   transaction was opened. The contents of all three of these variables is
-**   only guaranteed to be correct if the boolean Pager.dbSizeValid is true.
-**
-**   TODO: Under what conditions is dbSizeValid set? Cleared?
+**   transaction was opened. 
 **
 ** changeCountDone
 **
@@ -719,6 +717,7 @@ static int assert_pager_state(Pager *p){
   ** on the file.
   */
   assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );
+  assert( p->eLock!=PENDING_LOCK );
 
   switch( p->eState ){
     case PAGER_NONE:
@@ -899,16 +898,22 @@ static int write32bits(sqlite3_file *fd, i64 offset, u32 val){
 }
 
 /*
-** This function and pagerLockDb() are wrappers around sqlite3OsLock() and
-** sqlite3OsUnlock() that set the Pager.eLock variable to reflect the
-** current lock held on the database file.
+** Unlock the database file to level eLock, which must be either NO_LOCK
+** or SHARED_LOCK. Regardless of whether or not the call to xUnlock()
+** succeeds, set the Pager.eLock variable to match the (attempted) new lock.
+**
+** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is
+** called, do not modify it. See the comment above the #define of 
+** UNKNOWN_LOCK for an explanation of this.
 */
 static int pagerUnlockDb(Pager *pPager, int eLock){
   int rc = SQLITE_OK;
+
   assert( !pPager->exclusiveMode );
+  assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
+  assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
   if( isOpen(pPager->fd) ){
     assert( pPager->eLock>=eLock );
-    assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
     rc = sqlite3OsUnlock(pPager->fd, eLock);
     if( pPager->eLock!=UNKNOWN_LOCK ){
       pPager->eLock = eLock;
@@ -918,12 +923,21 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
   return rc;
 }
 
+/*
+** Lock the database file to level eLock, which must be either SHARED_LOCK,
+** RESERVED_LOCK or EXCLUSIVE_LOCK. If the caller is successful, set the
+** Pager.eLock variable to the new locking state. 
+**
+** Except, if Pager.eLock is set to UNKNOWN_LOCK when this function is 
+** called, do not modify it unless the new locking state is EXCLUSIVE_LOCK. 
+** See the comment above the #define of UNKNOWN_LOCK for an explanation 
+** of this.
+*/
 static int pagerLockDb(Pager *pPager, int eLock){
-  int rc;
+  int rc = SQLITE_OK;
+
   assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
-  if( pPager->eLock>=eLock && pPager->eLock!=UNKNOWN_LOCK ){
-    rc = SQLITE_OK;
-  }else{
+  if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
     rc = sqlite3OsLock(pPager->fd, eLock);
     if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
       pPager->eLock = eLock;
@@ -1583,6 +1597,11 @@ static void pager_unlock(Pager *pPager){
       sqlite3OsClose(pPager->jfd);
     }
 
+    /* If the pager is in the ERROR state and the call to unlock the database
+    ** file fails, set the current lock to UNKNOWN_LOCK. See the comment
+    ** above the #define for UNKNOWN_LOCK for an explanation of why this
+    ** is necessary.
+    */
     rc = pagerUnlockDb(pPager, NO_LOCK);
     if( rc!=SQLITE_OK && pPager->eState==PAGER_ERROR ){
       pPager->eLock = UNKNOWN_LOCK;
@@ -2750,7 +2769,7 @@ static int pagerBeginReadTransaction(Pager *pPager){
   int changed = 0;                /* True if cache must be reset */
 
   assert( pagerUseWal(pPager) );
-  assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_SHARED );
+  assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_READER );
 
   /* sqlite3WalEndReadTransaction() was not called for the previous
   ** transaction in locking_mode=EXCLUSIVE.  So call it now.  If we
@@ -3284,7 +3303,7 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){
 ** this is considered a 1 page file.
 */
 int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
-  assert( pPager->eState>=PAGER_SHARED );
+  assert( pPager->eState>=PAGER_READER );
   assert( pPager->eState!=PAGER_WRITER_FINISHED );
   *pnPage = (int)pPager->dbSize;
   return SQLITE_OK;
@@ -3308,11 +3327,6 @@ int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
 static int pager_wait_on_lock(Pager *pPager, int locktype){
   int rc;                              /* Return code */
 
-  /* The OS lock values must be the same as the Pager lock values */
-  assert( PAGER_SHARED==SHARED_LOCK );
-  assert( PAGER_RESERVED==RESERVED_LOCK );
-  assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
-
   /* Check that this is either a no-op (because the requested lock is 
   ** already held, or one of the transistions that the busy-handler
   ** may be invoked during, according to the comment above
@@ -4424,7 +4438,7 @@ int sqlite3PagerSharedLock(Pager *pPager){
     if( pPager->noReadlock==0 ){
       rc = pager_wait_on_lock(pPager, SHARED_LOCK);
       if( rc!=SQLITE_OK ){
-        assert( pPager->eLock==PAGER_UNLOCK );
+        assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
         goto failed;
       }
     }
@@ -5393,7 +5407,7 @@ int sqlite3PagerExclusiveLock(Pager *pPager){
   );
   assert( assert_pager_state(pPager) );
   if( 0==pagerUseWal(pPager) ){
-    rc = pager_wait_on_lock(pPager, PAGER_EXCLUSIVE);
+    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
   }
   return rc;
 }