/ Check-in [77eaab6f]
Login
Overview
Comment:Fix some problems with error recovery introduced while reworking pager state.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:77eaab6f77c53cc4f429b65dfcf287ad6084c2da
User & Date: dan 2010-08-04 19:14:22
Context
2010-08-05
15:30
Fixes for error handling with temp databases. And for errors that occur within OS locking primitives. check-in: f99a902f user: dan tags: experimental
2010-08-04
19:14
Fix some problems with error recovery introduced while reworking pager state. check-in: 77eaab6f user: dan tags: experimental
2010-08-03
18:29
Merge trunk changes into experimental branch. check-in: 15368a9f user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

123
124
125
126
127
128
129
130


















131
132
133
134
135
136
137
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

274
275
276
277
278
279
280
...
712
713
714
715
716
717
718





719
720
721
722
723
724
725
...
887
888
889
890
891
892
893

894
895
896
897
898


899











900






901
902
903
904
905
906
907
....
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
....
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734


1735
1736
1737
1738
1739
1740
1741
....
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
....
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
....
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
....
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
....
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
....
4404
4405
4406
4407
4408
4409
4410
4411
4412

4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
....
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
....
4445
4446
4447
4448
4449
4450
4451
4452

4453

4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
....
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
....
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
....
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
....
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
6279
....
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
....
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
6453
** struct as its argument.
*/
#define PAGERID(p) ((int)(p->fd))
#define FILEHANDLEID(fd) ((int)fd)

/*
** The Pager.eState variable stores the current 'state' of a pager. A
** pager may be in any one of the following six states:


















**
**  NONE:
**
**    The pager starts up in this state. Nothing is guaranteed in this
**    state - the file may or may not be locked and the database size is
**    unknown. The database may not be read or written.
**
................................................................................
**    Condition (3) is necessary because it can be triggered by a read-only
**    statement executed within a transaction. In this case, if the error
**    code were simply returned to the user, the b-tree layer would not
**    automatically attempt a rollback, as it assumes that an error in a
**    read-only statement cannot leave the pager in an internally inconsistent 
**    state.
**    
**
** State diagram:
**
**                            NONE <------+------+
**                              |         |      |
**                              V         |      |
**               +---------> READER-------+      |
**               |              |                |
**               |              V                |
**               |<-------WRITER_INITIAL-----> ERROR
**               |              |                ^  
**               |              V                |
**               |<------WRITER_CACHEMOD-------->|
**               |              |                |
**               |              V                |
**               |<-------WRITER_DBMOD---------->|
**               |              |                |
**               |              V                |
**               +<------WRITER_FINISHED-------->+
**
**
** State transitions and the [function] that performs each:
** 
**   NONE              -> READER              [PagerSharedLock]
**   READER            -> WRITER_INITIAL      [PagerBegin]
**   WRITER_INITIAL    -> WRITER_CACHEMOD     [pager_open_journal]
**   WRITER_CACHEMOD   -> WRITER_DBMOD        [syncJournal]
**   WRITER_DBMOD      -> WRITER_FINISHED     [PagerCommitPhaseOne]
** 
**   WRITER_INITIAL    -> READER              [pager_end_transaction]
**   WRITER_CACHEMOD   -> READER              [pager_end_transaction]
**   WRITER_DBMOD      -> READER              [pager_end_transaction]
**   WRITER_FINISHED   -> READER              [pager_end_transaction]
** 
**   READER            -> NONE                [pager_unlock]

**
** Notes:
**
**   * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
**     connection is open in WAL mode. A WAL connection is always in one
**     of the first four states.
**
................................................................................
  */
  assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
  assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );

  /* Check that MEMDB implies noSync. */
  assert( !MEMDB || p->noSync );






  switch( p->eState ){
    case PAGER_NONE:
      assert( !MEMDB );
      assert( !p->tempFile );
      assert( pPager->errCode==SQLITE_OK );
      assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
      break;
................................................................................
  return sqlite3OsWrite(fd, ac, 4, offset);
}

