/ Check-in [0a636798]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Incremental checkin on pager state refactoring.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:0a636798bdb6961a47327091715b254f79add823
User & Date: dan 2010-08-03 06:42:40
Context
2010-08-03
12:48
Add state diagram to comments in experimental version of pager.c. check-in: 16dcf5a6 user: dan tags: experimental
06:42
Incremental checkin on pager state refactoring. check-in: 0a636798 user: dan tags: experimental
2010-08-02
14:32
Experimental refactoring of the Pager object state. This version is surely buggy. check-in: 03a24051 user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

126
127
128
129
130
131
132




133
134

135
136





137
138
139

140
141
142
143
144
145
146
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184













185
186
187
188
189
190
191
...
421
422
423
424
425
426
427
428
429

430
431
432
433
434
435
436
...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
....
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
....
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
....
2585
2586
2587
2588
2589
2590
2591

2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604


2605
2606
2607
2608
2609
2610














2611
2612










2613
2614
2615
2616
2617
2618
2619
....
2631
2632
2633
2634
2635
2636
2637
2638
2639

2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
....
2956
2957
2958
2959
2960
2961
2962






2963
2964
2965
2966

2967

2968
2969
2970
2971
2972
2973
2974
....
3084
3085
3086
3087
3088
3089
3090



3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
....
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
....
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
....
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
....
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
....
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
....
4264
4265
4266
4267
4268
4269
4270
4271



4272


4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283

4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
....
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
....
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430

4431
4432
4433
4434
4435
4436
4437
4438
4439
....
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454









4455
4456
4457
4458
4459


4460
4461
4462
4463
4464
4465
4466
....
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
....
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
....
5688
5689
5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
....
6006
6007
6008
6009
6010
6011
6012
6013
6014
6015
6016
6017
6018
6019
6020
....
6283
6284
6285
6286
6287
6288
6289
6290
6291
6292
6293
6294
6295
6296
6297
6298
6299
6300
6301
6302

6303


6304
6305
6306
6307
6308
6309
6310
6311
6312
6313
6314
6315

6316
6317
6318
6319
6320
6321
6322
6323
6324
6325
#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:




**    * No read or write transaction is active.
**    * Any lock, or no lock at all, may be held on the database file.

**
**  READER:





**    * A read transaction is active.
**    * A SHARED or greater lock is held on the database file.
**    * The dbSize variable is valid.

**
**  WRITER_INITIAL:
**    * A write transaction is active.
**    * A RESERVED or greater lock is held on the database file.
**    * The dbSize, dbOrigSize and dbFileSize variables are all valid.
**    * The contents of the pager cache have not been modified.
**
................................................................................
**    * A write transaction is active.
**    * An EXCLUSIVE or greater lock is held on the database file.
**    * All writing and syncing of journal and database data has finished.
**      If no error occured, all that remains is to finalize the journal to
**      commit the transaction. If an error did occur, the caller will need
**      to rollback the transaction. 
**
**
** Allowable transitions and the [function] that performe 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]













*/
#define PAGER_NONE                  0
#define PAGER_READER                1
#define PAGER_WRITER_INITIAL        2
#define PAGER_WRITER_CACHEMOD       3
#define PAGER_WRITER_DBMOD          4
#define PAGER_WRITER_FINISHED       5
................................................................................
  ** other variables in this block are described in the comment directly 
  ** above this class definition.
  */
#if 0
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 journalStarted;          /* True if header of journal is synced */
#endif
  u8 dbSizeValid;             /* Set when dbSize is correct */

  u8 eState;                  /* Pager state (NONE, READER, WRITER_INITIAL..) */
  u8 eLock;                   /* Current lock held on database file */

  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSpill;              /* Do not spill the cache when non-zero */
  u8 doNotSyncSpill;          /* Do not do a spill that requires jrnl sync */
