/ Check-in [ba1ab858]
Login

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

Overview
Comment:Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT".
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent
Files: files | file ages | folders
SHA1: ba1ab858e2997c88dd7eee6e6893a8616d85c665
User & Date: dan 2015-08-24 19:56:04
Wiki:begin-concurrent
Context
2015-08-24
22:06
Remove duplicated line of code. check-in: 47280f2a user: mistachkin tags: begin-concurrent
19:56
Change "BEGIN UNLOCKED" to "BEGIN CONCURRENT". check-in: ba1ab858 user: dan tags: begin-concurrent
19:08
Fix handling of attempts to modify the database schema, application_id or user_version within an UNLOCKED transaction. check-in: 5b9f2721 user: dan 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
451
452
453
...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
....
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
....
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
....
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
....
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
....
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
....
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
....
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
....
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
....
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
....
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
....
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
    }
  }
}

#endif /* SQLITE_OMIT_SHARED_CACHE */


#ifdef SQLITE_ENABLE_UNLOCKED
/*
** The following structure - BtreePtrmap - stores the in-memory pointer map
** used for newly allocated pages in UNLOCKED 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;
typedef struct PtrmapEntry PtrmapEntry;
struct PtrmapEntry {
  Pgno parent;
................................................................................
      pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK);
      pMap->nRollback = pMap->aSvpt[iSvpt];
    }
  }
}

/*
** This function is called after an UNLOCKED 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;
  BtreePtrmap *pMap = sqlite3_malloc(sizeof(BtreePtrmap));
  assert( pBt->pMap==0 && sqlite3PagerIsUnlocked(pBt->pPager) );
  if( pMap==0 ){
    rc = SQLITE_NOMEM;
  }else{
    memset(&pBt->pPage1->aData[32], 0, sizeof(u32)*2);
    memset(pMap, 0, sizeof(BtreePtrmap));
    pMap->iFirst = pBt->nPage + 1;
    pBt->pMap = pMap;
................................................................................

  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_UNLOCKED
  if( pBt->pMap ){
    *pRC = btreePtrmapStore(pBt, key, eType, parent);
    return;
  }
#endif

  assert( pBt->autoVacuum );
................................................................................
    */
    while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );

    if( rc==SQLITE_OK && wrflag ){
      if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
        rc = SQLITE_READONLY;
      }else{
        int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1);
        int bSubjInMem = sqlite3TempInMemory(p->db);
        assert( p->db->bUnlocked==0 || wrflag==1 );
        rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem);
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }
#ifdef SQLITE_ENABLE_UNLOCKED
        if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){
          rc = btreePtrmapAllocate(pBt);
        }
#endif
      }
    }
  
    if( rc!=SQLITE_OK ){
................................................................................
  return rc;
}

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

#ifdef SQLITE_ENABLE_UNLOCKED
/*
** This function is called as part of merging an UNLOCKED 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.
**
** If pnCurrent is NULL, then all pages in the range are moved to currently
** free locations (i.e. free-list entries) within the database file before page
................................................................................
    }
  }
  return rc;
}

/*
** The b-tree handle passed as the only argument is about to commit an
** UNLOCKED 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;
  MemPage *pPage1 = pBt->pPage1;
  u8 *p1 = pPage1->aData;
................................................................................
  ** or freed by this transaction. In this case no special handling is 
  ** required. Otherwise, if page 1 is dirty, proceed.  */
  BtreePtrmap *pMap = pBt->pMap;
  Pgno iTrunk = get4byte(&p1[32]);
  Pgno nPage = btreePagecount(pBt);
  u32 nFree = get4byte(&p1[36]);

  assert( sqlite3PagerIsUnlocked(pPager) );
  assert( pBt->pMap );
  rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage);
  assert( p1==pPage1->aData );

  if( rc==SQLITE_OK ){
    Pgno nHPage = get4byte(&p1[28]);
    Pgno nFin = nHPage;         /* Size of db after transaction merge */
................................................................................
          }
          sqlite3PagerUnref(pTrunk);
        };
      }

      if( nHPage<(pMap->iFirst-1) ){
        /* The database consisted of (pMap->iFirst-1) pages when the current
        ** unlocked transaction was opened. And an unlocked transaction may
        ** not be executed on an auto-vacuum database - so the db should 
        ** not have shrunk since the transaction was opened. Therefore nHPage
        ** should be set to (pMap->iFirst-1) or greater. */
        rc = SQLITE_CORRUPT_BKPT;
      }else{
        /* The current transaction allocated pages pMap->iFirst through
        ** nPage (inclusive) at the end of the database file. Meanwhile,
................................................................................
  int rc = SQLITE_OK;
  if( p->inTrans==TRANS_WRITE ){
    BtShared *pBt = p->pBt;
    sqlite3BtreeEnter(p);

#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum ){
      assert( ISUNLOCKED==0 );
      rc = autoVacuumCommit(pBt);
      if( rc!=SQLITE_OK ){
        sqlite3BtreeLeave(p);
        return rc;
      }
    }
    if( pBt->bDoTruncate ){
      sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
    }
#endif
    if( rc==SQLITE_OK && ISUNLOCKED ){
      rc = btreeFixUnlocked(p);
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
    }
    sqlite3BtreeLeave(p);
  }
................................................................................

    /* Set the current transaction state to TRANS_NONE and unlock the 
    ** pager if this call closed the only read or write transaction.  */
    p->inTrans = TRANS_NONE;
    unlockBtreeIfUnused(pBt);
  }

  /* If this was an UNLOCKED transaction, delete the pBt->pMap object */
  btreePtrmapDelete(pBt);
  btreeIntegrity(p);
}

/*
** Commit the transaction currently in progress.
**
................................................................................
  assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) );
  pPage1 = pBt->pPage1;
  mxPage = btreePagecount(pBt);
  /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
  ** stores stores the total number of pages on the freelist. */
  n = get4byte(&pPage1->aData[36]);
  testcase( n==mxPage-1 );
  if( ISUNLOCKED==0 && n>=mxPage ){
    return SQLITE_CORRUPT_BKPT;
  }

  /* Ensure page 1 is writable. This function will either change the number
  ** of pages in the free-list or the size of the database file. Since both
  ** of these operations involve modifying page 1 header fields, page 1
  ** will definitely be written by this transaction. If this is an UNLOCKED
  ** transaction, ensure the BtreePtrmap structure has been allocated.  */
  rc = sqlite3PagerWrite(pPage1->pDbPage);
  if( rc ) return rc;

  if( n>0 ){
    /* There are pages on the freelist.  Reuse one of those pages. */
    Pgno iTrunk;
................................................................................
    u32 nSearch = 0;   /* Count of the number of search attempts */
    
    /* If eMode==BTALLOC_EXACT and a query of the pointer-map
    ** shows that the page 'nearby' is somewhere on the free-list, then
    ** the entire-list will be searched for that page.
    */
    if( eMode==BTALLOC_EXACT ){
      assert( ISAUTOVACUUM!=ISUNLOCKED );
      if( ISAUTOVACUUM ){
        if( nearby<=mxPage ){
          u8 eType;
          assert( nearby>0 );
          assert( pBt->autoVacuum );
          rc = ptrmapGet(pBt, nearby, &eType, 0);
          if( rc ) return rc;
................................................................................
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }

/*
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** Or, if this is an UNLOCKED transaction on a wal-mode database, the WRITER
** lock on the wal file. In this case this function also checks that the
** UNLOCKED transaction can be safely committed (does not commit with any
** other transaction committed since it was opened).
**
** SQLITE_OK is returned if successful. SQLITE_BUSY if the required locks
** cannot be obtained due to a conflicting lock. If the locks cannot be
** obtained for an UNLOCKED transaction due to a conflict with an already
** committed transaction, SQLITE_BUSY_SNAPSHOT is returned. Otherwise, if
** some other error (OOM, IO, etc.) occurs, the relevant SQLite error code
** is returned.
*/
int sqlite3BtreeExclusiveLock(Btree *p){
  int rc;
  BtShared *pBt = p->pBt;
  assert( p->inTrans==TRANS_WRITE && pBt->pPage1 );
  sqlite3BtreeEnter(p);
  rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage);
  sqlite3BtreeLeave(p);
  return rc;
}







|


|







 







|







|







 







|







 







|

|




|
|







 







|

|







 







|







 







|







 







|







 







|










|







 







|







 







|






|







 







|







 







|

|




|













436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
....
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
....
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
....
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
....
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
....
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
....
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
....
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
....
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
....
5856
5857
5858
5859
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
....
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
....
9970
9971
9972
9973
9974
9975
9976
9977
9978
9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
    }
  }
}

#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;
typedef struct PtrmapEntry PtrmapEntry;
struct PtrmapEntry {
  Pgno parent;
................................................................................
      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;
  BtreePtrmap *pMap = sqlite3_malloc(sizeof(BtreePtrmap));
  assert( pBt->pMap==0 && sqlite3PagerIsConcurrent(pBt->pPager) );
  if( pMap==0 ){
    rc = SQLITE_NOMEM;
  }else{
    memset(&pBt->pPage1->aData[32], 0, sizeof(u32)*2);
    memset(pMap, 0, sizeof(BtreePtrmap));
    pMap->iFirst = pBt->nPage + 1;
    pBt->pMap = pMap;
................................................................................

  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 );
................................................................................
    */
    while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );

    if( rc==SQLITE_OK && wrflag ){
      if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
        rc = SQLITE_READONLY;
      }else{
        int exFlag = (p->db->bConcurrent && !ISAUTOVACUUM) ? -1 : (wrflag>1);
        int bSubjInMem = sqlite3TempInMemory(p->db);
        assert( p->db->bConcurrent==0 || wrflag==1 );
        rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem);
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }
#ifdef SQLITE_ENABLE_CONCURRENT
        if( rc==SQLITE_OK && sqlite3PagerIsConcurrent(pBt->pPager) ){
          rc = btreePtrmapAllocate(pBt);
        }
#endif
      }
    }
  
    if( rc!=SQLITE_OK ){
................................................................................
  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.
**
** If pnCurrent is NULL, then all pages in the range are moved to currently
** free locations (i.e. free-list entries) within the database file before page
................................................................................
    }
  }
  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;
  MemPage *pPage1 = pBt->pPage1;
  u8 *p1 = pPage1->aData;