/*
** If file pFd is open, call sqlite3OsUnlock() on it.
*/
static int osUnlock(Pager *pPager, int eLock){

  if( !isOpen(pPager->fd) ){
    return SQLITE_OK;
  }
  assert( pPager->eLock>=eLock );
  assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );


  pPager->eLock = eLock;











  return sqlite3OsUnlock(pPager->fd, eLock);






}

/*
** This function determines whether or not the atomic-write optimization
** can be used with this pager. The optimization can be used if:
**
**  (a) the value returned by OsDeviceCharacteristics() indicates that
................................................................................
  }

  /* If Pager.errCode is set, the contents of the pager cache cannot be
  ** trusted. Now that there are no outstanding references to the pager,
  ** it can safely move back to PAGER_NONE state. This happens in both
  ** normal and exclusive-locking mode.
  */
  if( pPager->errCode ){
    pager_reset(pPager);
    pPager->changeCountDone = 0;
    pPager->eState = PAGER_NONE;
    pPager->errCode = SQLITE_OK;
  }
}

................................................................................
  if( pagerUseWal(pPager) ){
    /* Drop the WAL write-lock, if any. Also, if the connection was in 
    ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE 
    ** lock held on the database file.
    */
    rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
    assert( rc2==SQLITE_OK );
    if( !pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, 0) ){
      rc2 = osUnlock(pPager, SHARED_LOCK);
    }
  }else if( !pPager->exclusiveMode ){


    rc2 = osUnlock(pPager, SHARED_LOCK);
    pPager->changeCountDone = 0;
  }
  pPager->eState = PAGER_READER;
  pPager->setMaster = 0;

  return (rc==SQLITE_OK?rc2:rc);
................................................................................
  assert( pPager->eState>=PAGER_SHARED );
  assert( pPager->eState!=PAGER_WRITER_FINISHED );
  *pnPage = (int)pPager->dbSize;
  return SQLITE_OK;
}


static int pagerLock(Pager *pPager, int eLock){
  int rc;
  assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
  if( pPager->eLock>=eLock ){
    rc = SQLITE_OK;
  }else{
    rc = sqlite3OsLock(pPager->fd, eLock);
    if( rc==SQLITE_OK ){
      pPager->eLock = eLock;
    }
  }
  return rc;
}