................................................................................
  /* Check that MEMDB implies noSync. */
  assert( !MEMDB || p->noSync );

  switch( p->eState ){
    case PAGER_NONE:
      assert( !MEMDB );
      assert( !p->tempFile );
      assert( !pagerUseWal(pPager) );
      break;

    case PAGER_READER:
      assert( p->eLock>=SHARED_LOCK || p->noReadlock );
      break;

    case PAGER_WRITER_INITIAL:
................................................................................
**
** TODO: Why can we not reset the pager while in error state?
*/
static void pager_reset(Pager *pPager){
  if( SQLITE_OK==pPager->errCode ){
    sqlite3BackupRestart(pPager->pBackup);
    sqlite3PcacheClear(pPager->pPCache);
    pPager->dbSizeValid = 0;
  }
}

/*
** Free all structures in the Pager.aSavepoint[] array and set both
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
** if it is open and the pager is not in exclusive mode.
................................................................................
      sqlite3OsClose(pPager->jfd);
    }

    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;
    releaseAllSavepoints(pPager);

    /* If the file is unlocked, somebody else might change it. The
    ** values stored in Pager.dbSize etc. might become invalid if
    ** this happens.  One can argue that this doesn't need to be cleared
    ** until the change-counter check fails in PagerSharedLock().
    ** Clearing the page size cache here is being conservative.
    */
    pPager->dbSizeValid = 0;

    if( pagerUseWal(pPager) ){
      sqlite3WalEndReadTransaction(pPager->pWal);
    }else{
      rc = osUnlock(pPager, NO_LOCK);
    }
    if( rc ){
      pPager->errCode = rc;
................................................................................
** 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) );


  /* 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 ){
    int dummy;
    if( changed ){
      pager_reset(pPager);


      assert( pPager->errCode || pPager->dbSizeValid==0 );
    }
    rc = sqlite3PagerPagecount(pPager, &dummy);
  }
  pPager->eState = PAGER_READER;















  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
................................................................................
** between the xAccess() below and an xDelete() being executed by some 
** other connection.
*/
static int pagerOpenWalIfPresent(Pager *pPager){
  int rc = SQLITE_OK;
  if( !pPager->tempFile ){
    int isWal;                    /* True if WAL file exists */
    int nPage;                    /* Size of the database file */


    assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
    rc = sqlite3PagerPagecount(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 ){
        pager_reset(pPager);
        rc = sqlite3PagerOpenWal(pPager, 0);
        if( rc==SQLITE_OK ){
          rc = pagerBeginReadTransaction(pPager);
        }
      }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
        pPager->journalMode = PAGER_JOURNALMODE_DELETE;
      }
................................................................................
  if( rc==SQLITE_OK ){
    u16 pageSize = *pPageSize;
    assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
    if( (pPager->memDb==0 || pPager->dbSize==0)
     && sqlite3PcacheRefCount(pPager->pPCache)==0 
     && pageSize && pageSize!=pPager->pageSize 
    ){






      char *pNew = (char *)sqlite3PageMalloc(pageSize);
      if( !pNew ){
        rc = SQLITE_NOMEM;
      }else{

        pager_reset(pPager);

        pPager->pageSize = pageSize;
        sqlite3PageFree(pPager->pTmpSpace);
        pPager->pTmpSpace = pNew;
        sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
      }
    }
    *pPageSize = (u16)pPager->pageSize;
................................................................................
**
** Otherwise, if everything is successful, then SQLITE_OK is returned
** and *pnPage is set to the number of pages in the database.
*/
int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
  Pgno nPage = 0;           /* Value to return via *pnPage */




  /* Determine the number of pages in the file. Store this in nPage. */
  if( pPager->dbSizeValid ){
    nPage = pPager->dbSize;
  }else{
    int rc;                 /* Error returned by OsFileSize() */
    i64 n = 0;              /* File size in bytes returned by OsFileSize() */

    if( pagerUseWal(pPager) && pPager->eState!=PAGER_NONE ){
      sqlite3WalDbsize(pPager->pWal, &nPage);
    }

    if( nPage==0 ){
      assert( isOpen(pPager->fd) || pPager->tempFile );
      if( isOpen(pPager->fd) ){
        if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){
          pager_error(pPager, rc);
................................................................................
      }else{
        nPage = (Pgno)(n / pPager->pageSize);
      }
    }
    if( pPager->eState!=PAGER_NONE ){
      pPager->dbSize = nPage;
      pPager->dbFileSize = nPage;
      pPager->dbSizeValid = 1;
    }
  }

  /* If the current number of pages in the file is greater than the 
  ** configured maximum pager number, increase the allowed limit so
  ** that the file can be read.
  */
................................................................................
/*
** Truncate the in-memory database file image to nPage pages. This 
** function does not actually modify the database file on disk. It 
** just sets the internal state of the pager object so that the 
** truncation will be done when the current transaction is committed.
*/
void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
  assert( pPager->dbSizeValid );
  assert( pPager->dbSize>=nPage );
  assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
  pPager->dbSize = nPage;
  assertTruncateConstraint(pPager);
}


................................................................................
  IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))

  pPager->useJournal = (u8)useJournal;
  pPager->noReadlock = (noReadlock && readOnly) ?1:0;
  /* pPager->stmtOpen = 0; */
  /* pPager->stmtInUse = 0; */
  /* pPager->nRef = 0; */
  pPager->dbSizeValid = (u8)memDb;
  /* pPager->stmtSize = 0; */
  /* pPager->stmtJSize = 0; */
  /* pPager->nPage = 0; */
  pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
  /* pPager->state = PAGER_UNLOCK; */
#if 0
  assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