................................................................................
  ** or freed by this transaction. In this case no special handling is 
  ** required. Otherwise, if page 1 is dirty, proceed.  */
  BtreePtrmap *pMap = pBt->pMap;
  Pgno iTrunk = get4byte(&p1[32]);
  Pgno nPage = btreePagecount(pBt);
  u32 nFree = get4byte(&p1[36]);

  assert( sqlite3PagerIsConcurrent(pPager) );
  assert( pBt->pMap );
  rc = sqlite3PagerUpgradeSnapshot(pPager, pPage1->pDbPage);
  assert( p1==pPage1->aData );

  if( rc==SQLITE_OK ){
    Pgno nHPage = get4byte(&p1[28]);
    Pgno nFin = nHPage;         /* Size of db after transaction merge */
................................................................................
          }
          sqlite3PagerUnref(pTrunk);
        };
      }

      if( nHPage<(pMap->iFirst-1) ){
        /* The database consisted of (pMap->iFirst-1) pages when the current
        ** concurrent transaction was opened. And an concurrent transaction may
        ** not be executed on an auto-vacuum database - so the db should 
        ** not have shrunk since the transaction was opened. Therefore nHPage
        ** should be set to (pMap->iFirst-1) or greater. */
        rc = SQLITE_CORRUPT_BKPT;
      }else{
        /* The current transaction allocated pages pMap->iFirst through
        ** nPage (inclusive) at the end of the database file. Meanwhile,
................................................................................
  int rc = SQLITE_OK;
  if( p->inTrans==TRANS_WRITE ){
    BtShared *pBt = p->pBt;
    sqlite3BtreeEnter(p);

#ifndef SQLITE_OMIT_AUTOVACUUM
    if( pBt->autoVacuum ){
      assert( ISCONCURRENT==0 );
      rc = autoVacuumCommit(pBt);
      if( rc!=SQLITE_OK ){
        sqlite3BtreeLeave(p);
        return rc;
      }
    }
    if( pBt->bDoTruncate ){
      sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
    }
#endif
    if( rc==SQLITE_OK && ISCONCURRENT ){
      rc = btreeFixUnlocked(p);
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
    }
    sqlite3BtreeLeave(p);
  }
................................................................................

    /* Set the current transaction state to TRANS_NONE and unlock the 
    ** pager if this call closed the only read or write transaction.  */
    p->inTrans = TRANS_NONE;
    unlockBtreeIfUnused(pBt);
  }

  /* If this was an CONCURRENT transaction, delete the pBt->pMap object */
  btreePtrmapDelete(pBt);
  btreeIntegrity(p);
}

/*
** Commit the transaction currently in progress.
**
................................................................................
  assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) );
  pPage1 = pBt->pPage1;
  mxPage = btreePagecount(pBt);
  /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
  ** stores stores the total number of pages on the freelist. */
  n = get4byte(&pPage1->aData[36]);
  testcase( n==mxPage-1 );
  if( ISCONCURRENT==0 && n>=mxPage ){
    return SQLITE_CORRUPT_BKPT;
  }

  /* Ensure page 1 is writable. This function will either change the number
  ** of pages in the free-list or the size of the database file. Since both
  ** of these operations involve modifying page 1 header fields, page 1
  ** will definitely be written by this transaction. If this is an CONCURRENT
  ** transaction, ensure the BtreePtrmap structure has been allocated.  */
  rc = sqlite3PagerWrite(pPage1->pDbPage);
  if( rc ) return rc;

  if( n>0 ){
    /* There are pages on the freelist.  Reuse one of those pages. */
    Pgno iTrunk;
................................................................................
    u32 nSearch = 0;   /* Count of the number of search attempts */
    
    /* If eMode==BTALLOC_EXACT and a query of the pointer-map
    ** shows that the page 'nearby' is somewhere on the free-list, then
    ** the entire-list will be searched for that page.
    */
    if( eMode==BTALLOC_EXACT ){
      assert( ISAUTOVACUUM!=ISCONCURRENT );
      if( ISAUTOVACUUM ){
        if( nearby<=mxPage ){
          u8 eType;
          assert( nearby>0 );
          assert( pBt->autoVacuum );
          rc = ptrmapGet(pBt, nearby, &eType, 0);
          if( rc ) return rc;
................................................................................
int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }

/*
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** Or, if this is an CONCURRENT transaction on a wal-mode database, the WRITER
** lock on the wal file. In this case this function also checks that the
** CONCURRENT transaction can be safely committed (does not commit with any
** other transaction committed since it was opened).
**
** SQLITE_OK is returned if successful. SQLITE_BUSY if the required locks
** cannot be obtained due to a conflicting lock. If the locks cannot be
** obtained for an CONCURRENT transaction due to a conflict with an already
** committed transaction, SQLITE_BUSY_SNAPSHOT is returned. Otherwise, if
** some other error (OOM, IO, etc.) occurs, the relevant SQLite error code
** is returned.
*/
int sqlite3BtreeExclusiveLock(Btree *p){
  int rc;
  BtShared *pBt = p->pBt;
  assert( p->inTrans==TRANS_WRITE && pBt->pPage1 );
  sqlite3BtreeEnter(p);
  rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage);
  sqlite3BtreeLeave(p);
  return rc;
}

Changes to src/btreeInt.h.

444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
#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_UNLOCKED
  BtreePtrmap *pMap;
#endif
};

/*
** Allowed values for BtShared.btsFlags
*/
................................................................................
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
#define ISAUTOVACUUM (pBt->autoVacuum)
#else
#define ISAUTOVACUUM 0
#endif

#ifdef SQLITE_ENABLE_UNLOCKED
# define ISUNLOCKED (pBt->pMap!=0)
#else
# define ISUNLOCKED 0
#endif

#define REQUIRE_PTRMAP (ISAUTOVACUUM || ISUNLOCKED)

/*
** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information.
**
** The aRef[] array is allocated so that there is 1 bit for each page in
** the database. As the integrity-check proceeds, for each page used in







|







 







|
|

|


|







444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
#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
*/
................................................................................
*/
#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.
**
** The aRef[] array is allocated so that there is 1 bit for each page in
** the database. As the integrity-check proceeds, for each page used in

Changes to src/build.c.

3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
  if( !v ) return;
  if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
    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==TK_UNLOCKED));
}

/*
** Commit a transaction
*/
void sqlite3CommitTransaction(Parse *pParse){
  Vdbe *v;







|







3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
  if( !v ) return;
  if( type==TK_IMMEDIATE || type==TK_EXCLUSIVE ){
    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==TK_CONCURRENT));
}

/*
** Commit a transaction
*/
void sqlite3CommitTransaction(Parse *pParse){
  Vdbe *v;

Changes to src/pager.c.

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
....
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
....
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
....
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
....
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
....
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
....
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
....
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
....
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
....
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
....
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
....
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
  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_UNLOCKED
  Bitvec *pAllRead;           /* Pages read within current UNLOCKED 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 */
  sqlite3_backup *pBackup;    /* Pointer to list of ongoing backup processes */
................................................................................

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
#ifdef SQLITE_ENABLE_UNLOCKED
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pAllRead = 0;
#endif
}

/*
** This function is a no-op if the pager is in exclusive mode and not
................................................................................
  **   + 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 UNLOCKED 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_UNLOCKED
  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 UNLOCKED" transaction, the page 
    ** cannot be flushed to disk. Return early in this case. */
#ifdef SQLITE_ENABLE_UNLOCKED
    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 UNLOCKED 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_UNLOCKED
  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. 
................................................................................
**
** If the exFlag argument is 0, then acquire at least a RESERVED
** lock on the database file. If exFlag is >0, then acquire at least
** an EXCLUSIVE lock. If such a lock is already held, no locking 
** functions need be called.
**
** If (exFlag<0) and the database is in WAL mode, do not take any locks.
** The transaction will run in UNLOCKED mode instead.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
** within this transaction will be opened as an in-memory file. This
** has no effect if the sub-journal is already opened (as it may be when
** running in exclusive mode) or if the transaction does not require a
** sub-journal. If the subjInMemory argument is zero, then any required
** sub-journal is implemented in-memory if pPager is an in-memory database, 
................................................................................

  if( pPager->errCode ) return pPager->errCode;
  assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
  pPager->subjInMemory = (u8)subjInMemory;

  if( ALWAYS(pPager->eState==PAGER_READER) ){
    assert( pPager->pInJournal==0 );
#ifdef SQLITE_ENABLE_UNLOCKED
    assert( pPager->pAllRead==0 );
#endif

    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
................................................................................
      }

      /* Grab the write lock on the log file. If successful, upgrade to
      ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
#ifdef SQLITE_ENABLE_UNLOCKED
      if( exFlag<0 ){
        pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
        if( pPager->pAllRead==0 ){
          rc = SQLITE_NOMEM;
        }
      }else
#endif
................................................................................
}

/*
** 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_UNLOCKED) || !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
................................................................................
}

/*
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** Or, if this is a non-UNLOCKED transaction on a wal-mode database, this
** function is a no-op.
**
** If this is an UNLOCKED transaction on a wal-mode database, this function
** attempts to obtain the WRITER lock on the wal file and also checks to
** see that the transaction can be safely committed (does not commit with 
** any other transaction committed since it was opened).
**
** If the required locks are already held or successfully obtained and
** the transaction can be committed, SQLITE_OK is returned. If a required lock
** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
** is UNLOCKED and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
** and SQLite error code is returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  int rc = SQLITE_OK;
  assert( pPager->eState==PAGER_WRITER_CACHEMOD 
       || 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_UNLOCKED
  else{
    if( pPager->pAllRead ){
      /* This is an UNLOCKED 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_UNLOCKED
/*
** This function is called as part of committing an UNLOCKED 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
** contents of page 1 so that the client is operating on the snapshot 
** at the head of the wal file.
................................................................................
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
  pPager->dbSize = nSz;
}

/*
** Return true if this pager is currently within an UNLOCKED transaction.
*/
int sqlite3PagerIsUnlocked(Pager *pPager){
  return pPager->pAllRead!=0;
}

/*
** 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_UNLOCKED */


