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

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

Overview
Comment:Add the "mutexfree-shmlock" patch to this branch.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | begin-concurrent-pnu-wal2
Files: files | file ages | folders
SHA3-256: c3191fc8fcb0775f87f55cb8124e0c36caa54993c2a5fe770d437e4f6ee72d77
User & Date: dan 2018-12-18 19:46:50
Wiki:begin-concurrent-pnu-wal2
Context
2019-01-02
19:46
Merge changes from begin-concurrent-wal2. Also modify the unix-excl shm-locks on this branch so that it is not an error to attempt to release an EXCLUSIVE lock that is not held. check-in: 86685679 user: dan tags: begin-concurrent-pnu-wal2
2018-12-18
19:46
Add the "mutexfree-shmlock" patch to this branch. check-in: c3191fc8 user: dan tags: begin-concurrent-pnu-wal2
18:16
Fix a test script problem in wal2savepoint.test. check-in: e388d029 user: dan tags: begin-concurrent-pnu-wal2
2018-12-10
16:52
Make SQLITE_MFS_NSHARD a compile time setting. check-in: b9a74151 user: dan tags: mutexfree-shmlock
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

42
43
44
45
46
47
48










49
50
51
52
53
54
55
....
4257
4258
4259
4260
4261
4262
4263


















4264















4265
4266
4267
4268
4269
4270
4271
....
4279
4280
4281
4282
4283
4284
4285



4286
4287
4288
4289
4290
4291
4292
....
4783
4784
4785
4786
4787
4788
4789













































































4790
4791
4792
4793
4794
4795
4796
....
4815
4816
4817
4818
4819
4820
4821




4822
4823
4824








4825
4826
4827
4828
4829
4830
4831
....
4907
4908
4909
4910
4911
4912
4913



4914
4915
4916
4917
4918
4919

4920
4921
4922
4923
4924
4925
4926
**   *  Locking primitives for the proxy uber-locking-method. (MacOSX only)
**   *  Definitions of sqlite3_vfs objects for all locking methods
**      plus implementations of sqlite3_os_init() and sqlite3_os_end().
*/
#include "sqliteInt.h"
#if SQLITE_OS_UNIX              /* This file is used on unix only */











/*
** There are various methods for file locking used for concurrency
** control:
**
**   1. POSIX locking (the default),
**   2. No locking,
**   3. Dot-file locking,
................................................................................
  int nRef;                  /* Number of unixShm objects pointing to this */
  unixShm *pFirst;           /* All unixShm objects pointing to this */
#ifdef SQLITE_DEBUG
  u8 exclMask;               /* Mask of exclusive locks held */
  u8 sharedMask;             /* Mask of shared locks held */
  u8 nextShmId;              /* Next available unixShm.id value */
#endif


















};
















/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
**
** The following fields are initialized when this object is created and
** are read-only thereafter:
................................................................................
struct unixShm {
  unixShmNode *pShmNode;     /* The underlying unixShmNode object */
  unixShm *pNext;            /* Next unixShm with the same unixShmNode */
  u8 hasMutex;               /* True if holding the unixShmNode->pShmMutex */
  u8 id;                     /* Id of this connection within its unixShmNode */
  u16 sharedMask;            /* Mask of shared locks held */
  u16 exclMask;              /* Mask of exclusive locks held */



};

/*
** Constants used for locking
*/
#define UNIX_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)         /* first lock byte */
#define UNIX_SHM_DMS    (UNIX_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */
................................................................................
    *pp = 0;
  }
  if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
  sqlite3_mutex_leave(pShmNode->pShmMutex);
  return rc;
}














































