................................................................................
*/
static int hasHotJournal(Pager *pPager, int *pExists){
  sqlite3_vfs * const pVfs = pPager->pVfs;
  int rc = SQLITE_OK;           /* Return code */
  int exists = 1;               /* True if a journal file is present */
  int jrnlOpen = !!isOpen(pPager->jfd);

  assert( pPager!=0 );
  assert( pPager->useJournal );
  assert( isOpen(pPager->fd) );
  assert( pPager->eState<=PAGER_SHARED );

  assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
    SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
  ));

  *pExists = 0;
  if( !jrnlOpen ){
................................................................................
    ** 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
    ** be dealt with by the playback routine.  Ticket #3883.
    */
    rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
    if( rc==SQLITE_OK && !locked ){
      int nPage;

      /* Check the size of the database file. If it consists of 0 pages,
      ** then delete the journal file. See the header comment above for 
      ** the reasoning here.  Delete the obsolete journal file under
      ** a RESERVED lock to avoid race conditions and to avoid violating
      ** [H33020].
      */
      rc = sqlite3PagerPagecount(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);
          }
................................................................................
** 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 */



  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );


  if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }

  /* If this database is in an error-state, now is a chance to clear
  ** the error. Discard the contents of the pager-cache and rollback
  ** any hot journal in the file-system.
  */
  if( pPager->errCode ){
    if( isOpen(pPager->jfd) || pPager->zJournal ){
      isErrorReset = 1;
    }
    pPager->errCode = SQLITE_OK;

    pager_reset(pPager);
  }

  if( pagerUseWal(pPager) ){
    rc = pagerBeginReadTransaction(pPager);
  }else if( pPager->eState==PAGER_NONE || isErrorReset ){
    sqlite3_vfs * const pVfs = pPager->pVfs;
    int isHotJournal = 0;

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

    if( pPager->noReadlock==0 ){
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);
      if( rc!=SQLITE_OK ){
        assert( pPager->eLock==PAGER_UNLOCK );
        return pager_error(pPager, rc);
      }
    }
    pPager->eState = PAGER_READER;

    /* 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, &isHotJournal);
      if( rc!=SQLITE_OK ){
        goto failed;
      }
    }
    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 the 
      ** hot-journal back.
      ** 
................................................................................
      ** the journal before playing it back.
      */
      if( isOpen(pPager->jfd) ){
        rc = pagerSyncHotJournal(pPager);
        if( rc==SQLITE_OK ){
          pPager->eState = PAGER_WRITER_FINISHED;
          rc = pager_playback(pPager, 1);
          pPager->eState = PAGER_READER;
        }
        if( rc!=SQLITE_OK ){
          rc = pager_error(pPager, rc);
          goto failed;
        }
      }else{
        osUnlock(pPager, SHARED_LOCK);
      }

      assert( pPager->eState==PAGER_READER );
      assert( (pPager->eLock==SHARED_LOCK)
           || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
      );
    }

    if( pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0 ){
      /* The shared-lock has just been acquired on the database file
................................................................................
      ** 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.
      */
      int nPage = 0;
      char dbFileVers[sizeof(pPager->dbFileVers)];
      sqlite3PagerPagecount(pPager, &nPage);

      if( pPager->errCode ){
        rc = pPager->errCode;

        goto failed;
      }

      if( nPage>0 ){
        IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
        rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
        if( rc!=SQLITE_OK ){
          goto failed;
        }
................................................................................
        memset(dbFileVers, 0, sizeof(dbFileVers));
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }
    assert( pPager->eState==PAGER_READER );

    /* If there is a WAL file in the file-system, open this database in WAL
    ** mode. Otherwise, the following function call is a no-op.
    */
    rc = pagerOpenWalIfPresent(pPager);
  }










 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);


  }
  return rc;
}

/*
** If the reference count has reached zero, rollback any active
** transaction and unlock the pager.
................................................................................
  
  /* If already in the error state, this function is a no-op.  But on
  ** the other hand, this routine is never called if we are already in
  ** an error state. */
  if( NEVER(pPager->errCode) ) return pPager->errCode;

  if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
    testcase( pPager->dbSizeValid==0 );
    rc = sqlite3PagerPagecount(pPager, &nPage);
    if( rc ) return rc;
    pPager->pInJournal = sqlite3BitvecCreate(nPage);
    if( pPager->pInJournal==0 ){
      return SQLITE_NOMEM;
    }
  
................................................................................
      if( rc==SQLITE_OK ){
        rc = rc2;
      }
    }else{
      rc = pager_playback(pPager, 0);
    }

    if( !MEMDB ){
      pPager->dbSizeValid = 0;
    }

    /* If an error occurs during a ROLLBACK, we can no longer trust the pager
    ** cache. So call pager_error() on the way out to make any error 
    ** persistent.
    */
    rc = pager_error(pPager, rc);
  }
  return rc;
