/ Check-in [f8ae9bfd]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Changes from ENABLE_CONCURRENT (default off) to OMIT_CONCURRENT (default on). This is not a clear-cut decision and might be changed back.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA1: f8ae9bfd05abc35293ad6bc62ab1bdbe357d964e
User & Date: drh 2015-09-03 15:17:12
Wiki:begin-concurrent
Context
2015-09-03
20:52
Merge performance enhancements from trunk. This branch now runs (slightly) faster than the 3.8.11.1 release, though still slightly slower than trunk. check-in: c490bfb1 user: drh tags: begin-concurrent
15:17
Changes from ENABLE_CONCURRENT (default off) to OMIT_CONCURRENT (default on). This is not a clear-cut decision and might be changed back. check-in: f8ae9bfd user: drh tags: begin-concurrent
14:04
Merge trunk optimizations. check-in: 71e7299e user: drh tags: begin-concurrent
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
469
470
471
472
473
474
475

476
477
478
479
480
481
482
483
...
527
528
529
530
531
532
533

534
535
536
537
538
539
540
541
...
554
555
556
557
558
559
560

561
562
563
564
565
566
567
568
...
583
584
585
586
587
588
589

590
591
592
593
594
595
596
597
...
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
....
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
....
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
....
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
....
3916
3917
3918
3919
3920
3921
3922

3923
3924
3925
3926
3927
3928
3929
3930
....
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
    }
  }
}

#endif /* SQLITE_OMIT_SHARED_CACHE */


#ifdef SQLITE_ENABLE_CONCURRENT
/*
** The following structure - BtreePtrmap - stores the in-memory pointer map
** used for newly allocated pages in CONCURRENT transactions. Such pages are
** always allocated in a contiguous block (from the end of the file) starting
** with page BtreePtrmap.iFirst.
*/
typedef struct RollbackEntry RollbackEntry;
................................................................................
  int *aSvpt;                     /* First aRollback[] entry for savepoint i */

  int nRollback;                  /* Used size of aRollback[] array */
  int nRollbackAlloc;             /* Allocated size of aRollback[] array */
  RollbackEntry *aRollback;       /* Array of rollback entries */
};


/*
** If page number pgno is greater than or equal to BtreePtrmap.iFirst, 
** store an entry for it in the pointer-map structure.
*/
static int btreePtrmapStore(
  BtShared *pBt,
  Pgno pgno,
  u8 eType, 
................................................................................
    pMap->aPtr[iEntry].parent = parent;
    pMap->aPtr[iEntry].eType = eType;
  }

  return SQLITE_OK;
}


/*
** Open savepoint iSavepoint, if it is not already open.
*/
static int btreePtrmapBegin(BtShared *pBt, int nSvpt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap && nSvpt<pMap->nSvpt ){
    int i;
    if( nSvpt>=pMap->nSvptAlloc ){
................................................................................
    }
    pMap->nSvpt = nSvpt;
  }

  return SQLITE_OK;
}


/*
** Rollback (if op==SAVEPOINT_ROLLBACK) or release (if op==SAVEPOINT_RELEASE)
** savepoint iSvpt.
*/
static void btreePtrmapEnd(BtShared *pBt, int op, int iSvpt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap ){
    assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE );
................................................................................
      }
      pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK);
      pMap->nRollback = pMap->aSvpt[iSvpt];
    }
  }
}


/*
** This function is called after an CONCURRENT transaction is opened on the
** database. It allocates the BtreePtrmap structure used to track pointers
** to allocated pages and zeroes the nFree/iTrunk fields in the database 
** header on page 1.
*/
static int btreePtrmapAllocate(BtShared *pBt){
  int rc = SQLITE_OK;
................................................................................
      pMap->iFirst = pBt->nPage + 1;
      pBt->pMap = pMap;
    }
  }
  return rc;
}


/*
** Free any BtreePtrmap structure allocated by an earlier call to
** btreePtrmapAllocate().
*/
static void btreePtrmapDelete(BtShared *pBt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap ){
    sqlite3_free(pMap->aRollback);
    sqlite3_free(pMap->aPtr);
    sqlite3_free(pMap->aSvpt);
    sqlite3_free(pMap);
    pBt->pMap = 0;
  }
}
#else
# define btreePtrmapAllocate(x) SQLITE_OK
# define btreePtrmapDelete(x) 
# define btreePtrmapBegin(x,y)  SQLITE_OK
# define btreePtrmapEnd(x,y,z) 
#endif

static void releasePage(MemPage *pPage);  /* Forward reference */

/*
***** This routine is used inside of assert() only ****
**
** Verify that the cursor holds the mutex on its BtShared
................................................................................

  if( *pRC ) return;

  assert( sqlite3_mutex_held(pBt->mutex) );
  /* The master-journal page number is never added to a pointer-map page */
  assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );

