/ Check-in [5a043aa8]
Login

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

Overview
Comment:Support read-only MVCC transactions in server-mode. Started using "BEGIN READONLY".
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-process-edition
Files: files | file ages | folders
SHA3-256: 5a043aa8dd0751e644c495a59deea5fe05da905f49c664d978fe477f9240bc37
User & Date: dan 2017-07-08 20:46:17
Context
2017-07-11
16:47
Fix a bug causing readonly mvcc connections to read the wrong cache entry in some cases. check-in: b6157267 user: dan tags: server-process-edition
2017-07-08
20:46
Support read-only MVCC transactions in server-mode. Started using "BEGIN READONLY". check-in: 5a043aa8 user: dan tags: server-process-edition
2017-07-07
16:40
Merge latest trunk changes with this branch. check-in: 216c757f user: dan tags: server-process-edition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
  MemPage *pPage1;     /* Page 1 of the database file */
  int nPage;           /* Number of pages in the database */
  int nPageFile = 0;   /* Number of pages in the database file */
  int nPageHeader;     /* Number of pages in the database according to hdr */

  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( pBt->pPage1==0 );
  rc = sqlite3PagerSharedLock(pBt->pPager);
  if( rc!=SQLITE_OK ) return rc;
  rc = btreeGetPage(pBt, 1, &pPage1, 0);
  if( rc!=SQLITE_OK ) return rc;

  /* Do some checking to help insure the file we opened really is
  ** a valid database file. 
  */







|







2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
  MemPage *pPage1;     /* Page 1 of the database file */
  int nPage;           /* Number of pages in the database */
  int nPageFile = 0;   /* Number of pages in the database file */
  int nPageHeader;     /* Number of pages in the database according to hdr */

  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( pBt->pPage1==0 );
  rc = sqlite3PagerSharedLock(pBt->pPager, pBt->db->readonlyTrans);
  if( rc!=SQLITE_OK ) return rc;
  rc = btreeGetPage(pBt, 1, &pPage1, 0);
  if( rc!=SQLITE_OK ) return rc;

  /* Do some checking to help insure the file we opened really is
  ** a valid database file. 
  */

Changes to src/build.c.

3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
  db = pParse->db;
  assert( db!=0 );
  if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
    return;
  }
  v = sqlite3GetVdbe(pParse);
  if( !v ) return;
  if( type!=TK_DEFERRED ){
    for(i=0; i<db->nDb; i++){
      sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
      sqlite3VdbeUsesBtree(v, i);
    }
  }
  sqlite3VdbeAddOp0(v, OP_AutoCommit);
}

/*
** Generate VDBE code for a COMMIT or ROLLBACK statement.
** Code for ROLLBACK is generated if eType==TK_ROLLBACK.  Otherwise
** code is generated for a COMMIT.
*/







|





|







3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
  db = pParse->db;
  assert( db!=0 );
  if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
    return;
  }
  v = sqlite3GetVdbe(pParse);
  if( !v ) return;
  if( type!=TK_DEFERRED && type!=TK_READONLY ){
    for(i=0; i<db->nDb; i++){
      sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
      sqlite3VdbeUsesBtree(v, i);
    }
  }
  sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, type);
}

/*
** Generate VDBE code for a COMMIT or ROLLBACK statement.
** Code for ROLLBACK is generated if eType==TK_ROLLBACK.  Otherwise
** code is generated for a COMMIT.
*/

Changes to src/pager.c.

3024
3025
3026
3027
3028
3029
3030










3031
3032
3033
3034
3035







3036
3037
3038
3039
3040
3041
3042
....
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
....
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
....
5965
5966
5967
5968
5969
5970
5971

5972
5973
5974
5975
5976
5977
5978
....
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
#ifndef SQLITE_OMIT_WAL
  if( iFrame ){
    /* Try to pull the page from the write-ahead log. */
    rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
  }else