/*
** 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
668
....
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
....
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
....
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
....
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
....
5589
5590
5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
....
5608
5609
5610
5611
5612
5613
5614
5615
5616
5617
5618
5619
5620
5621
5622
....
5629
5630
5631
5632
5633
5634
5635
5636
5637
5638
5639
5640
5641
5642
5643
....
5945
5946
5947
5948
5949
5950
5951
5952
5953
5954
5955
5956
5957
5958
5959
....
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
6130
6131
6132
6133
6134
6135
6136
6137
6138
6139
6140
6141
6142
6143
....
6144
6145
6146
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156
6157
6158
6159
6160
....
6181
6182
6183
6184
6185
6186
6187
6188
6189
6190
6191
6192
6193
6194
6195
6196
6197
6198
6199
6200
6201
6202
6203
6204
6205
6206
6207
6208
6209
6210
  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 */
  sqlite3_backup *pBackup;    /* Pointer to list of ongoing backup processes */
................................................................................

/*
** Free the Pager.pInJournal and Pager.pAllRead bitvec objects.
*/
static void pagerFreeBitvecs(Pager *pPager){
  sqlite3BitvecDestroy(pPager->pInJournal);
  pPager->pInJournal = 0;
#ifdef SQLITE_ENABLE_CONCURRENT
  sqlite3BitvecDestroy(pPager->pAllRead);
  pPager->pAllRead = 0;
#endif
}

/*
** This function is a no-op if the pager is in exclusive mode and not
................................................................................
  **   + 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. 
................................................................................
**
** If the exFlag argument is 0, then acquire at least a RESERVED
** lock on the database file. If exFlag is >0, then acquire at least
** an EXCLUSIVE lock. If such a lock is already held, no locking 
** functions need be called.
**
** If (exFlag<0) and the database is in WAL mode, do not take any locks.
** The transaction will run in CONCURRENT mode instead.
**
** If the subjInMemory argument is non-zero, then any sub-journal opened
** within this transaction will be opened as an in-memory file. This
** has no effect if the sub-journal is already opened (as it may be when
** running in exclusive mode) or if the transaction does not require a
** sub-journal. If the subjInMemory argument is zero, then any required
** sub-journal is implemented in-memory if pPager is an in-memory database, 
................................................................................

  if( pPager->errCode ) return pPager->errCode;
  assert( pPager->eState>=PAGER_READER && pPager->eState<PAGER_ERROR );
  pPager->subjInMemory = (u8)subjInMemory;

  if( ALWAYS(pPager->eState==PAGER_READER) ){
    assert( pPager->pInJournal==0 );
#ifdef SQLITE_ENABLE_CONCURRENT
    assert( pPager->pAllRead==0 );
#endif

    if( pagerUseWal(pPager) ){
      /* If the pager is configured to use locking_mode=exclusive, and an
      ** exclusive lock on the database is not already held, obtain it now.
      */
................................................................................
      }

      /* Grab the write lock on the log file. If successful, upgrade to
      ** PAGER_RESERVED state. Otherwise, return an error code to the caller.
      ** The busy-handler is not invoked if another connection already
      ** holds the write-lock. If possible, the upper layer will call it.
      */
#ifdef SQLITE_ENABLE_CONCURRENT
      if( exFlag<0 ){
        pPager->pAllRead = sqlite3BitvecCreate(pPager->dbSize);
        if( pPager->pAllRead==0 ){
          rc = SQLITE_NOMEM;
        }
      }else
#endif
................................................................................
}

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

/*
** This function is called to ensure that all locks required to commit the
** current write-transaction to the database file are held. If the db is
** in rollback mode, this means the EXCLUSIVE lock on the database file.
**
** Or, if this is a non-CONCURRENT transaction on a wal-mode database, this
** function is a no-op.
**
** If this is an CONCURRENT transaction on a wal-mode database, this function
** attempts to obtain the WRITER lock on the wal file and also checks to
** see that the transaction can be safely committed (does not commit with 
** any other transaction committed since it was opened).
**
** If the required locks are already held or successfully obtained and
** the transaction can be committed, SQLITE_OK is returned. If a required lock
** cannot be obtained, SQLITE_BUSY is returned. Or, if the current transaction
** is CONCURRENT and cannot be committed due to a conflict, SQLITE_BUSY_SNAPSHOT
** is returned. Otherwise, if some other error occurs (IO error, OOM etc.),
** and SQLite error code is returned.
*/
int sqlite3PagerExclusiveLock(Pager *pPager, PgHdr *pPage1){
  int rc = SQLITE_OK;
  assert( pPager->eState==PAGER_WRITER_CACHEMOD 
       || 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
** contents of page 1 so that the client is operating on the snapshot 
** at the head of the wal file.
................................................................................
** Set the in-memory cache of the database file size to nSz pages.
*/
void sqlite3PagerSetDbsize(Pager *pPager, Pgno nSz){
  pPager->dbSize = nSz;
}

/*
** Return true if this pager is currently within an CONCURRENT transaction.
*/
int sqlite3PagerIsConcurrent(Pager *pPager){
  return pPager->pAllRead!=0;
}

/*
** 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).

Changes to src/pager.h.

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

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

void sqlite3PagerRekey(DbPage*, Pgno, u16);

void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerIsUnlocked(Pager*);
int sqlite3PagerIswriteable(DbPage*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);

#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
void *sqlite3PagerCodec(DbPage *);
#endif







|







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

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

void sqlite3PagerRekey(DbPage*, Pgno, u16);

void sqlite3PagerDropExclusiveLock(Pager*);
int sqlite3PagerIsConcurrent(Pager*);
int sqlite3PagerIswriteable(DbPage*);
int sqlite3PagerUpgradeSnapshot(Pager *pPager, DbPage*);
void sqlite3PagerSetDbsize(Pager *pPager, Pgno);

#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
void *sqlite3PagerCodec(DbPage *);
#endif

Changes to src/parse.y.

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= .             {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X).  {A = @X;}
transtype(A) ::= IMMEDIATE(X). {A = @X;}
transtype(A) ::= EXCLUSIVE(X). {A = @X;}
transtype(A) ::= UNLOCKED(X).  {A = @X;}
cmd ::= COMMIT trans_opt.      {sqlite3CommitTransaction(pParse);}
cmd ::= END trans_opt.         {sqlite3CommitTransaction(pParse);}
cmd ::= ROLLBACK trans_opt.    {sqlite3RollbackTransaction(pParse);}

savepoint_opt ::= SAVEPOINT.
savepoint_opt ::= .
cmd ::= SAVEPOINT nm(X). {







|







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
trans_opt ::= TRANSACTION.
trans_opt ::= TRANSACTION nm.
%type transtype {int}
transtype(A) ::= .             {A = TK_DEFERRED;}
transtype(A) ::= DEFERRED(X).  {A = @X;}
transtype(A) ::= IMMEDIATE(X). {A = @X;}
transtype(A) ::= EXCLUSIVE(X). {A = @X;}
transtype(A) ::= CONCURRENT(X).  {A = @X;}
cmd ::= COMMIT trans_opt.      {sqlite3CommitTransaction(pParse);}
cmd ::= END trans_opt.         {sqlite3CommitTransaction(pParse);}
cmd ::= ROLLBACK trans_opt.    {sqlite3RollbackTransaction(pParse);}

savepoint_opt ::= SAVEPOINT.
savepoint_opt ::= .
cmd ::= SAVEPOINT nm(X). {

Changes to src/sqliteInt.h.

1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  i64 szMmap;                   /* Default mmap_size setting */
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 bUnlocked;                 /* Current transaction is "UNLOCKED" */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  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() */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */







|







1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
  i64 szMmap;                   /* Default mmap_size setting */
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 bConcurrent;               /* Current transaction is "CONCURRENT" */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  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() */
  u8 isTransactionSavepoint;    /* True if the outermost savepoint is a TS */

Changes to src/test_config.c.

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

#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_UNLOCKED
  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "unlocked", "0", TCL_GLOBAL_ONLY);
#endif

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







|
|

|







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

#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);
#else
  Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY);
#endif

Changes to src/vacuum.c.

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  ** database. No locks are held on any other files (since the main file
  ** was committed at the btree level). So it safe to end the transaction
  ** by manually setting the autoCommit flag to true and detaching the
  ** vacuum database. The vacuum_db journal file is deleted when the pager
  ** is closed by the DETACH.
  */
  db->autoCommit = 1;
  assert( db->bUnlocked==0 );

  if( pDb ){
    sqlite3BtreeClose(pDb->pBt);
    pDb->pBt = 0;
    pDb->pSchema = 0;
  }








|







352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  ** database. No locks are held on any other files (since the main file
  ** was committed at the btree level). So it safe to end the transaction
  ** by manually setting the autoCommit flag to true and detaching the
  ** vacuum database. The vacuum_db journal file is deleted when the pager
  ** is closed by the DETACH.
  */
  db->autoCommit = 1;
  assert( db->bConcurrent==0 );

  if( pDb ){
    sqlite3BtreeClose(pDb->pBt);
    pDb->pBt = 0;
    pDb->pSchema = 0;
  }

Changes to src/vdbe.c.

2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
....
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
....
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
....
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
....
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
    }else{

      /* Determine whether or not this is a transaction savepoint. If so,
      ** and this is a RELEASE command, then the current transaction 
      ** is committed. 
      */
      int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
      assert( db->bUnlocked==0 || db->isTransactionSavepoint==0 );
      if( isTransaction && p1==SAVEPOINT_RELEASE ){
        if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
          goto vdbe_return;
        }
        db->autoCommit = 1;
        if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
          p->pc = (int)(pOp - aOp);
................................................................................
**
** 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.
**
** If P3 is non-zero, then this instruction is being executed as part of
** a "BEGIN UNLOCKED" command.
**
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
  int desiredAutoCommit;
  int iRollback;
  int turnOnAC;
  int bUnlocked;
  int hrc;

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

  if( turnOnAC && !iRollback 
   && (db->nVdbeWrite>0 || (db->bUnlocked && db->nVdbeActive>1))
  ){
    /* A transaction may only be committed if there are no other active
    ** writer VMs. If the transaction is UNLOCKED, then it may only be
    ** committed if there are no active VMs at all (readers or writers).
    **
    ** If this instruction is a COMMIT and the transaction may not be
    ** committed due to one of the conditions above, return an error
    ** indicating that other VMs must complete before the COMMIT can 
    ** be processed.  */
    sqlite3VdbeError(p, "cannot commit transaction - "
................................................................................
                        "SQL statements in progress");
    rc = SQLITE_BUSY;
  }else if( desiredAutoCommit!=db->autoCommit ){
    if( iRollback ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
      db->autoCommit = 1;
      db->bUnlocked = 0;
    }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
      goto vdbe_return;
    }else{
      db->autoCommit = (u8)desiredAutoCommit;
    }
    hrc = sqlite3VdbeHalt(p);
    if( (hrc & 0xFF)==SQLITE_BUSY ){
      p->pc = (int)(pOp - aOp);
      db->autoCommit = (u8)(1-desiredAutoCommit);
      p->rc = hrc;
      rc = SQLITE_BUSY;
      goto vdbe_return;
    }
    db->bUnlocked = (u8)bUnlocked;
    assert( db->nStatement==0 );
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
    }else{
      rc = SQLITE_ERROR;
    }