/*
** Change the lock state for a shared-memory segment.
**
** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
** different here than in posix.  In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back.  But one may
** not go from shared to exclusive or from exclusive to shared.
................................................................................
  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  /* assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); */
  assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
  assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );





  mask = (1<<(ofst+n)) - (1<<ofst);
  assert( n>1 || mask==(1<<ofst) );








  sqlite3_mutex_enter(pShmNode->pShmMutex);
  if( flags & SQLITE_SHM_UNLOCK ){
    u16 allMask = 0; /* Mask of locks held by siblings */

    /* See if any siblings hold this same lock */
    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
      if( pX==p ) continue;
................................................................................
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
*/
static void unixShmBarrier(
  sqlite3_file *fd                /* Database file holding the shared memory */
){
  UNUSED_PARAMETER(fd);



  sqlite3MemoryBarrier();         /* compiler-defined memory barrier */
  assert( fd->pMethods->xLock==nolockLock 
       || unixFileMutexNotheld((unixFile*)fd) 
  );
  unixEnterMutex();               /* Also mutex, for redundancy */
  unixLeaveMutex();

}

/*
** Close a connection to shared-memory.  Delete the underlying 
** storage if deleteFlag is true.
**
** If there is no shared memory associated with the connection then this







>
>
>
>
>
>
>
>
>
>







 







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

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







 







>
>
>







 







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







 







>
>
>
>



>
>
>
>
>
>
>
>







 







>
>
>






>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
....
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
....
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
....
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
....
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
....
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
**   *  Locking primitives for the proxy uber-locking-method. (MacOSX only)
**   *  Definitions of sqlite3_vfs objects for all locking methods
**      plus implementations of sqlite3_os_init() and sqlite3_os_end().
*/
#include "sqliteInt.h"
#if SQLITE_OS_UNIX              /* This file is used on unix only */

/* Turn this feature on in all builds for now */
#define SQLITE_MUTEXFREE_SHMLOCK 1
#define SQLITE_MFS_EXCLUSIVE     255
#ifndef SQLITE_MFS_NSHARD
# define SQLITE_MFS_NSHARD       8
#endif
#if SQLITE_MFS_NSHARD<1
# error "SQLITE_MFS_NSHARD must be greater than 0"
#endif

/*
** There are various methods for file locking used for concurrency
** control:
**
**   1. POSIX locking (the default),
**   2. No locking,
**   3. Dot-file locking,
................................................................................
  int nRef;                  /* Number of unixShm objects pointing to this */
  unixShm *pFirst;           /* All unixShm objects pointing to this */
#ifdef SQLITE_DEBUG
  u8 exclMask;               /* Mask of exclusive locks held */
  u8 sharedMask;             /* Mask of shared locks held */
  u8 nextShmId;              /* Next available unixShm.id value */
#endif

#ifdef SQLITE_MUTEXFREE_SHMLOCK
  /* In unix-excl mode, if SQLITE_MUTEXFREE_SHMLOCK is defined, all locks
  ** are stored in the following 64-bit value. There are in total 8 
  ** shm-locking slots, each of which are assigned 8-bits from the 64-bit
  ** value. The least-significant 8 bits correspond to shm-locking slot
  ** 0, and so on.
  **
  ** If the 8-bits corresponding to a shm-locking locking slot are set to
  ** 0xFF, then a write-lock is held on the slot. Or, if they are set to
  ** a non-zero value smaller than 0xFF, then they represent the total 
  ** number of read-locks held on the slot. There is no way to distinguish
  ** between a write-lock and 255 read-locks.  */
  struct LockingSlot {
    u32 nLock;
    u64 aPadding[7];
  } aMFSlot[3 + SQLITE_MFS_NSHARD*5];
#endif
};

/*
** Atomic CAS primitive used in multi-process mode. Equivalent to:
** 
**   int unixCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){
**     if( *ptr==oldval ){
**       *ptr = newval;
**       return 1;
**     }
**     return 0;
**   }
*/
#define unixCompareAndSwap(ptr,oldval,newval) \
    __sync_bool_compare_and_swap(ptr,oldval,newval)


/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
**
** The following fields are initialized when this object is created and
** are read-only thereafter:
................................................................................
struct unixShm {
  unixShmNode *pShmNode;     /* The underlying unixShmNode object */
  unixShm *pNext;            /* Next unixShm with the same unixShmNode */
  u8 hasMutex;               /* True if holding the unixShmNode->pShmMutex */
  u8 id;                     /* Id of this connection within its unixShmNode */
  u16 sharedMask;            /* Mask of shared locks held */
  u16 exclMask;              /* Mask of exclusive locks held */
#ifdef SQLITE_MUTEXFREE_SHMLOCK
  u8 aMFCurrent[8];          /* Current slot used for each shared lock */
#endif
};

/*
** Constants used for locking
*/
#define UNIX_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)         /* first lock byte */
#define UNIX_SHM_DMS    (UNIX_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */
................................................................................
    *pp = 0;
  }
  if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
  sqlite3_mutex_leave(pShmNode->pShmMutex);
  return rc;
}