#endif
  {










    i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
    rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
    if( rc==SQLITE_IOERR_SHORT_READ ){
      rc = SQLITE_OK;
    }







  }

  if( pgno==1 ){
    if( rc ){
      /* If the read is unsuccessful, set the dbFileVers[] to something
      ** that will never be a valid file version.  dbFileVers[] is a copy
      ** of bytes 24..39 of the database.  Bytes 28..31 should always be
................................................................................
**      the contents of the page cache and rolling back any open journal
**      file.
**
** 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 OPEN or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
................................................................................
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    assert( sqlite3PagerRefcount(pPager)==0 );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);
    if( rc==SQLITE_OK){
      rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
    }
  }
#endif
  if( rc==SQLITE_OK && pagerUseWal(pPager) ){
    assert( rc==SQLITE_OK );
    rc = pagerBeginReadTransaction(pPager);
................................................................................
    if( !p ) return SQLITE_NOMEM_BKPT;
    memset(p, 0, sizeof(ServerPage));
    p->aData = (u8*)&p[1];
    p->nData = pPager->pageSize;
    p->pgno = pPg->pgno;
    p->pNext = pPager->pServerPage;
    pPager->pServerPage = p;

  }
#endif

  /* We should never write to the journal file the page that
  ** contains the database locks.  The following assert verifies
  ** that we do not. */
  assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
................................................................................
      if( pPager->eLock>=RESERVED_LOCK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }else{
        int rc = SQLITE_OK;
        int state = pPager->eState;
        assert( state==PAGER_OPEN || state==PAGER_READER );
        if( state==PAGER_OPEN ){
          rc = sqlite3PagerSharedLock(pPager);
        }
        if( pPager->eState==PAGER_READER ){
          assert( rc==SQLITE_OK );
          rc = pagerLockDb(pPager, RESERVED_LOCK);
        }
        if( rc==SQLITE_OK ){
          sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);







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







 







|







 







|
|







 







>







 







|







3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
....
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
....
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
....
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
....
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
#ifndef SQLITE_OMIT_WAL
  if( iFrame ){
    /* Try to pull the page from the write-ahead log. */
    rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
  }else
#endif
  {
#ifdef SQLITE_SERVER_EDITION
    u8 *pData = 0;
    if( pagerIsServer(pPager) ){
      sqlite3ServerReadPage(pPager->pServer, pgno, &pData);
      if( pData ){
        memcpy(pPg->pData, pData, pgsz);
      }
    }
    if( pData==0 ){
#endif
      i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
      rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
      if( rc==SQLITE_IOERR_SHORT_READ ){
        rc = SQLITE_OK;
      }
#ifdef SQLITE_SERVER_EDITION
      if( pagerIsServer(pPager) ){
        sqlite3ServerEndReadPage(pPager->pServer, pgno);
      }
    }
#endif

  }

  if( pgno==1 ){
    if( rc ){
      /* If the read is unsuccessful, set the dbFileVers[] to something
      ** that will never be a valid file version.  dbFileVers[] is a copy
      ** of bytes 24..39 of the database.  Bytes 28..31 should always be
................................................................................
**      the contents of the page cache and rolling back any open journal
**      file.
**
** 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 bReadonly){
  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 OPEN or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
................................................................................
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    assert( sqlite3PagerRefcount(pPager)==0 );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer, bReadonly);
    if( rc==SQLITE_OK ){
      rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
    }
  }
#endif
  if( rc==SQLITE_OK && pagerUseWal(pPager) ){
    assert( rc==SQLITE_OK );
    rc = pagerBeginReadTransaction(pPager);
................................................................................
    if( !p ) return SQLITE_NOMEM_BKPT;
    memset(p, 0, sizeof(ServerPage));
    p->aData = (u8*)&p[1];
    p->nData = pPager->pageSize;
    p->pgno = pPg->pgno;
    p->pNext = pPager->pServerPage;
    pPager->pServerPage = p;
    memcpy(p->aData, pPg->pData, pPager->pageSize);  
  }
#endif

  /* We should never write to the journal file the page that
  ** contains the database locks.  The following assert verifies
  ** that we do not. */
  assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
................................................................................
      if( pPager->eLock>=RESERVED_LOCK ){
        sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
      }else{
        int rc = SQLITE_OK;
        int state = pPager->eState;
        assert( state==PAGER_OPEN || state==PAGER_READER );
        if( state==PAGER_OPEN ){
          rc = sqlite3PagerSharedLock(pPager, 0);
        }
        if( pPager->eState==PAGER_READER ){
          assert( rc==SQLITE_OK );
          rc = pagerLockDb(pPager, RESERVED_LOCK);
        }
        if( rc==SQLITE_OK ){
          sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);

Changes to src/pager.h.

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerSync(Pager *pPager, const char *zMaster);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);

#ifndef SQLITE_OMIT_WAL
  int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
  int sqlite3PagerWalSupported(Pager *pPager);
  int sqlite3PagerWalCallback(Pager *pPager);
  int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
  int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);







|







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
int sqlite3PagerExclusiveLock(Pager*);
int sqlite3PagerSync(Pager *pPager, const char *zMaster);
int sqlite3PagerCommitPhaseTwo(Pager*);
int sqlite3PagerRollback(Pager*);
int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly);

#ifndef SQLITE_OMIT_WAL
  int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
  int sqlite3PagerWalSupported(Pager *pPager);
  int sqlite3PagerWalCallback(Pager *pPager);
  int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
  int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);

Changes to src/parse.y.

133
134
135
136
137
138
139

140
141
142
143
144
145
146

cmd ::= BEGIN transtype(Y) trans_opt.  {sqlite3BeginTransaction(pParse, Y);}
trans_opt ::= .
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= .             {A = TK_DEFERRED;}

transtype(A) ::= DEFERRED(X).  {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
cmd ::= COMMIT|END(X) trans_opt.   {sqlite3EndTransaction(pParse,@X);}
cmd ::= ROLLBACK(X) trans_opt.     {sqlite3EndTransaction(pParse,@X);}

savepoint_opt ::= SAVEPOINT.







>







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

cmd ::= BEGIN transtype(Y) trans_opt.  {sqlite3BeginTransaction(pParse, Y);}
trans_opt ::= .
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= .             {A = TK_DEFERRED;}
transtype(A) ::= READONLY(X).  {A = @X; /*A-overwrites-X*/}
transtype(A) ::= DEFERRED(X).  {A = @X; /*A-overwrites-X*/}
transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/}
transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/}
cmd ::= COMMIT|END(X) trans_opt.   {sqlite3EndTransaction(pParse,@X);}
cmd ::= ROLLBACK(X) trans_opt.     {sqlite3EndTransaction(pParse,@X);}

savepoint_opt ::= SAVEPOINT.

Changes to src/server.c.

114
115
116
117
118
119
120

121
122
123
124
125
126
127
128







129
130
131
132
133
134
135
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337


338
339












340
341
342
343
344
345
346
347
348
349
350


351
352
353
354
355
356
357
358
359
360
361
...
378
379
380
381
382
383
384

385
386
387
388

389
390





391
392
393
394
395
396
397
398
399
400
401
402
403

404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446


447
448

449
450
451
452
453
454
455
456
457
458
459
460
461
462
463



464
465
466
467
468
469
470
...
520
521
522
523
524
525
526





527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575


576
577
578
579
580
581
582
583














































584
/*
** Once instance for each client connection open on a server mode database
** in this process.
*/
struct Server {
  ServerDb *pDb;                  /* Database object */
  Pager *pPager;                  /* Associated pager object */

  int iTransId;                   /* Current transaction id (or -1) */
  int iCommitId;                  /* Current comit id (or 0) */
  int nAlloc;                     /* Allocated size of aLock[] array */
  int nLock;                      /* Number of entries in aLock[] */
  u32 *aLock;                     /* Mapped lock file */
  Server *pNext;                  /* Next in pCommit or pReader list */
};








#define SERVER_WRITE_LOCK 3
#define SERVER_READ_LOCK  2
#define SERVER_NO_LOCK    1

/*
** Global mutex functions used by code in this file.
*/
................................................................................
  *ppOut = pNew;
  return rc;
}