................................................................................
** This routine is used for testing and analysis only.
*/
int *sqlite3PagerStats(Pager *pPager){
  static int a[11];
  a[0] = sqlite3PcacheRefCount(pPager->pPCache);
  a[1] = sqlite3PcachePagecount(pPager->pPCache);
  a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
  a[3] = pPager->dbSizeValid ? (int) pPager->dbSize : -1;
  a[4] = pPager->eState;
  a[5] = pPager->errCode;
  a[6] = pPager->nHit;
  a[7] = pPager->nMiss;
  a[8] = 0;  /* Used to be pPager->nOvfl */
  a[9] = pPager->nRead;
  a[10] = pPager->nWrite;
................................................................................
  pPgOld = pager_lookup(pPager, pgno);
  assert( !pPgOld || pPgOld->nRef==1 );
  if( pPgOld ){
    pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
    if( MEMDB ){
      /* Do not discard pages from an in-memory database since we might
      ** need to rollback later.  Just move the page out of the way. */
      assert( pPager->dbSizeValid );
      sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
    }else{
      sqlite3PcacheDrop(pPgOld);
    }
  }

  origPgno = pPg->pgno;
................................................................................
** 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. *pisOpen 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 *pisOpen to 1 and return SQLITE_OK
** without doing anything.
*/
int sqlite3PagerOpenWal(
  Pager *pPager,                  /* Pager object */
  int *pisOpen                    /* OUT: Set to true if call is a no-op */
){
  int rc = SQLITE_OK;             /* Return code */


  assert( pPager->eState>=PAGER_READER );


  assert( (pisOpen==0 && !pPager->tempFile && !pPager->pWal) || *pisOpen==0 );

  if( !pPager->tempFile && !pPager->pWal ){
    if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN;

    /* Open the connection to the log file. If this operation fails, 
    ** (e.g. due to malloc() failure), unlock the database file and 
    ** return an error code.
    */
    rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
    if( rc==SQLITE_OK ){
      pPager->journalMode = PAGER_JOURNALMODE_WAL;

    }
  }else{
    *pisOpen = 1;
  }

  return rc;
}

/*
** This function is called to close the connection to the log file prior







>
>
>
>


>


>
>
>
>
>
|

|
>







 







<
|













>
>
>
>
>
>
>
>
>
>
>
>
>







 







<

>







 







<







 







<







 







<
<
<
<
<
<
<
<







 







>









|
<
<
|
>
>
|
|
<
|
<

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







 







|

>

|











|







 







>
>
>
>
>
>
|



>

>







 







>
>
>

|






|







 







<







 







<







 







<







 







<


|







 







|







|







 







|
>
>
>

>
>











>



|
<
<

<












<





|




|







 







|









|







 







|

<

<
<
>
|
<







 







<






>
>
>
>
>
>
>
>
>





>
>







 







<







 







<
<
<
<







 







|







 







<







 







|



|




|



>
|
>
>
|











>


|







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
156
157
...
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
444
445
446
447
448
449
450

451
452
453
454
455
456
457
458
459
...
642
643
644
645
646
647
648

649
650
651
652
653
654
655
....
1366
1367
1368
1369
1370
1371
1372

1373
1374
1375
1376
1377
1378
1379
....
1445
1446
1447
1448
1449
1450
1451








1452
1453
1454
1455
1456
1457
1458
....
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615


2616
2617
2618
2619
2620

2621

2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
....
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
....
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
....
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
....
3160
3161
3162
3163
3164
3165
3166

3167
3168
3169
3170
3171
3172
3173
....
3278
3279
3280
3281
3282
3283
3284

3285
3286
3287
3288
3289
3290
3291
....
4106
4107
4108
4109
4110
4111
4112

4113
4114
4115
4116
4117
4118
4119
....
4187
4188
4189
4190
4191
4192
4193

4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
....
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233
4234
....
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337


4338

4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350

4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
....
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
....
4464
4465
4466
4467
4468
4469
4470
4471
4472

4473


4474
4475

4476
4477
4478
4479
4480
4481
4482
....
4484
4485
4486
4487
4488
4489
4490

4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
....
4762
4763
4764
4765
4766
4767
4768

4769
4770
4771
4772
4773
4774
4775
....
5684
5685
5686
5687
5688
5689
5690




5691
5692
5693
5694
5695
5696
5697
....
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
....
6054
6055
6056
6057
6058
6059
6060

6061
6062
6063
6064
6065
6066
6067
....
6330
6331
6332
6333
6334
6335
6336
6337
6338
6339
6340
6341
6342
6343
6344
6345
6346
6347
6348
6349
6350
6351
6352
6353
6354
6355
6356
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
#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.
**
**    * No read or write transaction is active.
**    * Any lock, or no lock at all, may be held on the database file.
**    * The dbSize, dbOrigSize and dbFileSize variables may not be trusted.
**
**  READER:
**    In this state all the requirements for reading the database in 
**    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 read transaction may be active.
**    * 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).
**
**  WRITER_INITIAL:
**    * A write transaction is active.
**    * A RESERVED or greater lock is held on the database file.
**    * The dbSize, dbOrigSize and dbFileSize variables are all valid.
**    * The contents of the pager cache have not been modified.
**
................................................................................
**    * A write transaction is active.
**    * An EXCLUSIVE or greater lock is held on the database file.
**    * All writing and syncing of journal and database data has finished.
**      If no error occured, all that remains is to finalize the journal to
**      commit the transaction. If an error did occur, the caller will need
**      to rollback the transaction. 
**

** Allowable 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.
**
**   * Normally, a connection open in exclusive mode is never in PAGER_NONE
**     state. There are two exceptions: immediately after exclusive-mode has
**     been turned on (and before any read or write transactions are 
**     executed), and when the pager is leaving the "error state".
**
**   * See also: assert_pager_state().
*/
#define PAGER_NONE                  0
#define PAGER_READER                1
#define PAGER_WRITER_INITIAL        2
#define PAGER_WRITER_CACHEMOD       3
#define PAGER_WRITER_DBMOD          4
#define PAGER_WRITER_FINISHED       5
................................................................................
  ** other variables in this block are described in the comment directly 
  ** above this class definition.
  */
#if 0
  u8 state;                   /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
  u8 dbModified;              /* True if there are any changes to the Db */
  u8 journalStarted;          /* True if header of journal is synced */

  u8 dbSizeValid;             /* Set when dbSize is correct */
#endif
  u8 eState;                  /* Pager state (NONE, READER, WRITER_INITIAL..) */
  u8 eLock;                   /* Current lock held on database file */

  u8 changeCountDone;         /* Set after incrementing the change-counter */
  u8 setMaster;               /* True if a m-j name has been written to jrnl */
  u8 doNotSpill;              /* Do not spill the cache when non-zero */
  u8 doNotSyncSpill;          /* Do not do a spill that requires jrnl sync */
................................................................................
  /* Check that MEMDB implies noSync. */
  assert( !MEMDB || p->noSync );

  switch( p->eState ){
    case PAGER_NONE:
      assert( !MEMDB );
      assert( !p->tempFile );

      break;

    case PAGER_READER:
      assert( p->eLock>=SHARED_LOCK || p->noReadlock );
      break;

    case PAGER_WRITER_INITIAL:
................................................................................
**
** TODO: Why can we not reset the pager while in error state?
*/
static void pager_reset(Pager *pPager){
  if( SQLITE_OK==pPager->errCode ){
    sqlite3BackupRestart(pPager->pBackup);
    sqlite3PcacheClear(pPager->pPCache);

  }
}

/*
** Free all structures in the Pager.aSavepoint[] array and set both
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
** if it is open and the pager is not in exclusive mode.
................................................................................
      sqlite3OsClose(pPager->jfd);
    }

    sqlite3BitvecDestroy(pPager->pInJournal);
    pPager->pInJournal = 0;
    releaseAllSavepoints(pPager);









    if( pagerUseWal(pPager) ){
      sqlite3WalEndReadTransaction(pPager->pWal);
    }else{
      rc = osUnlock(pPager, NO_LOCK);
    }
    if( rc ){
      pPager->errCode = rc;
................................................................................
** 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_NONE || pPager->eState==PAGER_SHARED );

  /* 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;
}




/*
** TODO: Description here.
*/
static int pagerPagecount(Pager *pPager, Pgno *pnPage){
  Pgno nPage;                     /* Value to return via *pnPage */

  assert( pPager->eState==PAGER_NONE );
  nPage = sqlite3WalDbsize(pPager->pWal);
  if( nPage==0 ){
    i64 n = 0;                    /* Size of db file in bytes */
    assert( isOpen(pPager->fd) || pPager->tempFile );
    if( isOpen(pPager->fd) ){
      int rc = sqlite3OsFileSize(pPager->fd, &n);
      if( rc!=SQLITE_OK ){
        return rc;
      }
    }
    nPage = (Pgno)(n / pPager->pageSize);
    if( nPage==0 && n>0 ){
      nPage = 1;
    }
  }
  *pnPage = nPage;
  return SQLITE_OK;
}


/*
** 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
................................................................................
** between the xAccess() below and an xDelete() being executed by some 
** other connection.
*/
static int pagerOpenWalIfPresent(Pager *pPager){
  int rc = SQLITE_OK;
  if( !pPager->tempFile ){
    int isWal;                    /* True if WAL file exists */
    Pgno nPage;                   /* Size of the database file */

    assert( pPager->eState==PAGER_NONE );
    assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
    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 ){
        assert( sqlite3PcachePagecount(pPager->pPCache)==0 );
        rc = sqlite3PagerOpenWal(pPager, 0);
        if( rc==SQLITE_OK ){
          rc = pagerBeginReadTransaction(pPager);
        }
      }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
        pPager->journalMode = PAGER_JOURNALMODE_DELETE;
      }
................................................................................
  if( rc==SQLITE_OK ){
    u16 pageSize = *pPageSize;
    assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) );
    if( (pPager->memDb==0 || pPager->dbSize==0)
     && sqlite3PcacheRefCount(pPager->pPCache)==0 
     && pageSize && pageSize!=pPager->pageSize 
    ){
      char *pNew;                 /* New temp space */
      i64 nByte = 0;
      if( pPager->eState>PAGER_NONE && isOpen(pPager->fd) ){
        rc = sqlite3OsFileSize(pPager->fd, &nByte);
        if( rc!=SQLITE_OK ) return rc;
      }
      pNew = (char *)sqlite3PageMalloc(pageSize);
      if( !pNew ){
        rc = SQLITE_NOMEM;
      }else{
        assert( pPager->eState==PAGER_NONE || pPager->eState==PAGER_READER );
        pager_reset(pPager);
        pPager->dbSize = nByte/pageSize;
        pPager->pageSize = pageSize;
        sqlite3PageFree(pPager->pTmpSpace);
        pPager->pTmpSpace = pNew;
        sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
      }
    }
    *pPageSize = (u16)pPager->pageSize;
................................................................................
**
** Otherwise, if everything is successful, then SQLITE_OK is returned
** and *pnPage is set to the number of pages in the database.
*/
int sqlite3PagerPagecount(Pager *pPager, int *pnPage){
  Pgno nPage = 0;           /* Value to return via *pnPage */

  assert( pPager->eState>=PAGER_SHARED );
  assert( pPager->eState!=PAGER_WRITER_FINISHED );

  /* Determine the number of pages in the file. Store this in nPage. */
  if( pPager->eState>PAGER_NONE ){
    nPage = pPager->dbSize;
  }else{
    int rc;                 /* Error returned by OsFileSize() */
    i64 n = 0;              /* File size in bytes returned by OsFileSize() */

    if( pagerUseWal(pPager) && pPager->eState!=PAGER_NONE ){
      nPage = sqlite3WalDbsize(pPager->pWal);
    }

    if( nPage==0 ){
      assert( isOpen(pPager->fd) || pPager->tempFile );
      if( isOpen(pPager->fd) ){
        if( SQLITE_OK!=(rc = sqlite3OsFileSize(pPager->fd, &n)) ){
          pager_error(pPager, rc);
................................................................................
      }else{
        nPage = (Pgno)(n / pPager->pageSize);
      }
    }
    if( pPager->eState!=PAGER_NONE ){
      pPager->dbSize = nPage;
      pPager->dbFileSize = nPage;

    }
  }

  /* If the current number of pages in the file is greater than the 
  ** configured maximum pager number, increase the allowed limit so
  ** that the file can be read.
  */
................................................................................
/*
** Truncate the in-memory database file image to nPage pages. This 
** function does not actually modify the database file on disk. It 
** just sets the internal state of the pager object so that the 
** truncation will be done when the current transaction is committed.
*/
void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){

  assert( pPager->dbSize>=nPage );
  assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
  pPager->dbSize = nPage;
  assertTruncateConstraint(pPager);
}


................................................................................
  IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))

  pPager->useJournal = (u8)useJournal;
  pPager->noReadlock = (noReadlock && readOnly) ?1:0;
  /* pPager->stmtOpen = 0; */
  /* pPager->stmtInUse = 0; */
  /* pPager->nRef = 0; */

  /* pPager->stmtSize = 0; */
  /* pPager->stmtJSize = 0; */
  /* pPager->nPage = 0; */
  pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
  /* pPager->state = PAGER_UNLOCK; */