#ifdef SQLITE_MUTEXFREE_SHMLOCK
static int unixMutexFreeShmlock(
  unixFile *pFd,             /* Database file holding the shared memory */
  int ofst,                  /* First lock to acquire or release */
  int n,                     /* Number of locks to acquire or release */
  int flags                  /* What to do with the lock */
){
  struct LockMapEntry {
    int iFirst;
    int nSlot;
  } aMap[9] = {
    { 0, 1 },
    { 1, 1 },
    { 2, 1 },
    { 3+0*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
    { 3+1*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
    { 3+2*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
    { 3+3*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
    { 3+4*SQLITE_MFS_NSHARD, SQLITE_MFS_NSHARD },
    { 3+5*SQLITE_MFS_NSHARD, 0 },
  };

  unixShm *p = pFd->pShm;               /* The shared memory being locked */
  unixShm *pX;                          /* For looping over all siblings */
  unixShmNode *pShmNode = p->pShmNode;  /* The underlying file iNode */
  int rc = SQLITE_OK;
  int iIncr;
  u16 mask;                             /* Mask of locks to take or release */

  if( flags & SQLITE_SHM_SHARED ){
    /* SHARED locks */
    u32 iOld, iNew, *ptr;
    int iIncr = -1;
    if( (flags & SQLITE_SHM_UNLOCK)==0 ){
      p->aMFCurrent[ofst] = (p->aMFCurrent[ofst] + 1) % aMap[ofst].nSlot;
      iIncr = 1;
    }
    ptr = &pShmNode->aMFSlot[aMap[ofst].iFirst + p->aMFCurrent[ofst]].nLock;
    do {
      iOld = *ptr;
      iNew = iOld + iIncr;
      if( iNew>SQLITE_MFS_EXCLUSIVE ){
        return SQLITE_BUSY;
      }
    }while( 0==unixCompareAndSwap(ptr, iOld, iNew) );
  }else{
    /* EXCLUSIVE locks */
    int iFirst = aMap[ofst].iFirst;
    int iLast = aMap[ofst+n].iFirst;
    int i;
    for(i=iFirst; i<iLast; i++){
      u32 *ptr = &pShmNode->aMFSlot[i].nLock;
      if( flags & SQLITE_SHM_UNLOCK ){
        assert( (*ptr)==SQLITE_MFS_EXCLUSIVE );
        *ptr = 0;
      }else{
        u32 iOld;
        do {
          iOld = *ptr;
          if( iOld>0 ){
            while( i>iFirst ){
              i--;
              pShmNode->aMFSlot[i].nLock = 0;
            }
            return SQLITE_BUSY;
          }
        }while( 0==unixCompareAndSwap(ptr, iOld, SQLITE_MFS_EXCLUSIVE) );
      }
    }
  }

  return SQLITE_OK;
}
#else
# define unixMutexFreeShmlock(a,b,c,d) SQLITE_OK
#endif

/*
** Change the lock state for a shared-memory segment.
**
** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
** different here than in posix.  In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back.  But one may
** not go from shared to exclusive or from exclusive to shared.
................................................................................
  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  /* assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); */
  assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
  assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );

  if( pDbFd->pInode->bProcessLock ){
    return unixMutexFreeShmlock(pDbFd, ofst, n, flags);
  }

  mask = (1<<(ofst+n)) - (1<<ofst);
  assert( n>1 || mask==(1<<ofst) );
  if( flags & SQLITE_SHM_LOCK ){
    assert( !(flags&SQLITE_SHM_SHARED) || (p->sharedMask&mask)==0 );
    assert( !(flags&SQLITE_SHM_EXCLUSIVE) || !(p->exclMask&mask) );
  }else{
    assert( !(flags&SQLITE_SHM_SHARED) || (p->sharedMask&mask)==mask );
    assert( !(flags&SQLITE_SHM_EXCLUSIVE) || (p->exclMask&mask)==mask );
  }

  sqlite3_mutex_enter(pShmNode->pShmMutex);
  if( flags & SQLITE_SHM_UNLOCK ){
    u16 allMask = 0; /* Mask of locks held by siblings */

    /* See if any siblings hold this same lock */
    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
      if( pX==p ) continue;
................................................................................
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
*/
static void unixShmBarrier(
  sqlite3_file *fd                /* Database file holding the shared memory */
){
  UNUSED_PARAMETER(fd);
#ifdef SQLITE_MUTEXFREE_SHMLOCK
  __sync_synchronize();
#else
  sqlite3MemoryBarrier();         /* compiler-defined memory barrier */
  assert( fd->pMethods->xLock==nolockLock 
       || unixFileMutexNotheld((unixFile*)fd) 
  );
  unixEnterMutex();               /* Also mutex, for redundancy */
  unixLeaveMutex();
#endif
}

/*
** Close a connection to shared-memory.  Delete the underlying 
** storage if deleteFlag is true.
**
** If there is no shared memory associated with the connection then this

Changes to src/test_superlock.c.

37
38
39
40
41
42
43


44
45
46
47
48
49
50
...
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
...
135
136
137
138
139
140
141

142

143
144
145
146
147
148
149
...
152
153
154
155
156
157
158

159



160


161
162
163
164
165
166
167
...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
** An instance of the following structure is allocated for each active
** superlock. The opaque handle returned by sqlite3demo_superlock() is
** actually a pointer to an instance of this structure.
*/
struct Superlock {
  sqlite3 *db;                    /* Database handle used to lock db */
  int bWal;                       /* True if db is a WAL database */


};
typedef struct Superlock Superlock;

/*
** The pCtx pointer passed to this function is actually a pointer to a
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
** by the structure and return the result.
................................................................................
}

/*
** Obtain the extra locks on the database file required for WAL databases.
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
  sqlite3 *db,                    /* Database handle open on WAL database */
  SuperlockBusy *pBusy            /* Busy handler wrapper object */
){
  int rc;                         /* Return code */
  sqlite3_file *fd = 0;           /* Main database file handle */
  void volatile *p = 0;           /* Pointer to first page of shared memory */


  /* Obtain a pointer to the sqlite3_file object open on the main db file. */
  rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
  if( rc!=SQLITE_OK ) return rc;

  /* Obtain the "recovery" lock. Normally, this lock is only obtained by
  ** clients running database recovery.  
  */

  rc = superlockShmLock(fd, 2, 1, pBusy);
  if( rc!=SQLITE_OK ) return rc;


  /* Zero the start of the first shared-memory page. This means that any
  ** clients that open read or write transactions from this point on will
  ** have to run recovery before proceeding. Since they need the "recovery"
  ** lock that this process is holding to do that, no new read or write
  ** transactions may now be opened. Nor can a checkpoint be run, for the
  ** same reason.
................................................................................
  if( rc!=SQLITE_OK ) return rc;
  memset((void *)p, 0, 32);

  /* Obtain exclusive locks on all the "read-lock" slots. Once these locks
  ** are held, it is guaranteed that there are no active reader, writer or 
  ** checkpointer clients.
  */

  rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);

  return rc;
}

/*
** Release a superlock held on a database file. The argument passed to 
** this function must have been obtained from a successful call to
** sqlite3demo_superlock().
................................................................................
  Superlock *p = (Superlock *)pLock;
  if( p->bWal ){
    int rc;                         /* Return code */
    int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
    sqlite3_file *fd = 0;
    rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
    if( rc==SQLITE_OK ){

      fd->pMethods->xShmLock(fd, 2, 1, flags);



      fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);


    }
  }
  sqlite3_close(p->db);
  sqlite3_free(p);
}