/*
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
  int rc = SQLITE_OK;

  if( p->iTransId<0 ){
    int id;
    ServerDb *pDb = p->pDb;
    u32 t;



    sqlite3_mutex_enter(pDb->mutex);













    /* Find a transaction id to use */
    rc = SQLITE_BUSY;
    t = pDb->transmask;
    for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
      if( (t & (1 << id))==0 ){
        t = t | (1 << id);
        rc = SQLITE_OK;
        break;
      }
    }
    pDb->transmask = t;



    sqlite3_mutex_leave(pDb->mutex);

    if( rc==SQLITE_OK ){
      ServerJournal *pJrnl = &pDb->aJrnl[id];
      sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal);
      p->iTransId = id;
    }
  }

  return rc;
................................................................................
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int rc = SQLITE_OK;

  Server **pp;
  ServerDb *pDb = p->pDb;
  ServerPage *pFree = 0;
  ServerPage *pPg = 0;

  sqlite3_mutex_enter(pDb->mutex);






  serverReleaseLocks(p);

  /* Clear the bit in the transaction mask. */
  pDb->transmask &= ~((u32)1 << p->iTransId);

  /* If this connection is in the committers list, remove it. */
  for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
    if( *pp==p ){
      *pp = p->pNext;
      break;
    }
  }


  /* See if it is possible to free any ServerPage records. If so, remove
  ** them from the linked list and hash table, but do not call sqlite3_free()
  ** on them until the mutex has been released.  */
  if( pDb->pPgFirst ){
    Server *pIter;
    int iOldest = 0x7FFFFFFF;
    for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
      iOldest = MIN(iOldest, pIter->iCommitId);
    }
    for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
      iOldest = MIN(iOldest, pIter->iCommitId);
    }

    pFree = pDb->pPgFirst;
    for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
      if( pPg->pHashPrev ){
        pPg->pHashPrev->pHashNext = pPg->pHashNext;
      }else{
        int iHash = pPg->pgno % HMA_HASH_SIZE;
        assert( pDb->apPg[iHash]==pPg );
        pDb->apPg[iHash] = pPg->pHashNext;
      }
      if( pPg->pHashNext ){
        pPg->pHashNext->pHashPrev = pPg->pHashPrev;
      }
    }
    if( pPg==0 ){
      pDb->pPgFirst = pDb->pPgLast = 0;
    }else{
      pDb->pPgFirst = pPg;
    }
  }

  sqlite3_mutex_leave(pDb->mutex);

  /* Call sqlite3_free() on any pages that were unlinked from the hash
  ** table above. */
  while( pFree && pFree!=pPg ){
    ServerPage *pNext = pFree->pNext;
    sqlite3_free(pFree);
    pFree = pNext;
  }



  p->iTransId = -1;
  p->iCommitId = 0;

  return rc;
}