/*
** Try to obtain a lock of type locktype on the database file. If
** a similar or greater lock is already held, this function is a no-op
** (returning SQLITE_OK immediately).
**
** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke 
** the busy callback if the lock is currently not available. Repeat 
................................................................................
       || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
  );

  if( pPager->eLock>=locktype ){
    rc = SQLITE_OK;
  }else{
    do {
      rc = sqlite3OsLock(pPager->fd, locktype);
    }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );
    if( rc==SQLITE_OK ){
      pPager->eLock = (u8)locktype;
      IOTRACE(("LOCK %p %d\n", pPager, locktype))
    }
  }
  return rc;
}

/*
** Function assertTruncateConstraint(pPager) checks that one of the 
** following is true for all dirty pages currently in the page-cache:
................................................................................
  ));

  *pExists = 0;
  if( !jrnlOpen ){
    rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
  }
  if( rc==SQLITE_OK && exists ){
    int locked;                 /* True if some process holds a RESERVED lock */

    /* Race condition here:  Another process might have been holding the
    ** the RESERVED lock and have a journal open at the sqlite3OsAccess() 
    ** call above, but then delete the journal and drop the lock before
    ** we get to the following sqlite3OsCheckReservedLock() call.  If that
    ** is the case, this routine might think there is a hot journal when
    ** in fact there is none.  This results in a false-positive which will
................................................................................
      ** a RESERVED lock to avoid race conditions and to avoid violating
      ** [H33020].
      */
      rc = pagerPagecount(pPager, &nPage);
      if( rc==SQLITE_OK ){
        if( nPage==0 ){
          sqlite3BeginBenignMalloc();
          if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){
            sqlite3OsDelete(pVfs, pPager->zJournal, 0);
            sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
          }
          sqlite3EndBenignMalloc();
        }else{
          /* The journal file exists and no other connection has a reserved
          ** or greater lock on the database file. Now check that there is
          ** at least one non-zero bytes at the start of the journal file.
          ** If there is, then we consider this journal to be hot. If not, 
................................................................................
**
** Otherwise, if everything is successful, SQLITE_OK is returned. If an
** IO error occurs while locking the database, checking for a hot-journal
** file or rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager){
  int rc = SQLITE_OK;                /* Return code */
  int isErrorReset = 0;              /* True if recovering from error state */

  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** be NONE or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.
  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_READER );
  assert( assert_pager_state(pPager) );
  if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }

  if( !pagerUseWal(pPager) && pPager->eState==PAGER_NONE ){
    sqlite3_vfs * const pVfs = pPager->pVfs;

    assert( !MEMDB && !pPager->tempFile );
    assert( pPager->noReadlock==0 || pPager->readOnly );

    if( pPager->noReadlock==0 ){
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);
      if( rc!=SQLITE_OK ){
................................................................................
        goto failed;
      }
    }

    /* 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 ){
      rc = hasHotJournal(pPager, &isErrorReset);

      if( rc!=SQLITE_OK ){
        goto failed;
      }
    }
    if( isErrorReset ){
      /* 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 the 
      ** hot-journal back.
      ** 
................................................................................
      ** other process attempting to access the database file will get to 
      ** this point in the code and fail to obtain its own EXCLUSIVE lock 
      ** on the database file.
      **
      ** Unless the pager is in locking_mode=exclusive mode, the lock is
      ** downgraded to SHARED_LOCK before this function returns.
      */
      rc = pagerLock(pPager, EXCLUSIVE_LOCK);
      if( rc!=SQLITE_OK ){
        goto failed;
      }
 
      /* If it is not already open and the file exists on disk, open the 
      ** journal for read/write access. Write access is required because 
      ** in exclusive-access mode the file descriptor will be kept open 
................................................................................
      ** 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.
      */
      if( !isOpen(pPager->jfd) ){
        int res;

        rc = sqlite3OsAccess(pVfs,pPager->zJournal,SQLITE_ACCESS_EXISTS,&res);

        if( rc==SQLITE_OK ){
          if( res ){
            int fout = 0;
            int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
            assert( !pPager->tempFile );
            rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
            assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
            if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
              rc = SQLITE_CANTOPEN_BKPT;
              sqlite3OsClose(pPager->jfd);
            }
          }
        }
      }
      if( rc!=SQLITE_OK ){
        goto failed;
      }

................................................................................
** transaction and unlock the pager.
**
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
** the rollback journal, the unlock is not performed and there is
** nothing to rollback, so this routine is a no-op.
*/ 
static void pagerUnlockIfUnused(Pager *pPager){
  if( (sqlite3PcacheRefCount(pPager->pPCache)==0)
   && (!pPager->exclusiveMode || pPager->journalOff>0) 
  ){
    pagerUnlockAndRollback(pPager);
  }
}