#if 0
  assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
................................................................................
*/
static int hasHotJournal(Pager *pPager, int *pExists){
  sqlite3_vfs * const pVfs = pPager->pVfs;
  int rc = SQLITE_OK;           /* Return code */
  int exists = 1;               /* True if a journal file is present */
  int jrnlOpen = !!isOpen(pPager->jfd);


  assert( pPager->useJournal );
  assert( isOpen(pPager->fd) );
  assert( pPager->eState==PAGER_NONE );

  assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
    SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
  ));

  *pExists = 0;
  if( !jrnlOpen ){
................................................................................
    ** 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
    ** be dealt with by the playback routine.  Ticket #3883.
    */
    rc = sqlite3OsCheckReservedLock(pPager->fd, &locked);
    if( rc==SQLITE_OK && !locked ){
      Pgno nPage;                 /* Number of pages in database file */

      /* Check the size of the database file. If it consists of 0 pages,
      ** then delete the journal file. See the header comment above for 
      ** the reasoning here.  Delete the obsolete journal file under
      ** 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);
          }
................................................................................
** 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 this database is in an error-state, now is a chance to clear
  ** the error. Discard the contents of the pager-cache and rollback
  ** any hot journal in the file-system.
  */
  if( pPager->errCode ){
    if( isOpen(pPager->jfd) || pPager->zJournal ){
      isErrorReset = 1;
    }
    pPager->errCode = SQLITE_OK;
    pPager->eState = PAGER_NONE;
    pager_reset(pPager);
  }

  if( !pagerUseWal(pPager) && pPager->eState==PAGER_NONE ){


    sqlite3_vfs * const pVfs = pPager->pVfs;


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

    if( pPager->noReadlock==0 ){
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);
      if( rc!=SQLITE_OK ){
        assert( pPager->eLock==PAGER_UNLOCK );
        return pager_error(pPager, rc);
      }
    }


    /* 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.
      ** 
................................................................................
      ** the journal before playing it back.
      */
      if( isOpen(pPager->jfd) ){
        rc = pagerSyncHotJournal(pPager);
        if( rc==SQLITE_OK ){
          pPager->eState = PAGER_WRITER_FINISHED;
          rc = pager_playback(pPager, 1);
          pPager->eState = PAGER_NONE;
        }
        if( rc!=SQLITE_OK ){
          rc = pager_error(pPager, rc);
          goto failed;
        }
      }else{
        osUnlock(pPager, SHARED_LOCK);
      }

      assert( pPager->eState==PAGER_NONE );
      assert( (pPager->eLock==SHARED_LOCK)
           || (pPager->exclusiveMode && pPager->eLock>SHARED_LOCK)
      );
    }

    if( pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0 ){
      /* The shared-lock has just been acquired on the database file
................................................................................
      ** 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.
      */
      Pgno nPage = 0;
      char dbFileVers[sizeof(pPager->dbFileVers)];




      rc = pagerPagecount(pPager, &nPage);
      if( rc ) goto failed;


      if( nPage>0 ){
        IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
        rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
        if( rc!=SQLITE_OK ){
          goto failed;
        }
................................................................................
        memset(dbFileVers, 0, sizeof(dbFileVers));
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }


    /* If there is a WAL file in the file-system, open this database in WAL
    ** mode. Otherwise, the following function call is a no-op.
    */
    rc = pagerOpenWalIfPresent(pPager);
  }

  if( pagerUseWal(pPager) && rc==SQLITE_OK ){
    rc = pagerBeginReadTransaction(pPager);
  }

  if( pPager->eState==PAGER_NONE && rc==SQLITE_OK ){
    pPager->eState = PAGER_NONE;
    rc = pagerPagecount(pPager, &pPager->dbSize);
  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }else{
    pPager->eState = PAGER_READER;
  }
  return rc;
}