/*
................................................................................
  ** to drop the WAL read and write locks currently held. Otherwise, the
  ** new WAL locks may conflict with the old.
  */
  if( rc==SQLITE_OK ){
    if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
      rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
      if( rc==SQLITE_OK ){
        rc = superlockWalLock(pLock->db, &busy);
      }
    }
  }

  if( rc!=SQLITE_OK ){
    sqlite3demo_superunlock(pLock);
    *ppLock = 0;







>
>







 







|





>








>


>







 







>

>







 







>
|
>
>
>
|
>
>







 







|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
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
...
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
...
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
...
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
** An instance of the following structure is allocated for each active
** superlock. The opaque handle returned by sqlite3demo_superlock() is
** actually a pointer to an instance of this structure.
*/
struct Superlock {
  sqlite3 *db;                    /* Database handle used to lock db */
  int bWal;                       /* True if db is a WAL database */
  int bRecoveryLocked;            /* True if WAL RECOVERY lock is held */
  int bReaderLocked;              /* True if WAL READER locks are held */
};
typedef struct Superlock Superlock;

/*
** The pCtx pointer passed to this function is actually a pointer to a
** SuperlockBusy structure. Invoke the busy-handler function encapsulated
** by the structure and return the result.
................................................................................
}

/*
** Obtain the extra locks on the database file required for WAL databases.
** Invoke the supplied busy-handler as required.
*/
static int superlockWalLock(
  Superlock *pLock,               /* Superlock handle */
  SuperlockBusy *pBusy            /* Busy handler wrapper object */
){
  int rc;                         /* Return code */
  sqlite3_file *fd = 0;           /* Main database file handle */
  void volatile *p = 0;           /* Pointer to first page of shared memory */
  sqlite3 *db = pLock->db;

  /* Obtain a pointer to the sqlite3_file object open on the main db file. */
  rc = sqlite3_file_control(db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
  if( rc!=SQLITE_OK ) return rc;

  /* Obtain the "recovery" lock. Normally, this lock is only obtained by
  ** clients running database recovery.  
  */
  assert( pLock->bRecoveryLocked==0 );
  rc = superlockShmLock(fd, 2, 1, pBusy);
  if( rc!=SQLITE_OK ) return rc;
  pLock->bRecoveryLocked = 1;

  /* Zero the start of the first shared-memory page. This means that any
  ** clients that open read or write transactions from this point on will
  ** have to run recovery before proceeding. Since they need the "recovery"
  ** lock that this process is holding to do that, no new read or write
  ** transactions may now be opened. Nor can a checkpoint be run, for the
  ** same reason.
................................................................................
  if( rc!=SQLITE_OK ) return rc;
  memset((void *)p, 0, 32);

  /* Obtain exclusive locks on all the "read-lock" slots. Once these locks
  ** are held, it is guaranteed that there are no active reader, writer or 
  ** checkpointer clients.
  */
  assert( pLock->bReaderLocked==0 );
  rc = superlockShmLock(fd, 3, SQLITE_SHM_NLOCK-3, pBusy);
  if( rc==SQLITE_OK ) pLock->bReaderLocked = 1;
  return rc;
}

/*
** Release a superlock held on a database file. The argument passed to 
** this function must have been obtained from a successful call to
** sqlite3demo_superlock().
................................................................................
  Superlock *p = (Superlock *)pLock;
  if( p->bWal ){
    int rc;                         /* Return code */
    int flags = SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE;
    sqlite3_file *fd = 0;
    rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_FILE_POINTER, (void *)&fd);
    if( rc==SQLITE_OK ){
      if( p->bRecoveryLocked ){
        fd->pMethods->xShmLock(fd, 2, 1, flags);
        p->bRecoveryLocked = 0;
      }
      if( p->bReaderLocked ){
        fd->pMethods->xShmLock(fd, 3, SQLITE_SHM_NLOCK-3, flags);
        p->bReaderLocked = 0;
      }
    }
  }
  sqlite3_close(p->db);
  sqlite3_free(p);
}