int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){
  ServerDb *pDb = p->pDb;
  int rc = SQLITE_OK;
  ServerPage *pIter;
  ServerPage *pNext;

  if( pPg==0 ) return SQLITE_OK;

  sqlite3_mutex_enter(pDb->mutex);

  /* Assign a commit id to this transaction */
  assert( p->iCommitId==0 );



  p->iCommitId = pDb->iNextCommit++;

  /* Iterate through all pages. For each:
  **
  **   1. Set the iCommitId field.
  **   2. Add the page to the hash table.
  **   3. Wait until all slow-reader locks have cleared.
................................................................................
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
**
** If parameter bBlock is non-zero, then make this a blocking lock if
** possible.
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
  int rc = SQLITE_OK;





  ServerDb *pDb = p->pDb;
  int iWriter;
  int bSkip = 0;
  u32 *pSlot;

  assert( p->iTransId>=0 );
  assert( p->nLock<=p->nAlloc );
  if( p->nLock==p->nAlloc ){
    int nNew = p->nLock ? p->nLock*2 : 256;
    u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));
    if( aNew==0 ) return SQLITE_NOMEM_BKPT;
    memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));
    p->nAlloc = nNew;
    p->aLock = aNew;
  }

  sqlite3_mutex_enter(pDb->mutex);

  pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
  assert( slotGetWriter(*pSlot)<0 
       || slotReaderMask(*pSlot)==0 
       || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
  );

  iWriter = slotGetWriter(*pSlot);
  if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
    bSkip = 1;
  }else if( iWriter>=0 ){
    rc = SQLITE_BUSY_DEADLOCK;
  }else if( bWrite ){
    if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
      *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
    }else{
      rc = SQLITE_BUSY_DEADLOCK;
    }
  }else{
    *pSlot |= (1 << p->iTransId);
  }

  assert( slotGetWriter(*pSlot)<0 
       || slotReaderMask(*pSlot)==0 
       || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
  );

  sqlite3_mutex_leave(pDb->mutex);

  if( bSkip==0 ){
    p->aLock[p->nLock++] = pgno;
  }


  return rc;
}

int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite){
  assert( 0 );
  return 0;
}















































#endif /* ifdef SQLITE_SERVER_EDITION */







>

|






>
>
>
>
>
>
>







 







|


|




>
>


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



|







 







>
|
|
|
|
>
|

>
>
>
>
>
|

|
|

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

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|

|
|
|
|
|
|
|

>
>
|
|
>







<







>
>
>







 







>
>
>
>
>
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|

|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|

|

|
|
|
>
>








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

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
...
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
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
507
...
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
/*
** Once instance for each client connection open on a server mode database
** in this process.
*/
struct Server {
  ServerDb *pDb;                  /* Database object */
  Pager *pPager;                  /* Associated pager object */
  int eTrans;                     /* One of the SERVER_TRANS_xxx values */ 
  int iTransId;                   /* Current transaction id (or -1) */
  int iCommitId;                  /* Current commit id (or 0) */
  int nAlloc;                     /* Allocated size of aLock[] array */
  int nLock;                      /* Number of entries in aLock[] */
  u32 *aLock;                     /* Mapped lock file */
  Server *pNext;                  /* Next in pCommit or pReader list */
};

/*
** Possible values for Server.eTrans.
*/
#define SERVER_TRANS_NONE      0
#define SERVER_TRANS_READONLY  1
#define SERVER_TRANS_READWRITE 2

#define SERVER_WRITE_LOCK 3
#define SERVER_READ_LOCK  2
#define SERVER_NO_LOCK    1

/*
** Global mutex functions used by code in this file.
*/
................................................................................
  *ppOut = pNew;
  return rc;
}