/*
** If the reference count has reached zero, rollback any active
** transaction and unlock the pager.
................................................................................
  
  /* If already in the error state, this function is a no-op.  But on
  ** the other hand, this routine is never called if we are already in
  ** an error state. */
  if( NEVER(pPager->errCode) ) return pPager->errCode;

  if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){

    rc = sqlite3PagerPagecount(pPager, &nPage);
    if( rc ) return rc;
    pPager->pInJournal = sqlite3BitvecCreate(nPage);
    if( pPager->pInJournal==0 ){
      return SQLITE_NOMEM;
    }
  
................................................................................
      if( rc==SQLITE_OK ){
        rc = rc2;
      }
    }else{
      rc = pager_playback(pPager, 0);
    }





    /* If an error occurs during a ROLLBACK, we can no longer trust the pager
    ** cache. So call pager_error() on the way out to make any error 
    ** persistent.
    */
    rc = pager_error(pPager, rc);
  }
  return rc;
................................................................................
** This routine is used for testing and analysis only.
*/
int *sqlite3PagerStats(Pager *pPager){
  static int a[11];
  a[0] = sqlite3PcacheRefCount(pPager->pPCache);
  a[1] = sqlite3PcachePagecount(pPager->pPCache);
  a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
  a[3] = pPager->eState==PAGER_NONE ? -1 : (int) pPager->dbSize;
  a[4] = pPager->eState;
  a[5] = pPager->errCode;
  a[6] = pPager->nHit;
  a[7] = pPager->nMiss;
  a[8] = 0;  /* Used to be pPager->nOvfl */
  a[9] = pPager->nRead;
  a[10] = pPager->nWrite;
................................................................................
  pPgOld = pager_lookup(pPager, pgno);
  assert( !pPgOld || pPgOld->nRef==1 );
  if( pPgOld ){
    pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
    if( MEMDB ){
      /* Do not discard pages from an in-memory database since we might
      ** need to rollback later.  Just move the page out of the way. */

      sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
    }else{
      sqlite3PcacheDrop(pPgOld);
    }
  }

  origPgno = pPg->pgno;
................................................................................
** 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_NONE   || 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;

    /* Open the connection to the log file. If this operation fails, 
    ** (e.g. due to malloc() failure), unlock the database file and 
    ** return an error code.
    */
    rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, pPager->zWal, &pPager->pWal);
    if( rc==SQLITE_OK ){
      pPager->journalMode = PAGER_JOURNALMODE_WAL;
      pPager->eState = PAGER_NONE;
    }
  }else{
    *pbOpen = 1;
  }

  return rc;
}