#ifdef SQLITE_ENABLE_CONCURRENT
  if( pBt->pMap ){
    *pRC = btreePtrmapStore(pBt, key, eType, parent);
    return;
  }
#endif

  assert( pBt->autoVacuum );
................................................................................
        }
      }
    }
  }


trans_begun:
#ifdef SQLITE_ENABLE_CONCURRENT
  if( bConcurrent && rc==SQLITE_OK && sqlite3PagerIsWal(pBt->pPager) ){
    rc = sqlite3PagerBeginConcurrent(pBt->pPager);
    if( rc==SQLITE_OK && wrflag ){
      rc = btreePtrmapAllocate(pBt);
    }
  }
#endif
................................................................................
  return rc;
}

#else /* ifndef SQLITE_OMIT_AUTOVACUUM */
# define setChildPtrmaps(x) SQLITE_OK
#endif

#ifdef SQLITE_ENABLE_CONCURRENT
/*
** This function is called as part of merging an CONCURRENT transaction with
** the snapshot at the head of the wal file. It relocates all pages in the
** range iFirst..iLast, inclusive. It is assumed that the BtreePtrmap 
** structure at BtShared.pMap contains the location of the pointers to each
** page in the range.
**
................................................................................
        releasePage(pPg);
      }
    }
  }
  return rc;
}


/*
** The b-tree handle passed as the only argument is about to commit an
** CONCURRENT transaction. At this point it is guaranteed that this is 
** possible - the wal WRITER lock is held and it is known that there are 
** no conflicts with committed transactions.
*/
static int btreeFixUnlocked(Btree *p){
  BtShared *pBt = p->pBt;
................................................................................
    sqlite3PagerSetDbsize(pPager, nFin);
  }

  return rc;
}
#else
# define btreeFixUnlocked(X)  SQLITE_OK
#endif /* ENABLE_CONCURRENT */

/*
** This routine does the first phase of a two-phase commit.  This routine
** causes a rollback journal to be created (if it does not already exist)
** and populated with enough information so that if a power loss occurs
** the database can be restored to its original state by playing back
** the journal.  Then the contents of the journal are flushed out to







|







 







>
|







 







>
|







 







>
|







 







>
|







 







>
|













|




|







 







|







 







|







 







|







 







>
|







 







|







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
...
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
...
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
....
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
....
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
....
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
....
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
....
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
    }
  }
}

#endif /* SQLITE_OMIT_SHARED_CACHE */


#ifndef SQLITE_OMIT_CONCURRENT
/*
** The following structure - BtreePtrmap - stores the in-memory pointer map
** used for newly allocated pages in CONCURRENT transactions. Such pages are
** always allocated in a contiguous block (from the end of the file) starting
** with page BtreePtrmap.iFirst.
*/
typedef struct RollbackEntry RollbackEntry;
................................................................................
  int *aSvpt;                     /* First aRollback[] entry for savepoint i */

  int nRollback;                  /* Used size of aRollback[] array */
  int nRollbackAlloc;             /* Allocated size of aRollback[] array */
  RollbackEntry *aRollback;       /* Array of rollback entries */
};

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** If page number pgno is greater than or equal to BtreePtrmap.iFirst, 
** store an entry for it in the pointer-map structure.
*/
static int btreePtrmapStore(
  BtShared *pBt,
  Pgno pgno,
  u8 eType, 
................................................................................
    pMap->aPtr[iEntry].parent = parent;
    pMap->aPtr[iEntry].eType = eType;
  }

  return SQLITE_OK;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Open savepoint iSavepoint, if it is not already open.
*/
static int btreePtrmapBegin(BtShared *pBt, int nSvpt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap && nSvpt<pMap->nSvpt ){
    int i;
    if( nSvpt>=pMap->nSvptAlloc ){
................................................................................
    }
    pMap->nSvpt = nSvpt;
  }

  return SQLITE_OK;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Rollback (if op==SAVEPOINT_ROLLBACK) or release (if op==SAVEPOINT_RELEASE)
** savepoint iSvpt.
*/
static void btreePtrmapEnd(BtShared *pBt, int op, int iSvpt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap ){
    assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE );
................................................................................
      }
      pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK);
      pMap->nRollback = pMap->aSvpt[iSvpt];
    }
  }
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** This function is called after an CONCURRENT transaction is opened on the
** database. It allocates the BtreePtrmap structure used to track pointers
** to allocated pages and zeroes the nFree/iTrunk fields in the database 
** header on page 1.
*/
static int btreePtrmapAllocate(BtShared *pBt){
  int rc = SQLITE_OK;
................................................................................
      pMap->iFirst = pBt->nPage + 1;
      pBt->pMap = pMap;
    }
  }
  return rc;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Free any BtreePtrmap structure allocated by an earlier call to