................................................................................
  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_UNLOCKED
  if( db->bUnlocked 
   && (pOp->p2==BTREE_USER_VERSION || pOp->p2==BTREE_APPLICATION_ID)
  ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, "cannot modify %s within UNLOCKED transaction",
        pOp->p2==BTREE_USER_VERSION ? "user_version" : "application_id"
    );
    break; 
  }
#endif
  /* See note about index shifting on OP_ReadCookie */
  rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
  if( pOp->p2==BTREE_SCHEMA_VERSION ){
    /* When the schema cookie changes, record the new cookie internally */
    assert( db->bUnlocked==0 );
    pDb->pSchema->schema_cookie = (int)pIn3->u.i;
    db->flags |= SQLITE_InternChanges;
  }else if( pOp->p2==BTREE_FILE_FORMAT ){
    /* Record changes in the file format */
    pDb->pSchema->file_format = (u8)pIn3->u.i;
  }
  if( pOp->p1==1 ){
................................................................................
** 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_UNLOCKED
  if( isWriteLock && db->bUnlocked && pOp->p2==1 ){
    rc = SQLITE_ERROR;
    sqlite3VdbeError(p, 
        "cannot modify database schema within UNLOCKED transaction");
    rc = SQLITE_ERROR;
    break;
  }
#endif
  if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){
    int p1 = pOp->p1; 
    assert( p1>=0 && p1<db->nDb );







|







 







|







|




|



|
|




|


|







 







|













|







 







|
|



|









|







 







|
|


|







2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
....
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
....
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
....
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
....
6111
6112
6113
6114
6115
6116
6117
6118
6119
6120
6121
6122
6123
6124
6125
6126
6127
6128
6129
    }else{

      /* Determine whether or not this is a transaction savepoint. If so,
      ** and this is a RELEASE command, then the current transaction 
      ** is committed. 
      */
      int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
      assert( db->bConcurrent==0 || db->isTransactionSavepoint==0 );
      if( isTransaction && p1==SAVEPOINT_RELEASE ){
        if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
          goto vdbe_return;
        }
        db->autoCommit = 1;
        if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
          p->pc = (int)(pOp - aOp);
................................................................................
**
** 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.
**
** If P3 is non-zero, then this instruction is being executed as part of
** a "BEGIN CONCURRENT" command.
**
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
  int desiredAutoCommit;
  int iRollback;
  int turnOnAC;
  int bConcurrent;
  int hrc;

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

  if( turnOnAC && !iRollback 
   && (db->nVdbeWrite>0 || (db->bConcurrent && db->nVdbeActive>1))
  ){
    /* A transaction may only be committed if there are no other active
    ** writer VMs. If the transaction is CONCURRENT, then it may only be
    ** committed if there are no active VMs at all (readers or writers).
    **
    ** If this instruction is a COMMIT and the transaction may not be
    ** committed due to one of the conditions above, return an error
    ** indicating that other VMs must complete before the COMMIT can 
    ** be processed.  */
    sqlite3VdbeError(p, "cannot commit transaction - "
................................................................................
                        "SQL statements in progress");
    rc = SQLITE_BUSY;
  }else if( desiredAutoCommit!=db->autoCommit ){
    if( iRollback ){
      assert( desiredAutoCommit==1 );
      sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
      db->autoCommit = 1;
      db->bConcurrent = 0;
    }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
      goto vdbe_return;
    }else{
      db->autoCommit = (u8)desiredAutoCommit;
    }
    hrc = sqlite3VdbeHalt(p);
    if( (hrc & 0xFF)==SQLITE_BUSY ){
      p->pc = (int)(pOp - aOp);
      db->autoCommit = (u8)(1-desiredAutoCommit);
      p->rc = hrc;
      rc = SQLITE_BUSY;
      goto vdbe_return;
    }
    db->bConcurrent = (u8)bConcurrent;
    assert( db->nStatement==0 );
    sqlite3CloseSavepoints(db);
    if( p->rc==SQLITE_OK ){
      rc = SQLITE_DONE;
    }else{
      rc = SQLITE_ERROR;
    }
................................................................................
  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"
    );
    break; 
  }
#endif
  /* See note about index shifting on OP_ReadCookie */
  rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, (int)pIn3->u.i);
  if( pOp->p2==BTREE_SCHEMA_VERSION ){
    /* When the schema cookie changes, record the new cookie internally */
    assert( db->bConcurrent==0 );
    pDb->pSchema->schema_cookie = (int)pIn3->u.i;
    db->flags |= SQLITE_InternChanges;
  }else if( pOp->p2==BTREE_FILE_FORMAT ){
    /* Record changes in the file format */
    pDb->pSchema->file_format = (u8)pIn3->u.i;
  }
  if( pOp->p1==1 ){
................................................................................
** 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");
    rc = SQLITE_ERROR;
    break;
  }
#endif
  if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){
    int p1 = pOp->p1; 
    assert( p1>=0 && p1<db->nDb );

Changes to src/vdbeaux.c.

2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
....
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
....
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
....
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
    if( sqlite3BtreeIsInTrans(pBt) ){
      needXcommit = 1;
      if( i!=1 ) nTrans++;
      rc = sqlite3BtreeExclusiveLock(pBt);
    }
  }

#ifdef SQLITE_ENABLE_UNLOCKED
  if( db->bUnlocked && (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) ){
        sqlite3BtreeEnter(pBt);
................................................................................
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.
          */
          sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
          sqlite3CloseSavepoints(db);
          db->autoCommit = 1;
          db->bUnlocked = 0;
          p->nChange = 0;
        }
      }
    }

    /* Check for immediate foreign key violations. */
    if( p->rc==SQLITE_OK ){
................................................................................
        eStatementOp = SAVEPOINT_RELEASE;
      }else if( p->errorAction==OE_Abort ){
        eStatementOp = SAVEPOINT_ROLLBACK;
      }else{
        sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
        db->bUnlocked = 0;
        p->nChange = 0;
      }
    }
  
    /* If eStatementOp is non-zero, then a statement transaction needs to
    ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
    ** do so. If this operation returns an error, and the current statement
................................................................................
          p->rc = rc;
          sqlite3DbFree(db, p->zErrMsg);
          p->zErrMsg = 0;
        }
        sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
        db->bUnlocked = 0;
        p->nChange = 0;
      }
    }
  
    /* If this was an INSERT, UPDATE or DELETE and no statement transaction
    ** has been rolled back, update the database connection change-counter. 
    */







|
|







 







|







 







|







 







|







2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
....
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
....
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
....
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
    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) ){
        sqlite3BtreeEnter(pBt);
................................................................................
        }else{
          /* We are forced to roll back the active transaction. Before doing
          ** so, abort any other statements this handle currently has active.
          */
          sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
          sqlite3CloseSavepoints(db);
          db->autoCommit = 1;
          db->bConcurrent = 0;
          p->nChange = 0;
        }
      }
    }

    /* Check for immediate foreign key violations. */
    if( p->rc==SQLITE_OK ){
................................................................................
        eStatementOp = SAVEPOINT_RELEASE;
      }else if( p->errorAction==OE_Abort ){
        eStatementOp = SAVEPOINT_ROLLBACK;
      }else{
        sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
        db->bConcurrent = 0;
        p->nChange = 0;
      }
    }
  
    /* If eStatementOp is non-zero, then a statement transaction needs to
    ** be committed or rolled back. Call sqlite3VdbeCloseStatement() to
    ** do so. If this operation returns an error, and the current statement
................................................................................
          p->rc = rc;
          sqlite3DbFree(db, p->zErrMsg);
          p->zErrMsg = 0;
        }
        sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
        sqlite3CloseSavepoints(db);
        db->autoCommit = 1;
        db->bConcurrent = 0;
        p->nChange = 0;
      }
    }
  
    /* If this was an INSERT, UPDATE or DELETE and no statement transaction
    ** has been rolled back, update the database connection change-counter. 
    */

Changes to src/wal.c.

764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
....
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
....
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
....
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
....
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
....
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
}
#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
    

/*
** Set or release locks on the WAL.  Locks are either shared or exclusive.
** A lock cannot be moved directly between shared and exclusive - it must go
** through the unlocked state first.
**
** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
*/
static int walLockShared(Wal *pWal, int lockIdx){
  int rc;
  if( pWal->exclusiveMode ) return SQLITE_OK;
  rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
................................................................................
  u32 aFrameCksum[2] = {0, 0};
  int iLock;                      /* Lock offset to lock for checkpoint */
  int nLock;                      /* Number of locks to hold */

  /* Obtain an exclusive lock on all byte in the locking range not already
  ** locked by the caller. The caller is guaranteed to have locked the
  ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
  ** If successful, the same bytes that are locked here are unlocked before
  ** this function returns.
  */
  assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
  assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 );
  assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE );
  assert( pWal->writeLock );
  iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
................................................................................
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );
  return rc;
}