/*
** Acquire a reference to page number pgno in pager pPager (a page
** reference has type DbPage*). If the requested reference is 
................................................................................
    assert( pPager->pInJournal==0 );

    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
      if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
        rc = pagerLock(pPager, EXCLUSIVE_LOCK);
        if( rc!=SQLITE_OK ){
          return rc;
        }
        sqlite3WalExclusiveMode(pPager->pWal, 1);
      }

      /* Grab the write lock on the log file. If successful, upgrade to
................................................................................
      rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
      rc = pagerLock(pPager, RESERVED_LOCK);
      if( rc==SQLITE_OK && exFlag ){
        rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
      }
    }


    if( rc==SQLITE_OK ){
................................................................................
      int rc = SQLITE_OK;
      int state = pPager->eState;
      if( state==PAGER_NONE ){
        rc = sqlite3PagerSharedLock(pPager);
      }
      if( pPager->eState==PAGER_READER ){
        assert( rc==SQLITE_OK );
        rc = sqlite3OsLock(pPager->fd, RESERVED_LOCK);
      }
      if( rc==SQLITE_OK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
      if( rc==SQLITE_OK && state==PAGER_READER ){
        sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
      }else if( state==PAGER_NONE ){
        pager_unlock(pPager);
      }
      assert( state==pPager->eState );
    }
  }

................................................................................

  /* 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 = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED);
    if( rc==SQLITE_OK ){
      rc = sqlite3OsAccess(
          pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
      );
    }
    if( rc==SQLITE_OK && logexists ){
      rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
................................................................................
    }
  }
    
  /* 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 = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE);
    if( rc==SQLITE_OK ){
      rc = sqlite3WalClose(pPager->pWal,
                           (pPager->noSync ? 0 : pPager->sync_flags), 
        pPager->pageSize, (u8*)pPager->pTmpSpace
      );
      pPager->pWal = 0;
    }else{
      /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
      ** that we did get back to SHARED. */
      sqlite3OsUnlock(pPager->fd, SQLITE_LOCK_SHARED);
    }
  }
  return rc;
}

#ifdef SQLITE_HAS_CODEC
/*







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









|
|
<
<


>







 







>
>
>
>
>







 







>
|
<
<
|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>







 







|







 







<
<
|
|
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|

<
<
<
<







 







|







 







|

|







 







<












|







 







|
|
>
|
|
|
<
|







 







|







 







|
>
|
>
|
<
|
|
|
|
|
|
|
|
<







 







|
<
<







 







|







 







|







 







|





|







 







|







 







|









|







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
...
250
251
252
253
254
255
256




















257
258
259
260
261
262
263
264
265
266
267


268
269
270
271
272
273
274
275
276
277
...
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
...
889
890
891
892
893
894
895
896
897


898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
....
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
....
1744
1745
1746
1747
1748
1749
1750


1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
....
3261
3262
3263
3264
3265
3266
3267














3268
3269
3270
3271
3272
3273
3274
....
3297
3298
3299
3300
3301
3302
3303
3304
3305




3306
3307
3308
3309
3310
3311
3312
....
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
....
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
....
4377
4378
4379
4380
4381
4382
4383

4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
....
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417

4418
4419
4420
4421
4422
4423
4424
4425
....
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
....
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457

4458
4459
4460
4461
4462
4463
4464
4465

4466
4467
4468
4469
4470
4471
4472
....
4578
4579
4580
4581
4582
4583
4584
4585


4586
4587
4588
4589
4590
4591
4592
....
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
....
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
....
6258
6259
6260
6261
6262
6263
6264
6265
6266
6267
6268
6269
6270
6271
6272
6273
6274
6275
6276
6277
6278
....
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
....
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
6450
6451
6452
** struct as its argument.
*/
#define PAGERID(p) ((int)(p->fd))
#define FILEHANDLEID(fd) ((int)fd)