/*
** This function is called to close the connection to the log file prior

Changes to src/test_stat.c.

372
373
374
375
376
377
378

379





380
381
382
383
384
385
386
...
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

  sqlite3_free(pCsr->zPath);
  pCsr->zPath = 0;

  if( pCsr->aPage[0].pPg==0 ){
    rc = sqlite3_step(pCsr->pStmt);
    if( rc==SQLITE_ROW ){

      u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1);





      rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg);
      pCsr->aPage[0].iPgno = iRoot;
      pCsr->aPage[0].iCell = 0;
      pCsr->aPage[0].zPath = sqlite3_mprintf("/");
      pCsr->iPage = 0;
    }else{
      pCsr->isEof = 1;
................................................................................
}

static int statFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  sqlite3 *db = ((StatTable *)(pCursor->pVtab))->db;
  StatCursor *pCsr = (StatCursor *)pCursor;
  int nPage = 0;

  statResetCsr((StatCursor *)pCursor);
  sqlite3PagerPagecount(sqlite3BtreePager(db->aDb[0].pBt), &nPage);
  if( nPage==0 ){
    pCsr->isEof = 1;
    return SQLITE_OK;
  }

  return statNext(pCursor);
}

static int statColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int i







>

>
>
>
>
>







 







<

<


<
<
<
<
<
<







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
...
488
489
490
491
492
493
494

495

496
497






498
499
500
501
502
503
504

  sqlite3_free(pCsr->zPath);
  pCsr->zPath = 0;

  if( pCsr->aPage[0].pPg==0 ){
    rc = sqlite3_step(pCsr->pStmt);
    if( rc==SQLITE_ROW ){
      int nPage;
      u32 iRoot = sqlite3_column_int64(pCsr->pStmt, 1);
      sqlite3PagerPagecount(pPager, &nPage);
      if( nPage==0 ){
        pCsr->isEof = 1;
        return sqlite3_reset(pCsr->pStmt);
      }
      rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg);
      pCsr->aPage[0].iPgno = iRoot;
      pCsr->aPage[0].iCell = 0;
      pCsr->aPage[0].zPath = sqlite3_mprintf("/");
      pCsr->iPage = 0;
    }else{
      pCsr->isEof = 1;
................................................................................
}

static int statFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){

  StatCursor *pCsr = (StatCursor *)pCursor;


  statResetCsr((StatCursor *)pCursor);






  return statNext(pCursor);
}

static int statColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int i

Changes to src/wal.c.

2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173


2174
2175
2176
2177
2178
2179
2180

  *pInWal = 0;
  return SQLITE_OK;
}


/* 
** Set *pPgno to the size of the database file (or zero, if unknown).
*/
void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno){
  assert( pWal->readLock>=0 || pWal->lockError );
  *pPgno = pWal->hdr.nPage;


}