/*
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p, int bReadonly){
  int rc = SQLITE_OK;

  if( p->eTrans==SERVER_TRANS_NONE ){
    int id;
    ServerDb *pDb = p->pDb;
    u32 t;

    assert( p->iTransId<0 );
    assert( p->pNext==0 );
    sqlite3_mutex_enter(pDb->mutex);

    if( bReadonly ){
      Server *pIter;
      p->iCommitId = pDb->iNextCommit;
      for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
        if( pIter->iCommitId<p->iCommitId ){
          p->iCommitId = pIter->iCommitId;
        }
      }
      p->pNext = pDb->pReader;
      pDb->pReader = p;
      p->eTrans = SERVER_TRANS_READONLY;
    }else{
      /* Find a transaction id to use */
      rc = SQLITE_BUSY;
      t = pDb->transmask;
      for(id=0; id<HMA_MAX_TRANSACTIONID; id++){
        if( (t & (1 << id))==0 ){
          t = t | (1 << id);
          rc = SQLITE_OK;
          break;
        }
      }
      pDb->transmask = t;
      p->eTrans = SERVER_TRANS_READWRITE;
    }

    sqlite3_mutex_leave(pDb->mutex);

    if( rc==SQLITE_OK && bReadonly==0 ){
      ServerJournal *pJrnl = &pDb->aJrnl[id];
      sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal);
      p->iTransId = id;
    }
  }

  return rc;
................................................................................
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int rc = SQLITE_OK;
  if( p->eTrans!=SERVER_TRANS_NONE ){
    Server **pp;
    ServerDb *pDb = p->pDb;
    ServerPage *pFree = 0;
    ServerPage *pPg = 0;

    sqlite3_mutex_enter(pDb->mutex);

    if( p->eTrans==SERVER_TRANS_READONLY ){
      /* Remove the connection from the readers list */
      for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext));
      *pp = p->pNext;
    }else{
      serverReleaseLocks(p);

      /* Clear the bit in the transaction mask. */
      pDb->transmask &= ~((u32)1 << p->iTransId);

      /* If this connection is in the committers list, remove it. */
      for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){
        if( *pp==p ){
          *pp = p->pNext;
          break;
        }
      }
    }

    /* See if it is possible to free any ServerPage records. If so, remove
    ** them from the linked list and hash table, but do not call sqlite3_free()
    ** on them until the mutex has been released.  */
    if( pDb->pPgFirst ){
      Server *pIter;
      int iOldest = 0x7FFFFFFF;
      for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){
        iOldest = MIN(iOldest, pIter->iCommitId);
      }
      for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){
        iOldest = MIN(iOldest, pIter->iCommitId);
      }

      pFree = pDb->pPgFirst;
      for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){
        if( pPg->pHashPrev ){
          pPg->pHashPrev->pHashNext = pPg->pHashNext;
        }else{
          int iHash = pPg->pgno % HMA_HASH_SIZE;
          assert( pDb->apPg[iHash]==pPg );
          pDb->apPg[iHash] = pPg->pHashNext;
        }
        if( pPg->pHashNext ){
          pPg->pHashNext->pHashPrev = pPg->pHashPrev;
        }
      }
      if( pPg==0 ){
        pDb->pPgFirst = pDb->pPgLast = 0;
      }else{
        pDb->pPgFirst = pPg;
      }
    }

    sqlite3_mutex_leave(pDb->mutex);

    /* Call sqlite3_free() on any pages that were unlinked from the hash
    ** table above. */
    while( pFree && pFree!=pPg ){
      ServerPage *pNext = pFree->pNext;
      sqlite3_free(pFree);
      pFree = pNext;
    }

    p->pNext = 0;
    p->eTrans = SERVER_TRANS_NONE;
    p->iTransId = -1;
    p->iCommitId = 0;
  }
  return rc;
}