/*
** The Pager.eState variable stores the current 'state' of a pager. A
** pager may be in any one of the seven states shown in the following
** state diagram.
**
**                            NONE <------+------+
**                              |         |      |
**                              V         |      |
**               +---------> READER-------+      |
**               |              |                |
**               |              V                |
**               |<-------WRITER_INITIAL-----> ERROR
**               |              |                ^  
**               |              V                |
**               |<------WRITER_CACHEMOD-------->|
**               |              |                |
**               |              V                |
**               |<-------WRITER_DBMOD---------->|
**               |              |                |
**               |              V                |
**               +<------WRITER_FINISHED-------->+
**
**  NONE:
**
**    The pager starts up in this state. Nothing is guaranteed in this
**    state - the file may or may not be locked and the database size is
**    unknown. The database may not be read or written.
**
................................................................................
**    Condition (3) is necessary because it can be triggered by a read-only
**    statement executed within a transaction. In this case, if the error
**    code were simply returned to the user, the b-tree layer would not
**    automatically attempt a rollback, as it assumes that an error in a
**    read-only statement cannot leave the pager in an internally inconsistent 
**    state.
**    




















**
** State transitions and the [function] that performs each:
** 
**   NONE              -> READER              [PagerSharedLock]
**   READER            -> WRITER_INITIAL      [PagerBegin]
**   WRITER_INITIAL    -> WRITER_CACHEMOD     [pager_open_journal]
**   WRITER_CACHEMOD   -> WRITER_DBMOD        [syncJournal]
**   WRITER_DBMOD      -> WRITER_FINISHED     [PagerCommitPhaseOne]
** 
**   WRITER_***        -> READER              [pager_end_transaction]
**   WRITER_***        -> ERROR               [pager_error]


** 
**   READER            -> NONE                [pager_unlock]
**   ERROR             -> NONE                [pager_unlock]
**
** Notes:
**
**   * A pager is never in WRITER_DBMOD or WRITER_FINISHED state if the
**     connection is open in WAL mode. A WAL connection is always in one
**     of the first four states.
**
................................................................................
  */
  assert( p->journalMode==PAGER_JOURNALMODE_OFF || p->useJournal );
  assert( p->journalMode!=PAGER_JOURNALMODE_OFF || !isOpen(p->jfd) );

  /* Check that MEMDB implies noSync. */
  assert( !MEMDB || p->noSync );

  /* If changeCountDone is set, a RESERVED lock or greater must be held
  ** on the file.
  */
  assert( pPager->changeCountDone==0 || pPager->eLock>=RESERVED_LOCK );

  switch( p->eState ){
    case PAGER_NONE:
      assert( !MEMDB );
      assert( !p->tempFile );
      assert( pPager->errCode==SQLITE_OK );
      assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
      break;
................................................................................
  return sqlite3OsWrite(fd, ac, 4, offset);
}

/*
** If file pFd is open, call sqlite3OsUnlock() on it.
*/
static int osUnlock(Pager *pPager, int eLock){
  int rc = SQLITE_OK;
  if( isOpen(pPager->fd) ){


    assert( pPager->eLock>=eLock );
    assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
    rc = sqlite3OsUnlock(pPager->fd, eLock);
    if( rc==SQLITE_OK ){
      pPager->eLock = eLock;
    }
  }
  return rc;
}

static int osLock(Pager *pPager, int eLock){
  int rc;
  assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
  if( pPager->eLock>=eLock ){
    rc = SQLITE_OK;
  }else{
    rc = sqlite3OsLock(pPager->fd, eLock);
    if( rc==SQLITE_OK ){
      pPager->eLock = eLock;
      IOTRACE(("LOCK %p %d\n", pPager, locktype))
    }
  }
  return rc;
}

/*
** This function determines whether or not the atomic-write optimization
** can be used with this pager. The optimization can be used if:
**
**  (a) the value returned by OsDeviceCharacteristics() indicates that
................................................................................
  }

  /* If Pager.errCode is set, the contents of the pager cache cannot be
  ** trusted. Now that there are no outstanding references to the pager,
  ** it can safely move back to PAGER_NONE state. This happens in both
  ** normal and exclusive-locking mode.
  */
  if( pPager->errCode && !pPager->tempFile ){
    pager_reset(pPager);
    pPager->changeCountDone = 0;
    pPager->eState = PAGER_NONE;
    pPager->errCode = SQLITE_OK;
  }
}

................................................................................
  if( pagerUseWal(pPager) ){
    /* Drop the WAL write-lock, if any. Also, if the connection was in 
    ** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE 
    ** lock held on the database file.
    */
    rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
    assert( rc2==SQLITE_OK );


  }
  if( !pPager->exclusiveMode 
   && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
  ){
    rc2 = osUnlock(pPager, SHARED_LOCK);
    pPager->changeCountDone = 0;
  }
  pPager->eState = PAGER_READER;
  pPager->setMaster = 0;

  return (rc==SQLITE_OK?rc2:rc);