** btreePtrmapAllocate().
*/
static void btreePtrmapDelete(BtShared *pBt){
  BtreePtrmap *pMap = pBt->pMap;
  if( pMap ){
    sqlite3_free(pMap->aRollback);
    sqlite3_free(pMap->aPtr);
    sqlite3_free(pMap->aSvpt);
    sqlite3_free(pMap);
    pBt->pMap = 0;
  }
}
#else  /* SQLITE_OMIT_CONCURRENT */
# define btreePtrmapAllocate(x) SQLITE_OK
# define btreePtrmapDelete(x) 
# define btreePtrmapBegin(x,y)  SQLITE_OK
# define btreePtrmapEnd(x,y,z) 
#endif /* SQLITE_OMIT_CONCURRENT */

static void releasePage(MemPage *pPage);  /* Forward reference */

/*
***** This routine is used inside of assert() only ****
**
** Verify that the cursor holds the mutex on its BtShared
................................................................................

  if( *pRC ) return;

  assert( sqlite3_mutex_held(pBt->mutex) );
  /* The master-journal page number is never added to a pointer-map page */
  assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );

#ifndef SQLITE_OMIT_CONCURRENT
  if( pBt->pMap ){
    *pRC = btreePtrmapStore(pBt, key, eType, parent);
    return;
  }
#endif

  assert( pBt->autoVacuum );
................................................................................
        }
      }
    }
  }


trans_begun:
#ifndef SQLITE_OMIT_CONCURRENT
  if( bConcurrent && rc==SQLITE_OK && sqlite3PagerIsWal(pBt->pPager) ){
    rc = sqlite3PagerBeginConcurrent(pBt->pPager);
    if( rc==SQLITE_OK && wrflag ){
      rc = btreePtrmapAllocate(pBt);
    }
  }
#endif
................................................................................
  return rc;
}

#else /* ifndef SQLITE_OMIT_AUTOVACUUM */
# define setChildPtrmaps(x) SQLITE_OK
#endif

#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is called as part of merging an CONCURRENT transaction with
** the snapshot at the head of the wal file. It relocates all pages in the
** range iFirst..iLast, inclusive. It is assumed that the BtreePtrmap 
** structure at BtShared.pMap contains the location of the pointers to each
** page in the range.
**
................................................................................
        releasePage(pPg);
      }
    }
  }
  return rc;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** The b-tree handle passed as the only argument is about to commit an
** CONCURRENT transaction. At this point it is guaranteed that this is 
** possible - the wal WRITER lock is held and it is known that there are 
** no conflicts with committed transactions.
*/
static int btreeFixUnlocked(Btree *p){
  BtShared *pBt = p->pBt;
................................................................................
    sqlite3PagerSetDbsize(pPager, nFin);
  }

  return rc;
}
#else
# define btreeFixUnlocked(X)  SQLITE_OK
#endif /* SQLITE_OMIT_CONCURRENT */

/*
** This routine does the first phase of a two-phase commit.  This routine
** causes a rollback journal to be created (if it does not already exist)
** and populated with enough information so that if a power loss occurs
** the database can be restored to its original state by playing back
** the journal.  Then the contents of the journal are flushed out to

Changes to src/btreeInt.h.

444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */
  Btree *pWriter;       /* Btree with currently open write transaction */
#endif
  u8 *pTmpSpace;        /* Temp space sufficient to hold a single cell */
#ifdef SQLITE_ENABLE_CONCURRENT
  BtreePtrmap *pMap;
#endif
};

/*
** Allowed values for BtShared.btsFlags
*/
................................................................................
/*
** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
** if the database supports auto-vacuum or not. Because it is used
** within an expression that is an argument to another macro 
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM (pBt->autoVacuum)
#else
#define ISAUTOVACUUM 0
#endif

#ifdef SQLITE_ENABLE_CONCURRENT
# define ISCONCURRENT (pBt->pMap!=0)
#else
# define ISCONCURRENT 0
#endif

#define REQUIRE_PTRMAP (ISAUTOVACUUM || ISCONCURRENT)

/*
** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information.







|







 







|
|

|


|
|

|







444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */
  Btree *pWriter;       /* Btree with currently open write transaction */
#endif
  u8 *pTmpSpace;        /* Temp space sufficient to hold a single cell */
#ifndef SQLITE_OMIT_CONCURRENT
  BtreePtrmap *pMap;
#endif
};

/*
** Allowed values for BtShared.btsFlags
*/
................................................................................
/*
** The ISAUTOVACUUM macro is used within balance_nonroot() to determine
** if the database supports auto-vacuum or not. Because it is used
** within an expression that is an argument to another macro 
** (sqliteMallocRaw), it is not possible to use conditional compilation.
** So, this macro is defined instead.
*/
#ifdef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM 0
#else
#define ISAUTOVACUUM (pBt->autoVacuum)
#endif