int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){
  ServerDb *pDb = p->pDb;
  int rc = SQLITE_OK;
  ServerPage *pIter;


  if( pPg==0 ) return SQLITE_OK;

  sqlite3_mutex_enter(pDb->mutex);

  /* Assign a commit id to this transaction */
  assert( p->iCommitId==0 );
  assert( p->eTrans==SERVER_TRANS_READWRITE );
  assert( p->iTransId>=0 );

  p->iCommitId = pDb->iNextCommit++;

  /* Iterate through all pages. For each:
  **
  **   1. Set the iCommitId field.
  **   2. Add the page to the hash table.
  **   3. Wait until all slow-reader locks have cleared.
................................................................................
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
**
** If parameter bBlock is non-zero, then make this a blocking lock if
** possible.
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
  int rc = SQLITE_OK;

  assert( p->eTrans==SERVER_TRANS_READWRITE 
       || p->eTrans==SERVER_TRANS_READONLY 
  );
  if( p->eTrans==SERVER_TRANS_READWRITE ){
    ServerDb *pDb = p->pDb;
    int iWriter;
    int bSkip = 0;
    u32 *pSlot;

    assert( p->iTransId>=0 );
    assert( p->nLock<=p->nAlloc );
    if( p->nLock==p->nAlloc ){
      int nNew = p->nLock ? p->nLock*2 : 256;
      u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));
      if( aNew==0 ) return SQLITE_NOMEM_BKPT;
      memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));
      p->nAlloc = nNew;
      p->aLock = aNew;
    }

    sqlite3_mutex_enter(pDb->mutex);

    pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
    assert( slotGetWriter(*pSlot)<0 
        || slotReaderMask(*pSlot)==0 
        || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
        );

    iWriter = slotGetWriter(*pSlot);
    if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
      bSkip = 1;
    }else if( iWriter>=0 ){
      rc = SQLITE_BUSY_DEADLOCK;
    }else if( bWrite ){
      if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
        *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
      }else{
        rc = SQLITE_BUSY_DEADLOCK;
      }
    }else{
      *pSlot |= (1 << p->iTransId);
    }

    assert( slotGetWriter(*pSlot)<0 
        || slotReaderMask(*pSlot)==0 
        || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot))
        );

    sqlite3_mutex_leave(pDb->mutex);

    if( bSkip==0 ){
      p->aLock[p->nLock++] = pgno;
    }
  }

  return rc;
}

int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite){
  assert( 0 );
  return 0;
}

static void serverIncrSlowReader(u32 *pSlot, int n){
  assert( n==1 || n==-1 );
  *pSlot += (n * (1 << HMA_SLOT_RLWL_BITS));
}

void sqlite3ServerReadPage(Server *p, Pgno pgno, u8 **ppData){
  if( p->eTrans==SERVER_TRANS_READONLY ){
    ServerDb *pDb = p->pDb;
    ServerPage *pIter;
    ServerPage *pBest = 0;
    int iHash = pgno % HMA_HASH_SIZE;

    sqlite3_mutex_enter(pDb->mutex);

    /* Search the hash table for the oldest version of page pgno with
    ** a commit-id greater than or equal to Server.iCommitId.  */
    for(pIter=pDb->apPg[iHash]; pIter; pIter=pIter->pHashNext){
      if( pIter->iCommitId>=p->iCommitId 
       && (pBest==0 || pIter->iCommitId<pBest->iCommitId) 
      ){
        pBest = pIter;
      }
    }

    if( pBest ){
      *ppData = pBest->aData;
    }else{
      u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
      serverIncrSlowReader(pSlot, 1);
    }

    sqlite3_mutex_leave(pDb->mutex);
  }
}

void sqlite3ServerEndReadPage(Server *p, Pgno pgno){
  if( p->eTrans==SERVER_TRANS_READONLY ){
    ServerDb *pDb = p->pDb;
    u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
    sqlite3_mutex_enter(pDb->mutex);
    serverIncrSlowReader(pSlot, -1);
    assert( slotGetSlowReaders(*pSlot)>=0 );
    sqlite3_mutex_leave(pDb->mutex);
  }
}

#endif /* ifdef SQLITE_SERVER_EDITION */

Changes to src/server.h.

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45



46
47
48
49
  ServerPage *pHashNext;
  ServerPage *pHashPrev;
};

int sqlite3ServerConnect(Pager *pPager, Server **ppOut);
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);

int sqlite3ServerBegin(Server *p);
int sqlite3ServerPreCommit(Server*, ServerPage*);
int sqlite3ServerEnd(Server *p);

int sqlite3ServerReleaseWriteLocks(Server *p);

int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock);

int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite);




#endif /* SQLITE_SERVER_H */
#endif /* SQLITE_SERVER_EDITION */








|








>
>
>




30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  ServerPage *pHashNext;
  ServerPage *pHashPrev;
};

int sqlite3ServerConnect(Pager *pPager, Server **ppOut);
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);

int sqlite3ServerBegin(Server *p, int bReadonly);
int sqlite3ServerPreCommit(Server*, ServerPage*);
int sqlite3ServerEnd(Server *p);

int sqlite3ServerReleaseWriteLocks(Server *p);

int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock);

int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite);

void sqlite3ServerReadPage(Server*, Pgno, u8**);
void sqlite3ServerEndReadPage(Server*, Pgno);

#endif /* SQLITE_SERVER_H */
#endif /* SQLITE_SERVER_EDITION */

Changes to src/sqliteInt.h.