................................................................................
  assert( pPager->eState>=PAGER_SHARED );
  assert( pPager->eState!=PAGER_WRITER_FINISHED );
  *pnPage = (int)pPager->dbSize;
  return SQLITE_OK;
}
















/*
** Try to obtain a lock of type locktype on the database file. If
** a similar or greater lock is already held, this function is a no-op
** (returning SQLITE_OK immediately).
**
** Otherwise, attempt to obtain the lock using sqlite3OsLock(). Invoke 
** the busy callback if the lock is currently not available. Repeat 
................................................................................
       || (pPager->eLock==RESERVED_LOCK && locktype==EXCLUSIVE_LOCK)
  );

  if( pPager->eLock>=locktype ){
    rc = SQLITE_OK;
  }else{
    do {
      rc = osLock(pPager, locktype);
    }while( rc==SQLITE_BUSY && pPager->xBusyHandler(pPager->pBusyHandlerArg) );




  }
  return rc;
}

/*
** Function assertTruncateConstraint(pPager) checks that one of the 
** following is true for all dirty pages currently in the page-cache:
................................................................................
  ));

  *pExists = 0;
  if( !jrnlOpen ){
    rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
  }
  if( rc==SQLITE_OK && exists ){
    int locked = 0;             /* True if some process holds a RESERVED lock */

    /* Race condition here:  Another process might have been holding the
    ** the RESERVED lock and have a journal open at the sqlite3OsAccess() 
    ** call above, but then delete the journal and drop the lock before
    ** we get to the following sqlite3OsCheckReservedLock() call.  If that
    ** is the case, this routine might think there is a hot journal when
    ** in fact there is none.  This results in a false-positive which will
................................................................................
      ** a RESERVED lock to avoid race conditions and to avoid violating
      ** [H33020].
      */
      rc = pagerPagecount(pPager, &nPage);
      if( rc==SQLITE_OK ){
        if( nPage==0 ){
          sqlite3BeginBenignMalloc();
          if( osLock(pPager, RESERVED_LOCK)==SQLITE_OK ){
            sqlite3OsDelete(pVfs, pPager->zJournal, 0);
            osUnlock(pPager, SHARED_LOCK);
          }
          sqlite3EndBenignMalloc();
        }else{
          /* The journal file exists and no other connection has a reserved
          ** or greater lock on the database file. Now check that there is
          ** at least one non-zero bytes at the start of the journal file.
          ** If there is, then we consider this journal to be hot. If not, 
................................................................................
**
** Otherwise, if everything is successful, SQLITE_OK is returned. If an
** IO error occurs while locking the database, checking for a hot-journal
** file or rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager){
  int rc = SQLITE_OK;                /* Return code */


  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** be NONE or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.
  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_READER );
  assert( assert_pager_state(pPager) );
  if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }

  if( !pagerUseWal(pPager) && pPager->eState==PAGER_NONE ){
    int bHotJournal = 1;          /* True if there exists a hot journal-file */

    assert( !MEMDB && !pPager->tempFile );
    assert( pPager->noReadlock==0 || pPager->readOnly );

    if( pPager->noReadlock==0 ){
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);
      if( rc!=SQLITE_OK ){
................................................................................
        goto failed;
      }
    }

    /* 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( pPager->eLock<=SHARED_LOCK ){
      rc = hasHotJournal(pPager, &bHotJournal);
    }
    if( rc!=SQLITE_OK ){
      goto failed;
    }

    if( bHotJournal ){
      /* 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 the 
      ** hot-journal back.
      ** 
................................................................................
      ** other process attempting to access the database file will get to 
      ** this point in the code and fail to obtain its own EXCLUSIVE lock 
      ** on the database file.
      **
      ** Unless the pager is in locking_mode=exclusive mode, the lock is
      ** downgraded to SHARED_LOCK before this function returns.
      */
      rc = osLock(pPager, EXCLUSIVE_LOCK);
      if( rc!=SQLITE_OK ){
        goto failed;
      }
 
      /* If it is not already open and the file exists on disk, open the 
      ** journal for read/write access. Write access is required because 
      ** in exclusive-access mode the file descriptor will be kept open 
................................................................................
      ** 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.
      */
      if( !isOpen(pPager->jfd) ){
        sqlite3_vfs * const pVfs = pPager->pVfs;
        int bExists;              /* True if journal file exists */
        rc = sqlite3OsAccess(
            pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
        if( rc==SQLITE_OK && bExists ){

          int fout = 0;
          int f = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
          assert( !pPager->tempFile );
          rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &fout);
          assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
          if( rc==SQLITE_OK && fout&SQLITE_OPEN_READONLY ){
            rc = SQLITE_CANTOPEN_BKPT;
            sqlite3OsClose(pPager->jfd);

          }
        }
      }
      if( rc!=SQLITE_OK ){
        goto failed;
      }