#ifdef SQLITE_OMIT_CONCURRENT
# define ISCONCURRENT 0
#else
# define ISCONCURRENT (pBt->pMap!=0)
#endif

#define REQUIRE_PTRMAP (ISAUTOVACUUM || ISCONCURRENT)

/*
** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information.

Changes to src/pager.c.

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
...
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
....
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
....
1757
1758
1759
1760
1761
1762
1763

1764
1765
1766
1767
1768
1769
1770
1771
1772

1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
....
3065
3066
3067
3068
3069
3070
3071

3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
....
4482
4483
4484
4485
4486
4487
4488

4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
....
5328
5329
5330
5331
5332
5333
5334

5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
....
5966
5967
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
....
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
....
6194
6195
6196
6197
6198
6199
6200

6201
6202
6203
6204
6205
6206
6207

6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
  Pgno dbFileSize;            /* Number of pages in the database file */
  Pgno dbHintSize;            /* Value passed to FCNTL_SIZE_HINT call */
  int errCode;                /* One of several kinds of errors */
  int nRec;                   /* Pages journalled since last j-header written */
  u32 cksumInit;              /* Quasi-random value added to every checksum */
  u32 nSubRec;                /* Number of records written to sub-journal */
  Bitvec *pInJournal;         /* One bit for each page in the database file */
#ifdef SQLITE_ENABLE_CONCURRENT
  Bitvec *pAllRead;           /* Pages read within current CONCURRENT trans. */
#endif
  sqlite3_file *fd;           /* File descriptor for database */
  sqlite3_file *jfd;          /* File descriptor for main journal */
  sqlite3_file *sjfd;         /* File descriptor for sub-journal */
  i64 journalOff;             /* Current write offset in the journal file */
  i64 journalHdr;             /* Byte offset to previous journal header */
................................................................................

    case PAGER_WRITER_LOCKED:
      assert( p->eLock!=UNKNOWN_LOCK );
      assert( pPager->errCode==SQLITE_OK );
      if( !pagerUseWal(pPager) ){
        assert( p->eLock>=RESERVED_LOCK );
      }
#ifdef SQLITE_ENABLE_CONCURRENT
      assert( pPager->dbSize==pPager->dbOrigSize || pPager->pAllRead );
#endif
      assert( pPager->dbOrigSize==pPager->dbFileSize );
      assert( pPager->dbOrigSize==pPager->dbHintSize );
      assert( pPager->setMaster==0 );
      break;

................................................................................
      testcase( rc==SQLITE_NOMEM );
      assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
    }
  }
  return rc;
}

#ifdef SQLITE_ENABLE_CONCURRENT
/*
** If they are not already, begin recording all pages read from the pager layer
** by the b-tree layer This is used by concurrent transactions. Return
** SQLITE_OK if successful, or an SQLite error code (SQLITE_NOMEM) if an error
** occurs.
*/
int sqlite3PagerBeginConcurrent(Pager *pPager){
................................................................................
    if( pPager->pAllRead==0 ){
      rc = SQLITE_NOMEM;
    }
  }
  return rc;
}


/*
** Stop recording all pages read from the pager layer by the b-tree layer
** and discard any current records.
*/
void sqlite3PagerEndConcurrent(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pAllRead = 0;
}


/*
** Return true if the database is in wal mode. False otherwise.
*/
int sqlite3PagerIsWal(Pager *pPager){
  return pPager->pWal!=0;
}
#endif

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
................................................................................
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);


  /* If this is an CONCURRENT transaction, then page 1 must be reread from 
  ** the db file, even if it is not dirty. This is because the b-tree layer 
  ** may have already zeroed the nFree and iTrunk header fields.  */
#ifdef SQLITE_ENABLE_CONCURRENT
  if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
    rc = pagerUndoCallback((void*)pPager, 1);
  }
#endif

  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;
................................................................................
      || (pPg->flags & PGHDR_NEED_SYNC)!=0)
  ){
    return SQLITE_OK;
  }

  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){

    /* If the transaction is a "BEGIN CONCURRENT" transaction, the page 
    ** cannot be flushed to disk. Return early in this case. */
#ifdef SQLITE_ENABLE_CONCURRENT
    if( pPager->pAllRead ) return SQLITE_OK;
#endif

    /* Write a single frame for this page to the log. */
    rc = subjournalPageIfRequired(pPg); 
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0);
................................................................................
  assert( noContent==0 || bMmapOk==0 );

  if( pgno==0 ){
    return SQLITE_CORRUPT_BKPT;
  }
  pPager->hasBeenUsed = 1;


  /* If this is an CONCURRENT transaction and the page being read was
  ** present in the database file when the transaction was opened,
  ** mark it as read in the pAllRead vector.  */