1323
1324
1325
1326
1327
1328
1329

1330
1331
1332
1333
1334
1335
1336
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  int iSysErrno;                /* Errno value from last system error */
  u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */

  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 bBenignMalloc;             /* Do not require OOMs if true */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
  u8 suppressErr;               /* Do not issue error messages if true */
  u8 vtabOnConflict;            /* Value to return for s3_vtab_on_conflict() */







>







1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  int iSysErrno;                /* Errno value from last system error */
  u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 readonlyTrans;             /* Transaction opened with BEGIN READONLY */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 bBenignMalloc;             /* Do not require OOMs if true */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
  u8 suppressErr;               /* Do not issue error messages if true */
  u8 vtabOnConflict;            /* Value to return for s3_vtab_on_conflict() */

Changes to src/test2.c.

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID PGNO\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
  rc = sqlite3PagerSharedLock(pPager);
  if( rc==SQLITE_OK ){
    rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
  }
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
    return TCL_ERROR;
  }







|







320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
  if( argc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
       " ID PGNO\"", 0);
    return TCL_ERROR;
  }
  pPager = sqlite3TestTextToPtr(argv[1]);
  if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR;
  rc = sqlite3PagerSharedLock(pPager, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3PagerGet(pPager, pgno, &pPage, 0);
  }
  if( rc!=SQLITE_OK ){
    Tcl_AppendResult(interp, sqlite3ErrName(rc), 0);
    return TCL_ERROR;
  }

Changes to src/vdbe.c.

3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
....
3133
3134
3135
3136
3137
3138
3139

3140
3141
3142
3143
3144
3145
3146
....
3164
3165
3166
3167
3168
3169
3170

3171
3172
3173
3174
3175
3176
3177
    }
  }
  if( rc ) goto abort_due_to_error;

  break;
}

/* Opcode: AutoCommit P1 P2 * * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then a ROLLBACK fails.  A COMMIT fails if
** there are active writing VMs or active VMs that use shared cache.
**
** This instruction causes the VM to halt.
................................................................................
  int desiredAutoCommit;
  int iRollback;

  desiredAutoCommit = pOp->p1;
  iRollback = pOp->p2;
  assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
  assert( desiredAutoCommit==1 || iRollback==0 );

  assert( db->nVdbeActive>0 );  /* At least this one VM is active */
  assert( p->bIsReader );

  if( desiredAutoCommit!=db->autoCommit ){
    if( iRollback ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
................................................................................
      p->rc = rc = SQLITE_BUSY;
      goto vdbe_return;
    }
    assert( db->nStatement==0 );
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;

    }else{
      rc = SQLITE_ERROR;
    }
    goto vdbe_return;
  }else{
    sqlite3VdbeError(p,
        (!desiredAutoCommit)?"cannot start a transaction within a transaction":(







|







 







>







 







>







3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
....
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
....
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
    }
  }
  if( rc ) goto abort_due_to_error;

  break;
}