/* 
** This function starts a write transaction on the WAL.
**
** A read transaction must have already been started by a prior call







|

|
|
|
>
>







2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182

  *pInWal = 0;
  return SQLITE_OK;
}


/* 
** Return the size of the database in pages (or zero, if unknown).
*/
Pgno sqlite3WalDbsize(Wal *pWal){
  if( pWal && pWal->readLock>=0 ){
    return pWal->hdr.nPage;
  }
  return 0;
}


/* 
** This function starts a write transaction on the WAL.
**
** A read transaction must have already been started by a prior call

Changes to src/wal.h.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z)                 0
# define sqlite3WalClose(w,x,y,z)              0
# define sqlite3WalBeginReadTransaction(y,z)   0
# define sqlite3WalEndReadTransaction(z)
# define sqlite3WalRead(v,w,x,y,z)             0
# define sqlite3WalDbsize(y,z)
# define sqlite3WalBeginWriteTransaction(y)    0
# define sqlite3WalEndWriteTransaction(x)      0
# define sqlite3WalUndo(x,y,z)                 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z)          0
# define sqlite3WalFrames(u,v,w,x,y,z)         0
# define sqlite3WalCheckpoint(u,v,w,x)         0
................................................................................
*/
int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
void sqlite3WalEndReadTransaction(Wal *pWal);

/* Read a page from the write-ahead log, if it is present. */
int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);

/* Return the size of the database as it existed at the beginning
** of the snapshot */
void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno);

/* Obtain or release the WRITER lock. */
int sqlite3WalBeginWriteTransaction(Wal *pWal);
int sqlite3WalEndWriteTransaction(Wal *pWal);

/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);







|







 







|
<
|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72

#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z)                 0
# define sqlite3WalClose(w,x,y,z)              0
# define sqlite3WalBeginReadTransaction(y,z)   0
# define sqlite3WalEndReadTransaction(z)
# define sqlite3WalRead(v,w,x,y,z)             0
# define sqlite3WalDbsize(y)                   0
# define sqlite3WalBeginWriteTransaction(y)    0
# define sqlite3WalEndWriteTransaction(x)      0
# define sqlite3WalUndo(x,y,z)                 0
# define sqlite3WalSavepoint(y,z)
# define sqlite3WalSavepointUndo(y,z)          0
# define sqlite3WalFrames(u,v,w,x,y,z)         0
# define sqlite3WalCheckpoint(u,v,w,x)         0
................................................................................
*/
int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
void sqlite3WalEndReadTransaction(Wal *pWal);

/* Read a page from the write-ahead log, if it is present. */
int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);

/* If the WAL is not empty, return the size of the database. */

Pgno sqlite3WalDbsize(Wal *pWal);

/* Obtain or release the WRITER lock. */
int sqlite3WalBeginWriteTransaction(Wal *pWal);
int sqlite3WalEndWriteTransaction(Wal *pWal);

/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);

Changes to test/permutations.test.

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
lappend ::testsuitelist xxx

test_suite "veryquick" -prefix "" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault* 
]

test_suite "quick" -prefix "" -description {
  Quick test suite. Runs in around 10 minutes on a workstation.
} -files [
  test_set $allquicktests
]







|







125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
lappend ::testsuitelist xxx

test_suite "veryquick" -prefix "" -description {
  "Very" quick test suite. Runs in less than 5 minutes on a workstation. 
  This test suite is the same as the "quick" tests, except that some files
  that test malloc and IO errors are omitted.
} -files [
  test_set $allquicktests -exclude *malloc* *ioerr* *fault*
]

test_suite "quick" -prefix "" -description {
  Quick test suite. Runs in around 10 minutes on a workstation.
} -files [
  test_set $allquicktests
]