#ifdef SQLITE_ENABLE_CONCURRENT
  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
  }
#endif

  /* If the pager is in the error state, return an error immediately. 
................................................................................
}

/*
** Return TRUE if the page given in the argument was previously passed
** to sqlite3PagerWrite().  In other words, return TRUE if it is ok
** to change the content of the page.
*/
#if defined(SQLITE_ENABLE_CONCURRENT) || !defined(NDEBUG)
int sqlite3PagerIswriteable(DbPage *pPg){
  return pPg->flags & PGHDR_WRITEABLE;
}
#endif

/*
** A call to this routine tells the pager that it is not necessary to
................................................................................
       || pPager->eState==PAGER_WRITER_DBMOD 
       || pPager->eState==PAGER_WRITER_LOCKED 
  );
  assert( assert_pager_state(pPager) );
  if( 0==pagerUseWal(pPager) ){
    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  }
#ifdef SQLITE_ENABLE_CONCURRENT
  else{
    if( pPager->pAllRead ){
      /* This is an CONCURRENT transaction. Attempt to lock the wal database
      ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
      ** invoke the busy-handler and try again for as long as it returns
      ** non-zero.  */
      do {
        rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead);
      }while( rc==SQLITE_BUSY 
           && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
      );
    }
  }
#endif
  return rc;
}

#ifdef SQLITE_ENABLE_CONCURRENT
/*
** This function is called as part of committing an CONCURRENT transaction.
** At this point the wal WRITER lock is held, and all pages in the cache 
** except for page 1 are compatible with the snapshot at the head of the
** wal file. 
**
** This function updates the in-memory data structures and reloads the
................................................................................
  if( rc==SQLITE_OK ){
    rc = readDbPage(pPage1, iFrame);
  }

  return rc;
}


/*
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
  pPager->dbSize = nSz;
}


/*
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
  if( pagerUseWal(pPager) ){
    sqlite3WalEndWriteTransaction(pPager->pWal);
  }
}
#endif   /* ifdef SQLITE_ENABLE_CONCURRENT */


/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).







|







 







|







 







|







 







>
|








>
|





|







 







>



<







 







>


<







 







>



<







 







|







 







|













|



|







 







>
|






>
|








|







653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
...
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
....
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
....
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
....
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077

3078
3079
3080
3081
3082
3083
3084
....
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493

4494
4495
4496
4497
4498
4499
4500
....
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340

5341
5342
5343
5344
5345
5346
5347
....
5968
5969
5970
5971
5972
5973
5974
5975
5976
5977
5978
5979
5980
5981
5982
....
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
6161
6162
6163
6164
6165
6166
6167
6168
6169
6170
6171
6172
6173
6174
6175
6176
6177
6178
6179
6180
6181
....
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
6211
6212
6213
6214
6215
6216
6217
6218
6219
6220
6221
6222
6223
6224
6225
6226
6227
6228
  Pgno dbFileSize;            /* Number of pages in the database file */
  Pgno dbHintSize;            /* Value passed to FCNTL_SIZE_HINT call */
  int errCode;                /* One of several kinds of errors */
  int nRec;                   /* Pages journalled since last j-header written */
  u32 cksumInit;              /* Quasi-random value added to every checksum */
  u32 nSubRec;                /* Number of records written to sub-journal */
  Bitvec *pInJournal;         /* One bit for each page in the database file */
#ifndef SQLITE_OMIT_CONCURRENT
  Bitvec *pAllRead;           /* Pages read within current CONCURRENT trans. */
#endif
  sqlite3_file *fd;           /* File descriptor for database */
  sqlite3_file *jfd;          /* File descriptor for main journal */
  sqlite3_file *sjfd;         /* File descriptor for sub-journal */
  i64 journalOff;             /* Current write offset in the journal file */
  i64 journalHdr;             /* Byte offset to previous journal header */
................................................................................

    case PAGER_WRITER_LOCKED:
      assert( p->eLock!=UNKNOWN_LOCK );
      assert( pPager->errCode==SQLITE_OK );
      if( !pagerUseWal(pPager) ){
        assert( p->eLock>=RESERVED_LOCK );
      }
#ifndef SQLITE_OMIT_CONCURRENT
      assert( pPager->dbSize==pPager->dbOrigSize || pPager->pAllRead );
#endif
      assert( pPager->dbOrigSize==pPager->dbFileSize );
      assert( pPager->dbOrigSize==pPager->dbHintSize );
      assert( pPager->setMaster==0 );
      break;

................................................................................
      testcase( rc==SQLITE_NOMEM );
      assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
    }
  }
  return rc;
}