/* Opcode: AutoCommit P1 P2 P3 * *
**
** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
** back any currently active btree transactions. If there are any active
** VMs (apart from this one), then a ROLLBACK fails.  A COMMIT fails if
** there are active writing VMs or active VMs that use shared cache.
**
** This instruction causes the VM to halt.
................................................................................
  int desiredAutoCommit;
  int iRollback;

  desiredAutoCommit = pOp->p1;
  iRollback = pOp->p2;
  assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
  assert( desiredAutoCommit==1 || iRollback==0 );
  assert( desiredAutoCommit==0 || pOp->p3==0 );
  assert( db->nVdbeActive>0 );  /* At least this one VM is active */
  assert( p->bIsReader );

  if( desiredAutoCommit!=db->autoCommit ){
    if( iRollback ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
................................................................................
      p->rc = rc = SQLITE_BUSY;
      goto vdbe_return;
    }
    assert( db->nStatement==0 );
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
      db->readonlyTrans = (pOp->p3==TK_READONLY);
    }else{
      rc = SQLITE_ERROR;
    }
    goto vdbe_return;
  }else{
    sqlite3VdbeError(p,
        (!desiredAutoCommit)?"cannot start a transaction within a transaction":(

Changes to src/wal.c.

3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    if( walIsServer(pWal) ){
      rc = sqlite3ServerBegin(pWal->pServer);
      if( rc!=SQLITE_OK ) goto ckpt_out;
      if( eMode>=SQLITE_CHECKPOINT_RESTART ){
        /* Exclusive lock on page 1. This is exclusive access to the db. */
        rc = sqlite3ServerLock(pWal->pServer, 1, 1, 1);
      }else{
        /* Take the server write-lock ("page" 0) */
        rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);







|







3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    if( walIsServer(pWal) ){
      rc = sqlite3ServerBegin(pWal->pServer, 0);
      if( rc!=SQLITE_OK ) goto ckpt_out;
      if( eMode>=SQLITE_CHECKPOINT_RESTART ){
        /* Exclusive lock on page 1. This is exclusive access to the db. */
        rc = sqlite3ServerLock(pWal->pServer, 1, 1, 1);
      }else{
        /* Take the server write-lock ("page" 0) */
        rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1);

Changes to test/permutations.test.

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  All FTS5 tests.
} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]

test_suite "server" -prefix "" -description {
  All server-edition tests.
} -files [
  test_set \
      select1.test server2.test server3.test
]

test_suite "fts5-light" -prefix "" -description {
  All FTS5 tests.
} -files [
  test_set \
      [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \







|







273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  All FTS5 tests.
} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]

test_suite "server" -prefix "" -description {
  All server-edition tests.
} -files [
  test_set \
      select1.test server2.test server3.test server4.test
]

test_suite "fts5-light" -prefix "" -description {
  All FTS5 tests.
} -files [
  test_set \
      [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \

Added test/server4.test.





























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 2017 July 09
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  
#
# The focus of this script is testing the server mode of SQLite.
# Specifically, that "BEGIN READONLY" starts a read-only MVCC
# transaction.
#


set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server3

sqlite3 db2 test.db

do_execsql_test 1.0 {
  CREATE TABLE t1(x);
  INSERT INTO t1 VALUES(1);
  CREATE TABLE t2(x);
  INSERT INTO t2 VALUES(1);
  BEGIN;
    INSERT INTO t1 VALUES(2);
    INSERT INTO t2 VALUES(2);
}

do_execsql_test -db db2 1.1 {
  BEGIN READONLY;
    SELECT * FROM t1;
} {1}

do_execsql_test 1.2 {
  COMMIT;
  INSERT INTO t1 VALUES(3);
  SELECT * FROM t1;
} {1 2 3}

do_execsql_test 1.2a {
  INSERT INTO t2 VALUES(3);
} {}

do_execsql_test -db db2 1.3 {
  SELECT * FROM t2;
} {1}

do_execsql_test -db db2 1.4 {
  ROLLBACK;
  SELECT * FROM t1;
} {1 2 3}


finish_test

Changes to tool/mkkeywordhash.c.

235
236
237
238
239
240
241

242
243
244
245
246
247
248
  { "ORDER",            "TK_ORDER",        ALWAYS                 },
  { "OUTER",            "TK_JOIN_KW",      ALWAYS                 },
  { "PLAN",             "TK_PLAN",         EXPLAIN                },
  { "PRAGMA",           "TK_PRAGMA",       PRAGMA                 },
  { "PRIMARY",          "TK_PRIMARY",      ALWAYS                 },
  { "QUERY",            "TK_QUERY",        EXPLAIN                },
  { "RAISE",            "TK_RAISE",        TRIGGER                },

  { "RECURSIVE",        "TK_RECURSIVE",    CTE                    },
  { "REFERENCES",       "TK_REFERENCES",   FKEY                   },
  { "REGEXP",           "TK_LIKE_KW",      ALWAYS                 },
  { "REINDEX",          "TK_REINDEX",      REINDEX                },
  { "RELEASE",          "TK_RELEASE",      ALWAYS                 },
  { "RENAME",           "TK_RENAME",       ALTER                  },
  { "REPLACE",          "TK_REPLACE",      CONFLICT               },







>







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  { "ORDER",            "TK_ORDER",        ALWAYS                 },
  { "OUTER",            "TK_JOIN_KW",      ALWAYS                 },
  { "PLAN",             "TK_PLAN",         EXPLAIN                },
  { "PRAGMA",           "TK_PRAGMA",       PRAGMA                 },
  { "PRIMARY",          "TK_PRIMARY",      ALWAYS                 },
  { "QUERY",            "TK_QUERY",        EXPLAIN                },
  { "RAISE",            "TK_RAISE",        TRIGGER                },
  { "READONLY",         "TK_READONLY",     ALWAYS                 },
  { "RECURSIVE",        "TK_RECURSIVE",    CTE                    },
  { "REFERENCES",       "TK_REFERENCES",   FKEY                   },
  { "REGEXP",           "TK_LIKE_KW",      ALWAYS                 },
  { "REINDEX",          "TK_REINDEX",      REINDEX                },
  { "RELEASE",          "TK_RELEASE",      ALWAYS                 },
  { "RENAME",           "TK_RENAME",       ALTER                  },
  { "REPLACE",          "TK_REPLACE",      CONFLICT               },