#ifdef SQLITE_ENABLE_UNLOCKED
/* 
** This function is only ever called when committing a "BEGIN UNLOCKED"
** 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 unlocked transaction.
**
** This function performs three tasks:
**
**   1) It obtains the WRITER lock on the wal file,
**
**   2) It checks that there are no conflicts between the current
**      transaction and any transactions committed to the wal file since
**      it was opened, and
**
**   3) It ejects any non-dirty pages from the page-cache that have been
**      written by another client since the UNLOCKED transaction was started
**      (so as to avoid ending up with an inconsistent cache after the
**      current transaction is committed).
**
** If no error occurs and the caller may proceed with committing the 
** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
................................................................................
  */
  if( rc==SQLITE_OK ){
    WalIndexHdr head;

    if( walIndexLoadHdr(pWal, &head) ){
      /* This branch is taken if the wal-index header is corrupted. This 
      ** occurs if some other writer has crashed while committing a 
      ** transaction to this database since the current unlocked transaction
      ** was opened.  */
      rc = SQLITE_BUSY_SNAPSHOT;
    }else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){
      int iHash;
      int iLastHash = walFramePage(head.mxFrame);
      u32 iFirst = pWal->hdr.mxFrame+1;     /* First wal frame to check */
      if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){
................................................................................
              iOffset = walFrameOffset(i+iZero, sz) + WAL_FRAME_HDRSIZE + 40;
              rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset);
              if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
                rc = SQLITE_BUSY_SNAPSHOT;
              }
            }else if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){
              sqlite3_log(SQLITE_OK,
                  "cannot commit UNLOCKED transaction (conflict at page %d)",
                  (int)aPgno[i]
              );
              rc = SQLITE_BUSY_SNAPSHOT;
            }else if( (pPg = sqlite3PagerLookup(pPager, aPgno[i])) ){
              /* Page aPgno[i], which is present in the pager cache, has been
              ** modified since the current UNLOCKED transaction was started.
              ** However it was not read by the current transaction, so is not
              ** a conflict. There are two possibilities: (a) the page was
              ** allocated at the of the file by the current transaction or 
              ** (b) was present in the cache at the start of the transaction.
              **
              ** For case (a), do nothing. This page will be moved within the
              ** database file by the commit code to avoid the conflict. The
................................................................................
    }
  }

  return rc;
}

/*
** This function is called as part of committing an UNLOCKED 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
** (even if the UNLOCKED transaction ran against an older snapshot).
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3WalUpgradeSnapshot(Wal *pWal){
  int rc = SQLITE_OK;
  assert( pWal->writeLock );
  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
................................................................................
  ** 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_UNLOCKED */

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







|







 







|







 







|

|




|










|







 







|







 







|





|







 







|






|







 







|







764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
....
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
....
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
....
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
....
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
....
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
}
#endif /*defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
    