#ifndef SQLITE_OMIT_CONCURRENT
/*
** If they are not already, begin recording all pages read from the pager layer
** by the b-tree layer This is used by concurrent transactions. Return
** SQLITE_OK if successful, or an SQLite error code (SQLITE_NOMEM) if an error
** occurs.
*/
int sqlite3PagerBeginConcurrent(Pager *pPager){
................................................................................
    if( pPager->pAllRead==0 ){
      rc = SQLITE_NOMEM;
    }
  }
  return rc;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Stop recording all pages read from the pager layer by the b-tree layer
** and discard any current records.
*/
void sqlite3PagerEndConcurrent(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pAllRead = 0;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Return true if the database is in wal mode. False otherwise.
*/
int sqlite3PagerIsWal(Pager *pPager){
  return pPager->pWal!=0;
}
#endif /* SQLITE_OMIT_CONCURRENT */

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
................................................................................
  **   + Discard the cached page (if refcount==0), or
  **   + Reload page content from the database (if refcount>0).
  */
  pPager->dbSize = pPager->dbOrigSize;
  rc = sqlite3WalUndo(pPager->pWal, pagerUndoCallback, (void *)pPager);
  pList = sqlite3PcacheDirtyList(pPager->pPCache);

#ifndef SQLITE_OMIT_CONCURRENT
  /* If this is an CONCURRENT transaction, then page 1 must be reread from 
  ** the db file, even if it is not dirty. This is because the b-tree layer 
  ** may have already zeroed the nFree and iTrunk header fields.  */

  if( rc==SQLITE_OK && (pList==0 || pList->pgno!=1) && pPager->pAllRead ){
    rc = pagerUndoCallback((void*)pPager, 1);
  }
#endif

  while( pList && rc==SQLITE_OK ){
    PgHdr *pNext = pList->pDirty;
................................................................................
      || (pPg->flags & PGHDR_NEED_SYNC)!=0)
  ){
    return SQLITE_OK;
  }

  pPg->pDirty = 0;
  if( pagerUseWal(pPager) ){
#ifndef SQLITE_OMIT_CONCURRENT
    /* If the transaction is a "BEGIN CONCURRENT" transaction, the page 
    ** cannot be flushed to disk. Return early in this case. */

    if( pPager->pAllRead ) return SQLITE_OK;
#endif

    /* Write a single frame for this page to the log. */
    rc = subjournalPageIfRequired(pPg); 
    if( rc==SQLITE_OK ){
      rc = pagerWalFrames(pPager, pPg, 0, 0);
................................................................................
  assert( noContent==0 || bMmapOk==0 );

  if( pgno==0 ){
    return SQLITE_CORRUPT_BKPT;
  }
  pPager->hasBeenUsed = 1;

#ifndef SQLITE_OMIT_CONCURRENT
  /* If this is an CONCURRENT transaction and the page being read was
  ** present in the database file when the transaction was opened,
  ** mark it as read in the pAllRead vector.  */

  if( pPager->pAllRead && pgno<=pPager->dbOrigSize ){
    rc = sqlite3BitvecSet(pPager->pAllRead, pgno);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
  }
#endif

  /* If the pager is in the error state, return an error immediately. 
................................................................................
}

/*
** Return TRUE if the page given in the argument was previously passed
** to sqlite3PagerWrite().  In other words, return TRUE if it is ok
** to change the content of the page.
*/
#if !defined(SQLITE_OMIT_CONCURRENT) || !defined(NDEBUG)
int sqlite3PagerIswriteable(DbPage *pPg){
  return pPg->flags & PGHDR_WRITEABLE;
}
#endif

/*
** A call to this routine tells the pager that it is not necessary to
................................................................................
       || pPager->eState==PAGER_WRITER_DBMOD 
       || pPager->eState==PAGER_WRITER_LOCKED 
  );
  assert( assert_pager_state(pPager) );
  if( 0==pagerUseWal(pPager) ){
    rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
  }
#ifndef SQLITE_OMIT_CONCURRENT
  else{
    if( pPager->pAllRead ){
      /* This is an CONCURRENT transaction. Attempt to lock the wal database
      ** here. If SQLITE_BUSY (but not SQLITE_BUSY_SNAPSHOT) is returned,
      ** invoke the busy-handler and try again for as long as it returns
      ** non-zero.  */
      do {
        rc = sqlite3WalLockForCommit(pPager->pWal, pPage1, pPager->pAllRead);
      }while( rc==SQLITE_BUSY 
           && pPager->xBusyHandler(pPager->pBusyHandlerArg) 
      );
    }
  }
#endif /* SQLITE_OMIT_CONCURRENT */
  return rc;
}