................................................................................
** transaction and unlock the pager.
**
** Except, in locking_mode=EXCLUSIVE when there is nothing to in
** the rollback journal, the unlock is not performed and there is
** nothing to rollback, so this routine is a no-op.
*/ 
static void pagerUnlockIfUnused(Pager *pPager){
  if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){


    pagerUnlockAndRollback(pPager);
  }
}

/*
** Acquire a reference to page number pgno in pager pPager (a page
** reference has type DbPage*). If the requested reference is 
................................................................................
    assert( pPager->pInJournal==0 );

    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
      if( pPager->exclusiveMode && sqlite3WalExclusiveMode(pPager->pWal, -1) ){
        rc = osLock(pPager, EXCLUSIVE_LOCK);
        if( rc!=SQLITE_OK ){
          return rc;
        }
        sqlite3WalExclusiveMode(pPager->pWal, 1);
      }

      /* Grab the write lock on the log file. If successful, upgrade to
................................................................................
      rc = sqlite3WalBeginWriteTransaction(pPager->pWal);
    }else{
      /* Obtain a RESERVED lock on the database file. If the exFlag parameter
      ** is true, then immediately upgrade this to an EXCLUSIVE lock. The
      ** busy-handler callback can be used when upgrading to the EXCLUSIVE
      ** lock, but not when obtaining the RESERVED lock.
      */
      rc = osLock(pPager, RESERVED_LOCK);
      if( rc==SQLITE_OK && exFlag ){
        rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
      }
    }


    if( rc==SQLITE_OK ){
................................................................................
      int rc = SQLITE_OK;
      int state = pPager->eState;
      if( state==PAGER_NONE ){
        rc = sqlite3PagerSharedLock(pPager);
      }
      if( pPager->eState==PAGER_READER ){
        assert( rc==SQLITE_OK );
        rc = osLock(pPager, RESERVED_LOCK);
      }
      if( rc==SQLITE_OK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }
      if( rc==SQLITE_OK && state==PAGER_READER ){
        osUnlock(pPager, SHARED_LOCK);
      }else if( state==PAGER_NONE ){
        pager_unlock(pPager);
      }
      assert( state==pPager->eState );
    }
  }

................................................................................

  /* 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 = osLock(pPager, SHARED_LOCK);
    if( rc==SQLITE_OK ){
      rc = sqlite3OsAccess(
          pPager->pVfs, pPager->zWal, SQLITE_ACCESS_EXISTS, &logexists
      );
    }
    if( rc==SQLITE_OK && logexists ){
      rc = sqlite3WalOpen(pPager->pVfs, pPager->fd,
................................................................................
    }
  }
    
  /* 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 = osLock(pPager, EXCLUSIVE_LOCK);
    if( rc==SQLITE_OK ){
      rc = sqlite3WalClose(pPager->pWal,
                           (pPager->noSync ? 0 : pPager->sync_flags), 
        pPager->pageSize, (u8*)pPager->pTmpSpace
      );
      pPager->pWal = 0;
    }else{
      /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock
      ** that we did get back to SHARED. */
      osUnlock(pPager, SQLITE_LOCK_SHARED);
    }
  }
  return rc;
}

#ifdef SQLITE_HAS_CODEC
/*