/*
................................................................................
  ** to drop the WAL read and write locks currently held. Otherwise, the
  ** new WAL locks may conflict with the old.
  */
  if( rc==SQLITE_OK ){
    if( SQLITE_OK==(rc = superlockIsWal(pLock)) && pLock->bWal ){
      rc = sqlite3_exec(pLock->db, "COMMIT", 0, 0, 0);
      if( rc==SQLITE_OK ){
        rc = superlockWalLock(pLock, &busy);
      }
    }
  }

  if( rc!=SQLITE_OK ){
    sqlite3demo_superunlock(pLock);
    *ppLock = 0;

Changes to src/test_vfs.c.

1558
1559
1560
1561
1562
1563
1564
1565





















































1566
1567

1568
1569
1570
1571

  return TCL_OK;

 bad_args:
  Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-fullshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?");
  return TCL_ERROR;
}






















































int Sqlitetestvfs_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);

  return TCL_OK;
}

#endif








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


>




1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625

  return TCL_OK;

 bad_args:
  Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-fullshm BOOL? ?-default BOOL? ?-mxpathname INT? ?-szosfile INT? ?-iversion INT?");
  return TCL_ERROR;
}

extern int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb);
extern const char *sqlite3ErrName(int);

/*
** tclcmd: vfs_shmlock DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N
*/
static int SQLITE_TCLAPI test_vfs_shmlock(
  void * clientData,
  Tcl_Interp *interp,
  int objc,
  Tcl_Obj *CONST objv[]
){
  const char *azArg1[] = {"shared", "exclusive", 0};
  const char *azArg2[] = {"lock", "unlock", 0};
  sqlite3 *db = 0;
  int rc = SQLITE_OK;
  const char *zDbname = 0;
  int iArg1 = 0;
  int iArg2 = 0;
  int iOffset = 0;
  int n = 0;
  sqlite3_file *pFd;

  if( objc!=7 ){
    Tcl_WrongNumArgs(interp, 1, objv, 
        "DB DBNAME (shared|exclusive) (lock|unlock) OFFSET N"
    );
    return TCL_ERROR;
  }

  zDbname = Tcl_GetString(objv[2]);
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) 
   || Tcl_GetIndexFromObj(interp, objv[3], azArg1, "ARG", 0, &iArg1) 
   || Tcl_GetIndexFromObj(interp, objv[4], azArg2, "ARG", 0, &iArg2) 
   || Tcl_GetIntFromObj(interp, objv[5], &iOffset)
   || Tcl_GetIntFromObj(interp, objv[6], &n)
  ){
    return TCL_ERROR;
  }

  sqlite3_file_control(db, zDbname, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd);
  if( pFd==0 ){
    return TCL_ERROR;
  }
  rc = pFd->pMethods->xShmLock(pFd, iOffset, n, 
      (iArg1==0 ? SQLITE_SHM_SHARED : SQLITE_SHM_EXCLUSIVE)
    | (iArg2==0 ? SQLITE_SHM_LOCK : SQLITE_SHM_UNLOCK)
  );
  Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  return TCL_OK;
}


int Sqlitetestvfs_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
  Tcl_CreateObjCommand(interp, "vfs_shmlock", test_vfs_shmlock, 0, 0);
  return TCL_OK;
}

#endif

Changes to src/wal.c.

3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
....
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
      **         checkpoint need not have completed for this to cause problems.
      */
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);

      assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
      assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );

      /* It is possible that there is a checkpointer thread running 
      ** concurrent with this code. If this is the case, it may be that the
      ** checkpointer has already determined that it will checkpoint 
      ** snapshot X, where X is later in the wal file than pSnapshot, but 
      ** has not yet set the pInfo->nBackfillAttempted variable to indicate 
      ** its intent. To avoid the race condition this leads to, ensure that
      ** there is no checkpointer process by taking a shared CKPT lock 
      ** before checking pInfo->nBackfillAttempted.  
      **
      ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
      **       this already?
      */
      rc = walLockShared(pWal, WAL_CKPT_LOCK);

      if( rc==SQLITE_OK ){
        /* Check that the wal file has not been wrapped. Assuming that it has
        ** not, also check that no checkpointer has attempted to checkpoint any
        ** frames beyond pSnapshot->mxFrame. If either of these conditions are
        ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
        ** with *pSnapshot and set *pChanged as appropriate for opening the
................................................................................
          assert( pWal->readLock>0 );
          memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
          *pChanged = bChanged;
        }else{
          rc = SQLITE_ERROR_SNAPSHOT;
        }

        /* Release the shared CKPT lock obtained above. */
        walUnlockShared(pWal, WAL_CKPT_LOCK);
        pWal->minFrame = 1;
      }


      if( rc!=SQLITE_OK ){
        sqlite3WalEndReadTransaction(pWal);
      }