#ifndef SQLITE_OMIT_CONCURRENT
/*
** This function is called as part of committing an CONCURRENT transaction.
** At this point the wal WRITER lock is held, and all pages in the cache 
** except for page 1 are compatible with the snapshot at the head of the
** wal file. 
**
** This function updates the in-memory data structures and reloads the
................................................................................
  if( rc==SQLITE_OK ){
    rc = readDbPage(pPage1, iFrame);
  }

  return rc;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
  pPager->dbSize = nSz;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** If this is a WAL mode connection and the WRITER lock is currently held,
** relinquish it.
*/
void sqlite3PagerDropExclusiveLock(Pager *pPager){
  if( pagerUseWal(pPager) ){
    sqlite3WalEndWriteTransaction(pPager->pWal);
  }
}
#endif  /* SQLITE_OMIT_CONCURRENT */


/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
** journal (a single database transaction).

Changes to src/pager.h.

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
int sqlite3SectorSize(sqlite3_file *);

/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#ifdef SQLITE_ENABLE_CONCURRENT
void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else







|







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
int sqlite3SectorSize(sqlite3_file *);

/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);

void sqlite3PagerRekey(DbPage*, Pgno, u16);

#ifndef SQLITE_OMIT_CONCURRENT
void sqlite3PagerEndConcurrent(Pager*);
int sqlite3PagerBeginConcurrent(Pager*);
void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);
int sqlite3PagerIsWal(Pager*);
#else

Changes to src/test_config.c.

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583

#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_ENABLE_CONCURRENT
  Tcl_SetVar2(interp, "sqlite_options", "concurrent", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "concurrent", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_OMIT_UTF16
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);







|







569
570
571
572
573
574
575
576
577
578
579
580
581
582
583

#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY);
#endif

#ifndef SQLITE_OMIT_CONCURRENT
  Tcl_SetVar2(interp, "sqlite_options", "concurrent", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "concurrent", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_OMIT_UTF16
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY);

Changes to src/vdbe.c.

3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
....
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
  assert( DbMaskTest(p->btreeMask, pOp->p1) );
  assert( p->readOnly==0 );
  pDb = &db->aDb[pOp->p1];
  assert( pDb->pBt!=0 );
  assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
  pIn3 = &aMem[pOp->p3];
  sqlite3VdbeMemIntegerify(pIn3);
#ifdef SQLITE_ENABLE_CONCURRENT
  if( db->bConcurrent 
   && (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID)
  ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, "cannot modify %s within CONCURRENT transaction",
        pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id"
    );
................................................................................
** P2 contains the root-page of the table to lock.
**
** P4 contains a pointer to the name of the table being locked. This is only
** used to generate an error message if the lock cannot be obtained.
*/
case OP_TableLock: {
  u8 isWriteLock = (u8)pOp->p3;
#ifdef SQLITE_ENABLE_CONCURRENT
  if( isWriteLock && db->bConcurrent && pOp->p2==1 ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, 
        "cannot modify database schema within CONCURRENT transaction");
    break;
  }
#endif







|







 







|







3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
....
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
  assert( DbMaskTest(p->btreeMask, pOp->p1) );
  assert( p->readOnly==0 );
  pDb = &db->aDb[pOp->p1];
  assert( pDb->pBt!=0 );
  assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
  pIn3 = &aMem[pOp->p3];
  sqlite3VdbeMemIntegerify(pIn3);
#ifndef SQLITE_OMIT_CONCURRENT
  if( db->bConcurrent 
   && (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID)
  ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, "cannot modify %s within CONCURRENT transaction",
        pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id"
    );
................................................................................
** P2 contains the root-page of the table to lock.
**
** P4 contains a pointer to the name of the table being locked. This is only
** used to generate an error message if the lock cannot be obtained.
*/
case OP_TableLock: {
  u8 isWriteLock = (u8)pOp->p3;
#ifndef SQLITE_OMIT_CONCURRENT
  if( isWriteLock && db->bConcurrent && pOp->p2==1 ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, 
        "cannot modify database schema within CONCURRENT transaction");
    break;
  }
#endif

Changes to src/vdbeaux.c.

2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3BtreeExclusiveLock(pBt);
    }
  }