/*
** Set or release locks on the WAL.  Locks are either shared or exclusive.
** A lock cannot be moved directly between shared and exclusive - it must go
** through the concurrent state first.
**
** In locking_mode=EXCLUSIVE, all of these routines become no-ops.
*/
static int walLockShared(Wal *pWal, int lockIdx){
  int rc;
  if( pWal->exclusiveMode ) return SQLITE_OK;
  rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
................................................................................
  u32 aFrameCksum[2] = {0, 0};
  int iLock;                      /* Lock offset to lock for checkpoint */
  int nLock;                      /* Number of locks to hold */

  /* Obtain an exclusive lock on all byte in the locking range not already
  ** locked by the caller. The caller is guaranteed to have locked the
  ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
  ** If successful, the same bytes that are locked here are concurrent before
  ** this function returns.
  */
  assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
  assert( WAL_ALL_BUT_WRITE==WAL_WRITE_LOCK+1 );
  assert( WAL_CKPT_LOCK==WAL_ALL_BUT_WRITE );
  assert( pWal->writeLock );
  iLock = WAL_ALL_BUT_WRITE + pWal->ckptLock;
................................................................................
  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.
**
** This function performs three tasks:
**
**   1) It obtains the WRITER lock on the wal file,
**
**   2) It checks that there are no conflicts between the current
**      transaction and any transactions committed to the wal file since
**      it was opened, and
**
**   3) It ejects any non-dirty pages from the page-cache that have been
**      written by another client since the CONCURRENT transaction was started
**      (so as to avoid ending up with an inconsistent cache after the
**      current transaction is committed).
**
** If no error occurs and the caller may proceed with committing the 
** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
................................................................................
  */
  if( rc==SQLITE_OK ){
    WalIndexHdr head;

    if( walIndexLoadHdr(pWal, &head) ){
      /* This branch is taken if the wal-index header is corrupted. This 
      ** occurs if some other writer has crashed while committing a 
      ** transaction to this database since the current concurrent transaction
      ** was opened.  */
      rc = SQLITE_BUSY_SNAPSHOT;
    }else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){
      int iHash;
      int iLastHash = walFramePage(head.mxFrame);
      u32 iFirst = pWal->hdr.mxFrame+1;     /* First wal frame to check */
      if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){
................................................................................
              iOffset = walFrameOffset(i+iZero, sz) + WAL_FRAME_HDRSIZE + 40;
              rc = sqlite3OsRead(pWal->pWalFd, aNew, sizeof(aNew), iOffset);
              if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
                rc = SQLITE_BUSY_SNAPSHOT;
              }
            }else if( sqlite3BitvecTestNotNull(pAllRead, aPgno[i]) ){
              sqlite3_log(SQLITE_OK,
                  "cannot commit CONCURRENT transaction (conflict at page %d)",
                  (int)aPgno[i]
              );
              rc = SQLITE_BUSY_SNAPSHOT;
            }else if( (pPg = sqlite3PagerLookup(pPager, aPgno[i])) ){
              /* Page aPgno[i], which is present in the pager cache, has been
              ** modified since the current CONCURRENT transaction was started.
              ** However it was not read by the current transaction, so is not
              ** a conflict. There are two possibilities: (a) the page was
              ** allocated at the of the file by the current transaction or 
              ** (b) was present in the cache at the start of the transaction.
              **
              ** For case (a), do nothing. This page will be moved within the
              ** database file by the commit code to avoid the conflict. The
................................................................................
    }
  }

  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
** (even if the CONCURRENT transaction ran against an older snapshot).
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
int sqlite3WalUpgradeSnapshot(Wal *pWal){
  int rc = SQLITE_OK;
  assert( pWal->writeLock );
  memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
................................................................................
  ** 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 ){

Name change from test/unlocked.test to test/concurrent.test.

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
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
101
102
103
104
105
106
107
108
109
110
111
112
113
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
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
...
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
#
#***********************************************************************
#

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

ifcapable !unlocked {
  finish_test
  return
}

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test 1.1 {
  CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES(1, 'abcd');
  COMMIT;
}

do_execsql_test 1.2 {
  SELECT * FROM t1;
} {1 abcd}

do_execsql_test 1.3 {
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES(2, 'efgh');
  ROLLBACK;
}

do_execsql_test 1.4 {
  SELECT * FROM t1;
} {1 abcd}


#-------------------------------------------------------------------------
# UNLOCKED transactions cannot do cache spills.
#
foreach {tn trans spill} {
  1 {BEGIN UNLOCKED}  0
  2 {BEGIN}           1
} {
  do_test 1.5.$tn {
    sqlite3 db2 test.db
    set walsz [file size test.db-wal]

    execsql { PRAGMA cache_size = 10 } db2
................................................................................
  } [expr !$spill]

  execsql ROLLBACK db2
  db2 close
}

#-------------------------------------------------------------------------
# UNLOCKED transactions man not be committed while there are active
# readers.
do_execsql_test 1.6.setup {
  DROP TABLE t1;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  INSERT INTO t1 VALUES(3, 4);
  INSERT INTO t1 VALUES(5, 6);
}
foreach {tn trans commit_ok} {
  1 {BEGIN UNLOCKED}  0
  2 {BEGIN}           1
} {
  do_test 1.6.$tn.1 {
    set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
    sqlite3_step $stmt
  } SQLITE_ROW
  do_test 1.6.$tn.2 {
................................................................................
  }

  sqlite3_finalize $stmt
  catchsql ROLLBACK
}

#-------------------------------------------------------------------------
# UNLOCKED transactions may not modify the db schema.
#
foreach {tn sql} {
  1 { CREATE TABLE xx(a, b) }
  2 { DROP TABLE t1 }
  3 { CREATE INDEX i1 ON t1(a) }
  4 { CREATE VIEW v1 AS SELECT * FROM t1 }
  5 { CREATE TEMP TABLE xx(a, b) }
} {
  do_catchsql_test 1.7.$tn.1 "
    BEGIN UNLOCKED;
    $sql
  " {1 {cannot modify database schema within UNLOCKED transaction}}

  do_execsql_test 1.7.$tn.2 {
    SELECT sql FROM sqlite_master;
    SELECT sql FROM sqlite_temp_master;
  } {{CREATE TABLE t1(a, b)}}

  do_execsql_test 1.7.$tn.3 COMMIT
}

#-------------------------------------------------------------------------
# If an auto-vacuum database is written within an UNLOCKED transaction, it
# is handled in the same way as for a non-UNLOCKED transaction.
#
reset_db
do_execsql_test 1.8.1 {
  PRAGMA auto_vacuum = 1;
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('x', 'y');
} {wal}

do_execsql_test 1.8.2 {
  BEGIN UNLOCKED;
    SELECT * FROM t1;
  COMMIT;
} {x y}

do_catchsql_test 1.8.3 {
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES('a', 'b');
} {0 {}}

do_test 1.8.4 {
  sqlite3 db2 test.db
  catchsql {
    BEGIN UNLOCKED;
      INSERT INTO t1 VALUES('c', 'd');
  } db2
} {1 {database is locked}}

do_test 1.8.5 {
  db eval COMMIT
  db2 eval COMMIT
} {}

do_multiclient_test tn {

  #-----------------------------------------------------------------------
  # 1. Start an UNLOCKED transaction using [db1].
  #
  # 2. Start and then rollback a regular transaction using [db2]. This 
  #    can be done as the ongoing [db1] transaction is UNLOCKED.
  #
  # 3. The [db1] transaction can now be committed, as [db2] has relinquished
  #    the write lock.
  #
  do_test 2.$tn.1.1 {
    sql1 { 
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
      INSERT INTO t1 VALUES(1, 'one');
    }
    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(2, 'two');
    }
    code1 { sqlite3_get_autocommit db }
  } 0

  do_test 2.$tn.1.2 {
    sql2 {
................................................................................

  do_test 2.$tn.1.3 {
    sql1 COMMIT
    sql2 { SELECT * FROM t1 }
  } {1 one 2 two}
  
  #-----------------------------------------------------------------------
  # 1. Start an UNLOCKED transaction using [db1].
  #
  # 2. Commit a transaction using [db2].
  #
  # 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned,
  #    and the transaction is not rolled back.
  #
  do_test 2.$tn.2.1 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(-1, 'hello world');
    }
  } {}

  do_test 2.$tn.2.2 {
    sql2 {
      INSERT INTO t1 VALUES(3, 'three');
................................................................................
    sql1 {
      SELECT * FROM t1;
      ROLLBACK;
    }
  } {-1 {hello world} 1 one 2 two}
  
  #-----------------------------------------------------------------------
  # 1. Start an UNLOCKED transaction using [db1].
  #
  # 2. Open a transaction using [db2].
  #
  # 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned,
  #    and the transaction is not rolled back.
  #
  # 4. Have [db2] roll its transaction back. Then check that [db1] can
  #    commit.
  #
  do_test 2.$tn.3.1 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(4, 'four');
    }
  } {}

  do_test 2.$tn.3.2 {
    sql2 {
      BEGIN;
................................................................................
  # 3. See if it worked.
  #
  do_test 2.$tn.4.1 {
    sql1 { CREATE TABLE t2(a, b) }
  } {}
  do_test 2.$tn.4.2 {
    sql2 {
      BEGIN UNLOCKED;
        INSERT INTO t2 VALUES('i', 'n');
    }

    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(5, 'five');
      COMMIT;
    }

    sql2 COMMIT
  } {}

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

  do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n}
  do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n}

  #-----------------------------------------------------------------------
  # The "schema cookie" issue.
  #
  # 1. Begin and UNLOCKED write to "t1" using [db]
  #
  # 2. Create an index on t1 using [db2].
  #
  # 3. Attempt to commit the UNLOCKED write. This is an SQLITE_BUSY_SNAPSHOT,
  #    even though there is no page collision.
  #
  do_test 2.$tn.5.1 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(6, 'six');
    }
  } {}

  do_test 2.$tn.5.2 {
    sql2 { CREATE INDEX i1 ON t1(v); }
  } {}
................................................................................
  do_test 2.$tn.5.4 {
    sql2 { PRAGMA integrity_check }
  } {ok}
  catch { sql1 ROLLBACK }

  #-----------------------------------------------------------------------
  #
  # 1. Begin an UNLOCKED write to "t1" using [db]
  #
  # 2. Lots of inserts into t2. Enough to grow the db file and modify page 1.
  #
  # 3. Check that the UNLOCKED transaction can not be committed.
  #
  do_test 2.$tn.6.1 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(6, 'six');
    }
  } {}

  do_test 2.$tn.6.2 {
    sql2 { 
      WITH src(a,b) AS (
................................................................................
      SELECT count(*) FROM t1;
      SELECT count(*) FROM t2;
    }
  } {5 10001}

  #-----------------------------------------------------------------------
  # 
  # 1. Begin an big UNLOCKED write to "t1" using [db] - large enough to
  #    grow the db file.
  #
  # 2. Lots of inserts into t2. Also enough to grow the db file.
  #
  # 3. Check that the UNLOCKED transaction cannot be committed (due to a clash
  #    on page 1 - the db size field).
  #
  do_test 2.$tn.7.1 {
    sql1 {
      BEGIN UNLOCKED;
        WITH src(a,b) AS (
          VALUES(10000,10000) UNION ALL SELECT a+1,b+1 FROM src WHERE a<20000
        ) INSERT INTO t1 SELECT * FROM src;
    }
  } {}

  do_test 2.$tn.7.2 {
................................................................................
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('a', 'b');
  PRAGMA user_version = 10;
} {wal}
do_execsql_test 3.1 {
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES('c', 'd');
    SELECT * FROM t1;
} {a b c d}
do_catchsql_test 3.2 {
  PRAGMA user_version = 11;
} {1 {cannot modify user_version within UNLOCKED transaction}}
do_execsql_test 3.3 {
  PRAGMA user_version;
  SELECT * FROM t1;
} {10 a b c d}
do_catchsql_test 3.4 {
  PRAGMA application_id = 11;
} {1 {cannot modify application_id within UNLOCKED transaction}}
do_execsql_test 3.5 {
  COMMIT;
  PRAGMA user_version;
  PRAGMA application_id;
  SELECT * FROM t1;
} {10 0 a b c d}

finish_test








|

|










|









|










|


|







 







|









|







 







|









|

|










|
|










|





|






|












|


|











|







 







|








|







 







|











|







 







|




|







 







|



|




|







 







|



|



|







 







|




|




|







 







|





|






|









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
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
...
101
102
103
104
105
106
107
108
109
110
111
112
113
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
...
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
...
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
#
#***********************************************************************
#

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

ifcapable !concurrent {
  finish_test
  return
}

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
} {wal}

do_execsql_test 1.1 {
  CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
  BEGIN CONCURRENT;
    INSERT INTO t1 VALUES(1, 'abcd');
  COMMIT;
}

do_execsql_test 1.2 {
  SELECT * FROM t1;
} {1 abcd}

do_execsql_test 1.3 {
  BEGIN CONCURRENT;
    INSERT INTO t1 VALUES(2, 'efgh');
  ROLLBACK;
}

do_execsql_test 1.4 {
  SELECT * FROM t1;
} {1 abcd}


#-------------------------------------------------------------------------
# CONCURRENT transactions cannot do cache spills.
#
foreach {tn trans spill} {
  1 {BEGIN CONCURRENT}  0
  2 {BEGIN}           1
} {
  do_test 1.5.$tn {
    sqlite3 db2 test.db
    set walsz [file size test.db-wal]

    execsql { PRAGMA cache_size = 10 } db2
................................................................................
  } [expr !$spill]

  execsql ROLLBACK db2
  db2 close
}

#-------------------------------------------------------------------------
# CONCURRENT transactions man not be committed while there are active
# readers.
do_execsql_test 1.6.setup {
  DROP TABLE t1;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  INSERT INTO t1 VALUES(3, 4);
  INSERT INTO t1 VALUES(5, 6);
}
foreach {tn trans commit_ok} {
  1 {BEGIN CONCURRENT}  0
  2 {BEGIN}           1
} {
  do_test 1.6.$tn.1 {
    set stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy]
    sqlite3_step $stmt
  } SQLITE_ROW
  do_test 1.6.$tn.2 {
................................................................................
  }

  sqlite3_finalize $stmt
  catchsql ROLLBACK
}

#-------------------------------------------------------------------------
# CONCURRENT transactions may not modify the db schema.
#
foreach {tn sql} {
  1 { CREATE TABLE xx(a, b) }
  2 { DROP TABLE t1 }
  3 { CREATE INDEX i1 ON t1(a) }
  4 { CREATE VIEW v1 AS SELECT * FROM t1 }
  5 { CREATE TEMP TABLE xx(a, b) }
} {
  do_catchsql_test 1.7.$tn.1 "
    BEGIN CONCURRENT;
    $sql
  " {1 {cannot modify database schema within CONCURRENT transaction}}

  do_execsql_test 1.7.$tn.2 {
    SELECT sql FROM sqlite_master;
    SELECT sql FROM sqlite_temp_master;
  } {{CREATE TABLE t1(a, b)}}

  do_execsql_test 1.7.$tn.3 COMMIT
}

#-------------------------------------------------------------------------
# If an auto-vacuum database is written within an CONCURRENT transaction, it
# is handled in the same way as for a non-CONCURRENT transaction.
#
reset_db
do_execsql_test 1.8.1 {
  PRAGMA auto_vacuum = 1;
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('x', 'y');
} {wal}

do_execsql_test 1.8.2 {
  BEGIN CONCURRENT;
    SELECT * FROM t1;
  COMMIT;
} {x y}

do_catchsql_test 1.8.3 {
  BEGIN CONCURRENT;
    INSERT INTO t1 VALUES('a', 'b');
} {0 {}}

do_test 1.8.4 {
  sqlite3 db2 test.db
  catchsql {
    BEGIN CONCURRENT;
      INSERT INTO t1 VALUES('c', 'd');
  } db2
} {1 {database is locked}}

do_test 1.8.5 {
  db eval COMMIT
  db2 eval COMMIT
} {}

do_multiclient_test tn {

  #-----------------------------------------------------------------------
  # 1. Start an CONCURRENT transaction using [db1].
  #
  # 2. Start and then rollback a regular transaction using [db2]. This 
  #    can be done as the ongoing [db1] transaction is CONCURRENT.
  #
  # 3. The [db1] transaction can now be committed, as [db2] has relinquished
  #    the write lock.
  #
  do_test 2.$tn.1.1 {
    sql1 { 
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(k INTEGER PRIMARY KEY, v);
      INSERT INTO t1 VALUES(1, 'one');
    }
    sql1 { 
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(2, 'two');
    }
    code1 { sqlite3_get_autocommit db }
  } 0

  do_test 2.$tn.1.2 {
    sql2 {
................................................................................

  do_test 2.$tn.1.3 {
    sql1 COMMIT
    sql2 { SELECT * FROM t1 }
  } {1 one 2 two}
  
  #-----------------------------------------------------------------------
  # 1. Start an CONCURRENT transaction using [db1].
  #
  # 2. Commit a transaction using [db2].
  #
  # 3. Try to commit with [db1]. Check that SQLITE_BUSY_SNAPSHOT is returned,
  #    and the transaction is not rolled back.
  #
  do_test 2.$tn.2.1 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(-1, 'hello world');
    }
  } {}

  do_test 2.$tn.2.2 {
    sql2 {
      INSERT INTO t1 VALUES(3, 'three');
................................................................................
    sql1 {
      SELECT * FROM t1;
      ROLLBACK;
    }
  } {-1 {hello world} 1 one 2 two}
  
  #-----------------------------------------------------------------------
  # 1. Start an CONCURRENT transaction using [db1].
  #
  # 2. Open a transaction using [db2].
  #
  # 3. Try to commit with [db1]. Check that SQLITE_BUSY is returned,
  #    and the transaction is not rolled back.
  #
  # 4. Have [db2] roll its transaction back. Then check that [db1] can
  #    commit.
  #
  do_test 2.$tn.3.1 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(4, 'four');
    }
  } {}

  do_test 2.$tn.3.2 {
    sql2 {
      BEGIN;
................................................................................
  # 3. See if it worked.
  #
  do_test 2.$tn.4.1 {
    sql1 { CREATE TABLE t2(a, b) }
  } {}
  do_test 2.$tn.4.2 {
    sql2 {
      BEGIN CONCURRENT;
        INSERT INTO t2 VALUES('i', 'n');
    }

    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(5, 'five');
      COMMIT;
    }

    sql2 COMMIT
  } {}

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

  do_test 2.$tn.4.3.3 { sql2 {SELECT * FROM t2} } {i n}
  do_test 2.$tn.4.3.4 { sql1 {SELECT * FROM t2} } {i n}

  #-----------------------------------------------------------------------
  # The "schema cookie" issue.
  #
  # 1. Begin and CONCURRENT write to "t1" using [db]
  #
  # 2. Create an index on t1 using [db2].
  #
  # 3. Attempt to commit the CONCURRENT write. This is an SQLITE_BUSY_SNAPSHOT,
  #    even though there is no page collision.
  #
  do_test 2.$tn.5.1 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(6, 'six');
    }
  } {}

  do_test 2.$tn.5.2 {
    sql2 { CREATE INDEX i1 ON t1(v); }
  } {}
................................................................................
  do_test 2.$tn.5.4 {
    sql2 { PRAGMA integrity_check }
  } {ok}
  catch { sql1 ROLLBACK }

  #-----------------------------------------------------------------------
  #
  # 1. Begin an CONCURRENT write to "t1" using [db]
  #
  # 2. Lots of inserts into t2. Enough to grow the db file and modify page 1.
  #
  # 3. Check that the CONCURRENT transaction can not be committed.
  #
  do_test 2.$tn.6.1 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(6, 'six');
    }
  } {}

  do_test 2.$tn.6.2 {
    sql2 { 
      WITH src(a,b) AS (
................................................................................
      SELECT count(*) FROM t1;
      SELECT count(*) FROM t2;
    }
  } {5 10001}

  #-----------------------------------------------------------------------
  # 
  # 1. Begin an big CONCURRENT write to "t1" using [db] - large enough to
  #    grow the db file.
  #
  # 2. Lots of inserts into t2. Also enough to grow the db file.
  #
  # 3. Check that the CONCURRENT transaction cannot be committed (due to a clash
  #    on page 1 - the db size field).
  #
  do_test 2.$tn.7.1 {
    sql1 {
      BEGIN CONCURRENT;
        WITH src(a,b) AS (
          VALUES(10000,10000) UNION ALL SELECT a+1,b+1 FROM src WHERE a<20000
        ) INSERT INTO t1 SELECT * FROM src;
    }
  } {}

  do_test 2.$tn.7.2 {
................................................................................
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES('a', 'b');
  PRAGMA user_version = 10;
} {wal}
do_execsql_test 3.1 {
  BEGIN CONCURRENT;
    INSERT INTO t1 VALUES('c', 'd');
    SELECT * FROM t1;
} {a b c d}
do_catchsql_test 3.2 {
  PRAGMA user_version = 11;
} {1 {cannot modify user_version within CONCURRENT transaction}}
do_execsql_test 3.3 {
  PRAGMA user_version;
  SELECT * FROM t1;
} {10 a b c d}
do_catchsql_test 3.4 {
  PRAGMA application_id = 11;
} {1 {cannot modify application_id within CONCURRENT transaction}}
do_execsql_test 3.5 {
  COMMIT;
  PRAGMA user_version;
  PRAGMA application_id;
  SELECT * FROM t1;
} {10 0 a b c d}

finish_test

Name change from test/unlocked2.test to test/concurrent2.test.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
120
121
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#
#***********************************************************************
#

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

ifcapable !unlocked {
  finish_test
  return
}

do_multiclient_test tn {

  do_test 1.$tn.1 {
................................................................................
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);
      CREATE TABLE t2(y);
    }
  } {wal}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}

  # Test that an UNLOCKED transaction that allocates/frees no pages does
  # not conflict with a transaction that does allocate pages.
  do_test 1.$tn.2  {
    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(4);
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }
    sql1 {
      COMMIT;
    }
  } {}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
  
  # But that an UNLOCKED transaction does conflict with a transaction
  # that modifies the db schema.
  do_test 1.$tn.3  {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(5);
    }
    sql2 {
      CREATE TABLE t3(z);
    }
    list [catch { sql1 COMMIT } msg] $msg
  } {1 {database is locked}}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
  
  # Test that an UNLOCKED transaction that allocates at least one page 
  # does not conflict with a transaction that allocates no pages.
  do_test 1.$tn.4  {
    sql1 { 
      ROLLBACK;
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(8);
    }
    sql1 {
      COMMIT;
................................................................................
      CREATE TABLE t1(x UNIQUE);
      CREATE TABLE t2(y UNIQUE);
    }
  } {wal}

  do_test 2.$tn.2  {
    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }
    sql1 COMMIT
  } {}

  do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}

  do_test 2.$tn.4  {
    sql1 { 
      BEGIN UNLOCKED;
        DELETE FROM t1;
    }
    sql2 {
      DELETE FROM t2;
    }
    sql1 COMMIT
  } {}
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t2 VALUES(randomblob(1500));
      DELETE FROM t1 WHERE rowid=1;
    }

    sql1 {
      BEGIN UNLOCKED;
        DELETE FROM t1 WHERE rowid=2;
    }

    sql2 {
      DELETE FROM t2;
    }

................................................................................
    sql1 COMMIT
  } {}

  do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}

#-------------------------------------------------------------------------
# When an UNLOCKED transaction is opened on a database, the nFree and 
# iTrunk header fields of the cached version of page 1 are both set 
# to 0. This allows an UNLOCKED transaction to use its own private 
# free-page-list, which is merged with the main database free-list when
# the transaction is committed.
#
# The following tests check that nFree/iTrunk are correctly restored if
# an UNLOCKED transaction is rolled back, and that savepoint rollbacks
# that occur within UNLOCKED transactions do not incorrectly restore
# these fields to their on-disk values.
#
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
  DELETE FROM t1;
} {wal}

do_execsql_test 3.1 {
  BEGIN UNLOCKED;
    INSERT INTO t1 VALUES(1, 2);
  ROLLBACK;
}

do_execsql_test 3.2 { PRAGMA integrity_check } {ok}
do_execsql_test 3.3 { PRAGMA freelist_count } {2}

do_execsql_test 3.4.1 {
  BEGIN UNLOCKED;
    PRAGMA freelist_count;
} {2}
do_execsql_test 3.4.2 {
  SAVEPOINT xyz;
    INSERT INTO t1 VALUES(randomblob(1500), NULL);
    PRAGMA freelist_count;
} {0}
................................................................................
  ROLLBACK TO xyz;
} {}
do_execsql_test 3.4.4 { PRAGMA freelist_count } {0}
do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2}
do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok}

do_execsql_test 3.5.1 {
  BEGIN UNLOCKED;
    UPDATE t1 SET x=randomblob(10) WHERE y=555;
    PRAGMA freelist_count;
} {0}
do_execsql_test 3.5.2 {
  ROLLBACK;
  PRAGMA freelist_count;
} {2}
do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok}

#-------------------------------------------------------------------------
# Test that nothing goes wrong if an UNLOCKED transaction allocates a
# page at the end of the file, frees it within the same transaction, and
# then has to move the same page to avoid a conflict on COMMIT.
#
do_multiclient_test tn {
  do_test 4.$tn.1 {
    sql1 {
      PRAGMA journal_mode = wal;
................................................................................
      CREATE TABLE t1(x);
      CREATE TABLE t2(x);
    }
  } {wal}

  do_test 4.$tn.2 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
        INSERT INTO t1 VALUES(randomblob(1500));
        DELETE FROM t1 WHERE rowid = 1;
    }

    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      PRAGMA page_count;
    }
  } {wal 4}

  do_test 5.$tn.2 {
    sql1 {
      BEGIN UNLOCKED;
        INSERT INTO t2 VALUES(randomblob(1500));
        PRAGMA page_count;
    }
  } {5}

  do_test 5.$tn.3 {
    sql2 { 
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      PRAGMA wal_checkpoint;
    }
  } {wal 0 5 5}

  do_test 6.$tn.2 {
    sql1 { 
      BEGIN UNLOCKED;
        INSERT INTO t1 VALUES(randomblob(1500));
        INSERT INTO t1 VALUES(randomblob(1500));
    }
  } {}

  do_test 6.$tn.3 {
    sql2 {







|

|







 







|



|











|



|









|




|







 







|












|







 







|







 







|

|




|
|











|








|







 







|










|







 







|







 







|







 







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
120
121
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#
#***********************************************************************
#

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

ifcapable !concurrent {
  finish_test
  return
}

do_multiclient_test tn {

  do_test 1.$tn.1 {
................................................................................
      PRAGMA journal_mode = wal;
      CREATE TABLE t1(x);
      CREATE TABLE t2(y);
    }
  } {wal}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}

  # Test that an CONCURRENT transaction that allocates/frees no pages does
  # not conflict with a transaction that does allocate pages.
  do_test 1.$tn.2  {
    sql1 { 
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(4);
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }
    sql1 {
      COMMIT;
    }
  } {}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
  
  # But that an CONCURRENT transaction does conflict with a transaction
  # that modifies the db schema.
  do_test 1.$tn.3  {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(5);
    }
    sql2 {
      CREATE TABLE t3(z);
    }
    list [catch { sql1 COMMIT } msg] $msg
  } {1 {database is locked}}
  do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok}
  
  # Test that an CONCURRENT transaction that allocates at least one page 
  # does not conflict with a transaction that allocates no pages.
  do_test 1.$tn.4  {
    sql1 { 
      ROLLBACK;
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(8);
    }
    sql1 {
      COMMIT;
................................................................................
      CREATE TABLE t1(x UNIQUE);
      CREATE TABLE t2(y UNIQUE);
    }
  } {wal}

  do_test 2.$tn.2  {
    sql1 { 
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(randomblob(1500));
    }
    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
    }
    sql1 COMMIT
  } {}

  do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok}

  do_test 2.$tn.4  {
    sql1 { 
      BEGIN CONCURRENT;
        DELETE FROM t1;
    }
    sql2 {
      DELETE FROM t2;
    }
    sql1 COMMIT
  } {}
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t1 VALUES(randomblob(1500));
      INSERT INTO t2 VALUES(randomblob(1500));
      DELETE FROM t1 WHERE rowid=1;
    }

    sql1 {
      BEGIN CONCURRENT;
        DELETE FROM t1 WHERE rowid=2;
    }

    sql2 {
      DELETE FROM t2;
    }

................................................................................
    sql1 COMMIT
  } {}

  do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok}
}

#-------------------------------------------------------------------------
# When an CONCURRENT transaction is opened on a database, the nFree and 
# iTrunk header fields of the cached version of page 1 are both set 
# to 0. This allows an CONCURRENT transaction to use its own private 
# free-page-list, which is merged with the main database free-list when
# the transaction is committed.
#
# The following tests check that nFree/iTrunk are correctly restored if
# an CONCURRENT transaction is rolled back, and that savepoint rollbacks
# that occur within CONCURRENT transactions do not incorrectly restore
# these fields to their on-disk values.
#
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(x, y);
  INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500));
  DELETE FROM t1;
} {wal}

do_execsql_test 3.1 {
  BEGIN CONCURRENT;
    INSERT INTO t1 VALUES(1, 2);
  ROLLBACK;
}

do_execsql_test 3.2 { PRAGMA integrity_check } {ok}
do_execsql_test 3.3 { PRAGMA freelist_count } {2}

do_execsql_test 3.4.1 {
  BEGIN CONCURRENT;
    PRAGMA freelist_count;
} {2}
do_execsql_test 3.4.2 {
  SAVEPOINT xyz;
    INSERT INTO t1 VALUES(randomblob(1500), NULL);
    PRAGMA freelist_count;
} {0}
................................................................................
  ROLLBACK TO xyz;
} {}
do_execsql_test 3.4.4 { PRAGMA freelist_count } {0}
do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2}
do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok}

do_execsql_test 3.5.1 {
  BEGIN CONCURRENT;
    UPDATE t1 SET x=randomblob(10) WHERE y=555;
    PRAGMA freelist_count;
} {0}
do_execsql_test 3.5.2 {
  ROLLBACK;
  PRAGMA freelist_count;
} {2}
do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok}

#-------------------------------------------------------------------------
# Test that nothing goes wrong if an CONCURRENT transaction allocates a
# page at the end of the file, frees it within the same transaction, and
# then has to move the same page to avoid a conflict on COMMIT.
#
do_multiclient_test tn {
  do_test 4.$tn.1 {
    sql1 {
      PRAGMA journal_mode = wal;
................................................................................
      CREATE TABLE t1(x);
      CREATE TABLE t2(x);
    }
  } {wal}

  do_test 4.$tn.2 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(randomblob(1500));
        INSERT INTO t1 VALUES(randomblob(1500));
        DELETE FROM t1 WHERE rowid = 1;
    }

    sql2 {
      INSERT INTO t2 VALUES(randomblob(1500));
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      PRAGMA page_count;
    }
  } {wal 4}

  do_test 5.$tn.2 {
    sql1 {
      BEGIN CONCURRENT;
        INSERT INTO t2 VALUES(randomblob(1500));
        PRAGMA page_count;
    }
  } {5}

  do_test 5.$tn.3 {
    sql2 { 
................................................................................
      INSERT INTO t1 VALUES(randomblob(1500));
      PRAGMA wal_checkpoint;
    }
  } {wal 0 5 5}

  do_test 6.$tn.2 {
    sql1 { 
      BEGIN CONCURRENT;
        INSERT INTO t1 VALUES(randomblob(1500));
        INSERT INTO t1 VALUES(randomblob(1500));
    }
  } {}

  do_test 6.$tn.3 {
    sql2 {

Name change from test/unlocked3.test to test/concurrent3.test.

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
...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#
#    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.
#
#***********************************************************************
#
# Tests for transactions started with BEGIN UNLOCKED. The tests in this
# file focus on testing that deferred page allocation works properly.
#

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

if {$AUTOVACUUM} { finish_test ; return }
ifcapable !unlocked {
  finish_test
  return
}

proc create_schema {} {
  db eval {
    PRAGMA journal_mode = wal;
................................................................................
    reset_db
    create_schema
    continue
  }
  foreach db $DBLIST { sqlite3 $db test.db }

  do_test 1.$tn {
    foreach db $DBLIST { $db eval "BEGIN UNLOCKED" } 

    foreach op $oplist {
      set iTbl [string range $op 0 0]
      foreach char [split [string range $op 1 end] {}] {
        do_sql_op $iTbl $char
      }
    }







|






|


|







 







|







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
...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#
#    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.
#
#***********************************************************************
#
# Tests for transactions started with BEGIN CONCURRENT. The tests in this
# file focus on testing that deferred page allocation works properly.
#

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

if {$AUTOVACUUM} { finish_test ; return }
ifcapable !concurrent {
  finish_test
  return
}

proc create_schema {} {
  db eval {
    PRAGMA journal_mode = wal;
................................................................................
    reset_db
    create_schema
    continue
  }
  foreach db $DBLIST { sqlite3 $db test.db }

  do_test 1.$tn {
    foreach db $DBLIST { $db eval "BEGIN CONCURRENT" } 

    foreach op $oplist {
      set iTbl [string range $op 0 0]
      foreach char [split [string range $op 1 end] {}] {
        do_sql_op $iTbl $char
      }
    }

Changes to tool/mkkeywordhash.c.

167
168
169
170
171
172
173

174
175
176
177
178
179
180
...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  { "CASCADE",          "TK_CASCADE",      FKEY                   },
  { "CASE",             "TK_CASE",         ALWAYS                 },
  { "CAST",             "TK_CAST",         CAST                   },
  { "CHECK",            "TK_CHECK",        ALWAYS                 },
  { "COLLATE",          "TK_COLLATE",      ALWAYS                 },
  { "COLUMN",           "TK_COLUMNKW",     ALTER                  },
  { "COMMIT",           "TK_COMMIT",       ALWAYS                 },

  { "CONFLICT",         "TK_CONFLICT",     CONFLICT               },
  { "CONSTRAINT",       "TK_CONSTRAINT",   ALWAYS                 },
  { "CREATE",           "TK_CREATE",       ALWAYS                 },
  { "CROSS",            "TK_JOIN_KW",      ALWAYS                 },
  { "CURRENT_DATE",     "TK_CTIME_KW",     ALWAYS                 },
  { "CURRENT_TIME",     "TK_CTIME_KW",     ALWAYS                 },
  { "CURRENT_TIMESTAMP","TK_CTIME_KW",     ALWAYS                 },
................................................................................
  { "TEMPORARY",        "TK_TEMP",         ALWAYS                 },
  { "THEN",             "TK_THEN",         ALWAYS                 },
  { "TO",               "TK_TO",           ALWAYS                 },
  { "TRANSACTION",      "TK_TRANSACTION",  ALWAYS                 },
  { "TRIGGER",          "TK_TRIGGER",      TRIGGER                },
  { "UNION",            "TK_UNION",        COMPOUND               },
  { "UNIQUE",           "TK_UNIQUE",       ALWAYS                 },
  { "UNLOCKED",         "TK_UNLOCKED",     ALWAYS                 },
  { "UPDATE",           "TK_UPDATE",       ALWAYS                 },
  { "USING",            "TK_USING",        ALWAYS                 },
  { "VACUUM",           "TK_VACUUM",       VACUUM                 },
  { "VALUES",           "TK_VALUES",       ALWAYS                 },
  { "VIEW",             "TK_VIEW",         VIEW                   },
  { "VIRTUAL",          "TK_VIRTUAL",      VTAB                   },
  { "WITH",             "TK_WITH",         CTE                    },







>







 







<







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
...
259
260
261
262
263
264
265

266
267
268
269
270
271
272
  { "CASCADE",          "TK_CASCADE",      FKEY                   },
  { "CASE",             "TK_CASE",         ALWAYS                 },
  { "CAST",             "TK_CAST",         CAST                   },
  { "CHECK",            "TK_CHECK",        ALWAYS                 },
  { "COLLATE",          "TK_COLLATE",      ALWAYS                 },
  { "COLUMN",           "TK_COLUMNKW",     ALTER                  },
  { "COMMIT",           "TK_COMMIT",       ALWAYS                 },
  { "CONCURRENT",       "TK_CONCURRENT",   ALWAYS                 },
  { "CONFLICT",         "TK_CONFLICT",     CONFLICT               },
  { "CONSTRAINT",       "TK_CONSTRAINT",   ALWAYS                 },
  { "CREATE",           "TK_CREATE",       ALWAYS                 },
  { "CROSS",            "TK_JOIN_KW",      ALWAYS                 },
  { "CURRENT_DATE",     "TK_CTIME_KW",     ALWAYS                 },
  { "CURRENT_TIME",     "TK_CTIME_KW",     ALWAYS                 },
  { "CURRENT_TIMESTAMP","TK_CTIME_KW",     ALWAYS                 },
................................................................................
  { "TEMPORARY",        "TK_TEMP",         ALWAYS                 },
  { "THEN",             "TK_THEN",         ALWAYS                 },
  { "TO",               "TK_TO",           ALWAYS                 },
  { "TRANSACTION",      "TK_TRANSACTION",  ALWAYS                 },
  { "TRIGGER",          "TK_TRIGGER",      TRIGGER                },
  { "UNION",            "TK_UNION",        COMPOUND               },
  { "UNIQUE",           "TK_UNIQUE",       ALWAYS                 },

  { "UPDATE",           "TK_UPDATE",       ALWAYS                 },
  { "USING",            "TK_USING",        ALWAYS                 },
  { "VACUUM",           "TK_VACUUM",       VACUUM                 },
  { "VALUES",           "TK_VALUES",       ALWAYS                 },
  { "VIEW",             "TK_VIEW",         VIEW                   },
  { "VIRTUAL",          "TK_VIRTUAL",      VTAB                   },
  { "WITH",             "TK_WITH",         CTE                    },