|
|
|


|
|
|
<
<
<
<
<







 







<
<







3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422





3423
3424
3425
3426
3427
3428
3429
....
3434
3435
3436
3437
3438
3439
3440


3441
3442
3443
3444
3445
3446
3447
      **         checkpoint need not have completed for this to cause problems.
      */
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);

      assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
      assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );

      /* If it were possible for a checkpointer thread to run concurrent 
      ** with this code, it would be a problem. In this case, it could be
      ** that the checkpointer has already determined that it will checkpoint 
      ** snapshot X, where X is later in the wal file than pSnapshot, but 
      ** has not yet set the pInfo->nBackfillAttempted variable to indicate 
      ** its intent. Fortunately this is not possible, as the call to
      ** sqlite3WalSnapshotOpen() that sets pWal->pSnapshot also takes a
      ** SHARED lock on the checkpointer slot.  */






      if( rc==SQLITE_OK ){
        /* Check that the wal file has not been wrapped. Assuming that it has
        ** not, also check that no checkpointer has attempted to checkpoint any
        ** frames beyond pSnapshot->mxFrame. If either of these conditions are
        ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
        ** with *pSnapshot and set *pChanged as appropriate for opening the
................................................................................
          assert( pWal->readLock>0 );
          memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
          *pChanged = bChanged;
        }else{
          rc = SQLITE_ERROR_SNAPSHOT;
        }



        pWal->minFrame = 1;
      }


      if( rc!=SQLITE_OK ){
        sqlite3WalEndReadTransaction(pWal);
      }

Changes to test/lock_common.tcl.

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
# This file contains code used by several different test scripts. The
# code in this file allows testfixture to control another process (or
# processes) to test locking.
#

proc do_multiclient_test {varname script} {

  foreach code [list {
    if {[info exists ::G(valgrind)]} { db close ; continue }
    set ::code2_chan [launch_testfixture]
    set ::code3_chan [launch_testfixture]
    proc code2 {tcl} { testfixture $::code2_chan $tcl }
    proc code3 {tcl} { testfixture $::code3_chan $tcl }
    set tn 1
  } {
    proc code2 {tcl} { uplevel #0 $tcl }
    proc code3 {tcl} { uplevel #0 $tcl }
    set tn 2
  }] {




    faultsim_delete_and_reopen

    proc code1 {tcl} { uplevel #0 $tcl }
  
    # Open connections [db2] and [db3]. Depending on which iteration this
    # is, the connections may be created in this interpreter, or in 
    # interpreters running in other OS processes. As such, the [db2] and [db3]







|





<
|


<

>
>
>
>







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
# This file contains code used by several different test scripts. The
# code in this file allows testfixture to control another process (or
# processes) to test locking.
#

proc do_multiclient_test {varname script} {

  foreach {tn code} [list 1 {
    if {[info exists ::G(valgrind)]} { db close ; continue }
    set ::code2_chan [launch_testfixture]
    set ::code3_chan [launch_testfixture]
    proc code2 {tcl} { testfixture $::code2_chan $tcl }
    proc code3 {tcl} { testfixture $::code3_chan $tcl }

  } 2 {
    proc code2 {tcl} { uplevel #0 $tcl }
    proc code3 {tcl} { uplevel #0 $tcl }

  }] {
    # Do not run multi-process tests with the unix-excl VFS.
    #
    if {$tn==1 && [permutation]=="unix-excl"} continue

    faultsim_delete_and_reopen

    proc code1 {tcl} { uplevel #0 $tcl }
  
    # Open connections [db2] and [db3]. Depending on which iteration this
    # is, the connections may be created in this interpreter, or in 
    # interpreters running in other OS processes. As such, the [db2] and [db3]

Changes to test/permutations.test.

612
613
614
615
616
617
618










619
620
621
622
623
624
625
  Run some tests using the "test_onefile.c" demo
} -initialize {
  set ::G(perm:sqlite3_args) [list -vfs fs]
} -files {
  conflict.test  insert.test   insert2.test  insert3.test
  rollback.test  select1.test  select2.test  select3.test
}











# Run some tests using UTF-16 databases.
#
test_suite "utf16" -description {
  Run tests using UTF-16 databases
} -presql {
  pragma encoding = 'UTF-16'







>
>
>
>
>
>
>
>
>
>







612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  Run some tests using the "test_onefile.c" demo
} -initialize {
  set ::G(perm:sqlite3_args) [list -vfs fs]
} -files {
  conflict.test  insert.test   insert2.test  insert3.test
  rollback.test  select1.test  select2.test  select3.test
}

# Run some tests using the "unix-excl" VFS.
#
test_suite "unix-excl" -description {
  Run some tests using the "unix-excl" VFS
} -initialize {
  set ::G(perm:sqlite3_args) [list -vfs unix-excl]
} -files {
  shmlock.test
}

# Run some tests using UTF-16 databases.
#
test_suite "utf16" -description {
  Run tests using UTF-16 databases
} -presql {
  pragma encoding = 'UTF-16'

Added test/shmlock.test.



























































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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
93
94
95
96
97
98
99
100
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
# 2018 December 6
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#

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

set testprefix shmlock

ifcapable !wal {finish_test ; return }

sqlite3 db2 test.db
sqlite3 db3 test.db

do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
} {wal}
do_test 1.1 { execsql { SELECT * FROM t1 } db2 } {1 2}
do_test 1.2 { execsql { SELECT * FROM t1 } db3 } {1 2}

foreach {tn dbhandle cmd res} {
  1    db  {shared    lock   7 1}    OK
  2    db2 {exclusive lock   7 1}    BUSY
  3    db  {shared    unlock 7 1}    OK
  4    db2 {exclusive lock   7 1}    OK
  5    db  {shared    lock   7 1}    BUSY
  6    db  {exclusive lock   7 1}    BUSY
  7    db2 {exclusive unlock 7 1}    OK

  8    db  {exclusive lock   0 8}    OK
  9    db  {exclusive unlock 0 8}    OK
  10   db2 {exclusive lock   0 8}    OK
  11   db2 {exclusive unlock 0 8}    OK

  12   db  {shared    lock   0 1}    OK
  13   db2 {shared    lock   0 1}    OK
  14   db3 {shared    lock   0 1}    OK
  15   db3 {shared    unlock 0 1}    OK
  16   db3 {exclusive lock   0 1}    BUSY
  17   db2 {shared    unlock 0 1}    OK
  18   db3 {exclusive lock   0 1}    BUSY
  19   db  {shared    unlock 0 1}    OK
  20   db3 {exclusive lock   0 1}    OK
  21   db3 {exclusive unlock 0 1}    OK

  22   db  {shared    lock   3 1}    OK
  23   db2 {exclusive lock   2 2}    BUSY
  24   db  {shared    lock   2 1}    OK
  25   db2 {exclusive lock   0 5}    BUSY
  26   db2 {exclusive lock   0 4}    BUSY
  27   db2 {exclusive lock   0 3}    BUSY
  28   db  {shared    unlock 3 1}    OK
  29   db2 {exclusive lock   2 2}    BUSY
  28   db  {shared    unlock 2 1}    OK
  29   db2 {exclusive lock   2 2}    OK
  29   db2 {exclusive unlock 2 2}    OK
} {
  do_test 1.3.$tn [list vfs_shmlock $dbhandle main {*}$cmd] "SQLITE_$res"
}

db  close
db2 close
db3 close

if {[permutation]=="unix-excl"} {
  do_test 2.0 {
    for {set i 0} {$i < 256} {incr i} { 
      sqlite3 db$i test.db 
      execsql { SELECT * FROM t1 } db$i
    }
    for {set i 0} {$i < 255} {incr i} { 
      set rc [vfs_shmlock db$i main shared lock 4 1]
      if {$rc != "SQLITE_OK"} { error $rc }
    }

    vfs_shmlock db255 main shared lock 4 1
  } {SQLITE_BUSY}

  do_test 2.1 { vfs_shmlock db255 main exclusive lock   4 1 } SQLITE_BUSY
  do_test 2.2 { vfs_shmlock db0   main shared    unlock 4 1 } SQLITE_OK
  do_test 2.3 { vfs_shmlock db255 main shared    lock   4 1 } SQLITE_OK
  do_test 2.4 { vfs_shmlock db255 main shared    unlock 4 1 } SQLITE_OK
  do_test 2.5 { vfs_shmlock db255 main exclusive lock   4 1 } SQLITE_BUSY

  do_test 2.6 {
    for {set i 1} {$i < 255} {incr i} { 
      set rc [vfs_shmlock db255 main exclusive lock 4 1]
      if {$rc != "SQLITE_BUSY"} { error $rc }
      set rc [vfs_shmlock db$i main shared unlock 4 1]
      if {$rc != "SQLITE_OK"} { error $rc }
    }

    vfs_shmlock db255 main exclusive lock 4 1
  } {SQLITE_OK}

  vfs_shmlock db255 main exclusive unlock 4 1

  for {set i 0} {$i < 256} {incr i} {
    db$i close
  }
}

sqlite3 db0 test.db
sqlite3 db1 test.db
do_test 3.1 { execsql { SELECT * FROM t1 } db0 } {1 2}
do_test 3.2 { execsql { SELECT * FROM t1 } db1 } {1 2}

set L(0) {n n n n n n n n}
set L(1) {n n n n n n n n}
proc random_lock_test {idx} {
  global L
  set iSlot [expr int(rand()*8)]
  if {[expr int(rand()*2)]} {
    # Unlock operation
    if {[lindex $L($idx) $iSlot]!="n"} {
      vfs_shmlock db$idx main [lindex $L($idx) $iSlot] unlock $iSlot 1
      lset L($idx) $iSlot n
    }
  } else {
    # Lock operation
    if {[lindex $L($idx) $iSlot]=="n"} {
      set locktype [lindex {e s} [expr int(rand()*2)]]
      set n 1
      if {$locktype=="e"} {
        for {set l $iSlot} {$l<8 && [lindex $L($idx) $l]=="n"} {incr l} {}
        set n [expr int(rand()*($l-$iSlot))+1]
        # puts "iSlot=$iSlot l=$l L=$L($idx)"
        # puts "$iSlot $n"
      }
      set res [vfs_shmlock db$idx main $locktype lock $iSlot $n]

      set bBusy 0
      for {set i $iSlot} {$i<($iSlot+$n)} {incr i} {
        set other [lindex $L([expr ($idx+1)%2]) $i]
        if {($other!="n" && $locktype=="e")||($other=="e" && $locktype=="s")} {
          if {$res != "SQLITE_BUSY"} { error "BUSY not detected" }
          set bBusy 1
          break
        } 
      }

      if {$bBusy==0} {
        if {$res != "SQLITE_OK"} { error "BUSY false-positive" }
        for {set i $iSlot} {$i<($iSlot+$n)} {incr i} {
          lset L($idx) $i $locktype
        }
      }
    }
  }
}

set nStep 100000
for {set i 0} {$i < $nStep} {incr i} {
  random_lock_test 0
  random_lock_test 1
}

db0 close
db1 close

finish_test


Changes to test/wal.test.

1293
1294
1295
1296
1297
1298
1299

1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344

1345
1346
1347
1348
1349
1350
1351
#
#   3. Using connection 1, checkpoint the database. Make sure all
#      the data is present and the database is not corrupt.
#
# At one point, SQLite was failing to grow the mapping of the wal-index
# file in step 3 and the checkpoint was corrupting the database file.
#

do_test wal-20.1 {
  catch {db close}
  forcedelete test.db test.db-wal test.db-journal
  sqlite3 db test.db
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(randomblob(900));
    SELECT count(*) FROM t1;
  }
} {wal 1}
do_test wal-20.2 {
  set ::buddy [launch_testfixture]
  testfixture $::buddy {
    sqlite3 db test.db
    db transaction { db eval {
      PRAGMA wal_autocheckpoint = 0;
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 2 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 4 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 8 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 16 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 32 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 64 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 128 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 256 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 512 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 1024 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 2048 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 4096 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 8192 */
      INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 16384 */
    } }
  }
} {0}
do_test wal-20.3 {
  close $::buddy
  execsql { PRAGMA wal_checkpoint }
  execsql { SELECT count(*) FROM t1 }
} {16384}
do_test wal-20.4 {
  db close
  sqlite3 db test.db
  execsql { SELECT count(*) FROM t1 }
} {16384}
integrity_check wal-20.5


catch { db2 close }
catch { db close }

do_test wal-21.1 {
  faultsim_delete_and_reopen
  execsql { 







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







1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
#
#   3. Using connection 1, checkpoint the database. Make sure all
#      the data is present and the database is not corrupt.
#
# At one point, SQLite was failing to grow the mapping of the wal-index
# file in step 3 and the checkpoint was corrupting the database file.
#
if {[permutation]!="unix-excl"} {
  do_test wal-20.1 {
    catch {db close}
    forcedelete test.db test.db-wal test.db-journal
    sqlite3 db test.db
    execsql {
      PRAGMA journal_mode = WAL;
      CREATE TABLE t1(x);
      INSERT INTO t1 VALUES(randomblob(900));
      SELECT count(*) FROM t1;
    }
  } {wal 1}
  do_test wal-20.2 {
    set ::buddy [launch_testfixture]
    testfixture $::buddy {
      sqlite3 db test.db
      db transaction { db eval {
        PRAGMA wal_autocheckpoint = 0;
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 2 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 4 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 8 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 16 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 32 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 64 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 128 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 256 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 512 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 1024 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 2048 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 4096 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 8192 */
        INSERT INTO t1 SELECT randomblob(900) FROM t1;       /* 16384 */
      } }
    }
  } {0}
  do_test wal-20.3 {
    close $::buddy
    execsql { PRAGMA wal_checkpoint }
    execsql { SELECT count(*) FROM t1 }
  } {16384}
  do_test wal-20.4 {
    db close
    sqlite3 db test.db
    execsql { SELECT count(*) FROM t1 }
  } {16384}
  integrity_check wal-20.5
}

catch { db2 close }
catch { db close }

do_test wal-21.1 {
  faultsim_delete_and_reopen
  execsql {