#ifdef SQLITE_ENABLE_CONCURRENT
  if( db->bConcurrent && (rc & 0xFF)==SQLITE_BUSY ){
    /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
    ** attempting to take the WRITER lock on a wal file. Release the
    ** WRITER locks on all wal files and return early.  */
    for(i=0; i<db->nDb; i++){
      Btree *pBt = db->aDb[i].pBt;
      if( sqlite3BtreeIsInTrans(pBt) ){







|







2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3BtreeExclusiveLock(pBt);
    }
  }

#ifndef SQLITE_OMIT_CONCURRENT
  if( db->bConcurrent && (rc & 0xFF)==SQLITE_BUSY ){
    /* An SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT was encountered while 
    ** attempting to take the WRITER lock on a wal file. Release the
    ** WRITER locks on all wal files and return early.  */
    for(i=0; i<db->nDb; i++){
      Btree *pBt = db->aDb[i].pBt;
      if( sqlite3BtreeIsInTrans(pBt) ){

Changes to src/wal.c.

2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
....
2748
2749
2750
2751
2752
2753
2754

2755
2756
2757
2758
2759
2760
2761
2762
....
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );
  return rc;
}


#ifdef SQLITE_ENABLE_CONCURRENT
/* 
** This function is only ever called when committing a "BEGIN CONCURRENT"
** transaction. It may be assumed that no frames have been written to
** the wal file. The second parameter is a pointer to the in-memory 
** representation of page 1 of the database (which may or may not be
** dirty). The third is a bitvec with a bit set for each page in the
** database file that was read by the current concurrent transaction.
................................................................................
      }
    }
  }

  return rc;
}


/*
** This function is called as part of committing an CONCURRENT transaction.
** It is assumed that sqlite3WalLockForCommit() has already been successfully
** called and so (a) the WRITER lock is held and (b) it is known that the
** wal-index-header stored in shared memory is not corrupt.
**
** Before returning, this function upgrades the client so that it is 
** operating on the database snapshot currently at the head of the wal file
................................................................................
  ** any reads performed between now and committing the transaction will
  ** read from the old snapshot - not the one just upgraded to.  */
  if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
    rc = walUpgradeReadlock(pWal);
  }
  return rc;
}
#endif   /* SQLITE_ENABLE_CONCURRENT */

/*
** End a write transaction.  The commit has already been done.  This
** routine merely releases the lock.
*/
int sqlite3WalEndWriteTransaction(Wal *pWal){
  if( pWal->writeLock ){







|







 







>
|







 







|







2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
....
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
....
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );
  return rc;
}


#ifndef SQLITE_OMIT_CONCURRENT
/* 
** This function is only ever called when committing a "BEGIN CONCURRENT"
** transaction. It may be assumed that no frames have been written to
** the wal file. The second parameter is a pointer to the in-memory 
** representation of page 1 of the database (which may or may not be
** dirty). The third is a bitvec with a bit set for each page in the
** database file that was read by the current concurrent transaction.
................................................................................
      }
    }
  }

  return rc;
}

/* !defined(SQLITE_OMIT_CONCURRENT)
**
** This function is called as part of committing an CONCURRENT transaction.
** It is assumed that sqlite3WalLockForCommit() has already been successfully
** called and so (a) the WRITER lock is held and (b) it is known that the
** wal-index-header stored in shared memory is not corrupt.
**
** Before returning, this function upgrades the client so that it is 
** operating on the database snapshot currently at the head of the wal file
................................................................................
  ** any reads performed between now and committing the transaction will
  ** read from the old snapshot - not the one just upgraded to.  */
  if( pWal->readLock==0 && pWal->hdr.mxFrame!=walCkptInfo(pWal)->nBackfill ){
    rc = walUpgradeReadlock(pWal);
  }
  return rc;
}
#endif   /* SQLITE_OMIT_CONCURRENT */

/*
** End a write transaction.  The commit has already been done.  This
** routine merely releases the lock.
*/
int sqlite3WalEndWriteTransaction(Wal *pWal){
  if( pWal->writeLock ){

Changes to src/wal.h.

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal);

#ifdef SQLITE_ENABLE_CONCURRENT
/* Tell the wal layer that we want to commit a concurrent transaction */
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead);

/* Upgrade the state of the client to take into account changes written
** by other connections */
int sqlite3WalUpgradeSnapshot(Wal *pWal);
#endif

#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
*/
int sqlite3WalFramesize(Wal *pWal);
#endif

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */







|






|










122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

/* Return true if the argument is non-NULL and the WAL module is using
** heap-memory for the wal-index. Otherwise, if the argument is NULL or the
** WAL module is using shared-memory, return false. 
*/
int sqlite3WalHeapMemory(Wal *pWal);

#ifndef SQLITE_OMIT_CONCURRENT
/* Tell the wal layer that we want to commit a concurrent transaction */
int sqlite3WalLockForCommit(Wal *pWal, PgHdr *pPg, Bitvec *pRead);

/* Upgrade the state of the client to take into account changes written
** by other connections */
int sqlite3WalUpgradeSnapshot(Wal *pWal);
#endif /* SQLITE_OMIT_CONCURRENT */

#ifdef SQLITE_ENABLE_ZIPVFS
/* If the WAL file is not empty, return the number of bytes of content
** stored in each frame (i.e. the db page-size when the WAL was created).
*/
int sqlite3WalFramesize(Wal *pWal);
#endif

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */