SQLite

Check-in [64ecf7c7e5]
Login

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

Overview
Comment:Experimental implementation of pessimistic page-level locking based on rollback mode.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | server-edition
Files: files | file ages | folders
SHA3-256: 64ecf7c7e512827e8a5a42f9f3ad92ff57ec868820e3943dbc74d5823f9a889d
User & Date: dan 2017-04-26 20:45:00.409
Context
2017-04-27
13:05
If possible, delete the journal file when a database connection is closed. (check-in: d5b5326df2 user: dan tags: server-edition)
2017-04-26
20:45
Experimental implementation of pessimistic page-level locking based on rollback mode. (check-in: 64ecf7c7e5 user: dan tags: server-edition)
17:21
Add new test file cachespill.test. (check-in: 2d0b64316d user: dan tags: trunk)
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to main.mk.
65
66
67
68
69
70
71

72
73
74
75
76
77
78
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79







+







         icu.o insert.o json1.o legacy.o loadext.o \
         main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
         memjournal.o \
         mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
         notify.o opcodes.o os.o os_unix.o os_win.o \
         pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
         random.o resolve.o rowset.o rtree.o select.o sqlite3rbu.o status.o \
	 server.o \
         table.o threads.o tokenize.o treeview.o trigger.o \
         update.o userauth.o util.o vacuum.o \
         vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
	 vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
         utf.o vtab.o

LIBOBJ += sqlite3session.o
140
141
142
143
144
145
146


147
148
149
150
151
152
153
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156







+
+







  $(TOP)/src/pragma.h \
  $(TOP)/src/prepare.c \
  $(TOP)/src/printf.c \
  $(TOP)/src/random.c \
  $(TOP)/src/resolve.c \
  $(TOP)/src/rowset.c \
  $(TOP)/src/select.c \
  $(TOP)/src/server.c \
  $(TOP)/src/server.h \
  $(TOP)/src/status.c \
  $(TOP)/src/shell.c \
  $(TOP)/src/sqlite.h.in \
  $(TOP)/src/sqlite3ext.h \
  $(TOP)/src/sqliteInt.h \
  $(TOP)/src/sqliteLimit.h \
  $(TOP)/src/table.c \
Changes to src/pager.c.
702
703
704
705
706
707
708



709
710
711
712
713
714
715
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718







+
+
+







#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
#ifndef SQLITE_OMIT_WAL
  Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
  char *zWal;                 /* File name for write-ahead log */
#endif
#ifdef SQLITE_SERVER_EDITION
  Server *pServer;
#endif
};

/*
** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS 
** or CACHE_WRITE to sqlite3_db_status().
*/
831
832
833
834
835
836
837






838
839
840
841
842
843
844
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853







+
+
+
+
+
+







#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
# define pagerWalFrames(v,w,x,y) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif

#ifdef SQLITE_SERVER_EDITION
# define pagerIsServer(x) ((x)->pServer!=0)
#else
# define pagerIsServer(x) 0
#endif

#ifndef NDEBUG 
/*
** Usage:
**
**   assert( assert_pager_state(pPager) );
**
1128
1129
1130
1131
1132
1133
1134

1135
1136
1137
1138
1139
1140
1141
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151







+







*/
static int pagerUnlockDb(Pager *pPager, int eLock){
  int rc = SQLITE_OK;

  assert( !pPager->exclusiveMode || pPager->eLock==eLock );
  assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
  assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
  assert( eLock!=NO_LOCK || pagerIsServer(pPager)==0 );
  if( isOpen(pPager->fd) ){
    assert( pPager->eLock>=eLock );
    rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
    if( pPager->eLock!=UNKNOWN_LOCK ){
      pPager->eLock = (u8)eLock;
    }
    IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
1803
1804
1805
1806
1807
1808
1809






1810
1811
1812
1813
1814
1815
1816
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832







+
+
+
+
+
+







       || pPager->eState==PAGER_ERROR 
  );

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

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    sqlite3ServerEnd(pPager->pServer);
    pPager->eState = PAGER_OPEN;
  }else 
#endif
  if( pagerUseWal(pPager) ){
    assert( !isOpen(pPager->jfd) );
    sqlite3WalEndReadTransaction(pPager->pWal);
    pPager->eState = PAGER_OPEN;
  }else if( !pPager->exclusiveMode ){
    int rc;                       /* Error code returned by pagerUnlockDb() */
    int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
2101
2102
2103
2104
2105
2106
2107





2108
2109
2110
2111
2112
2113
2114
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135







+
+
+
+
+







  }

  if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
    rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
    if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    rc2 = sqlite3ServerReleaseWriteLocks(pPager->pServer);
  }else
#endif
  if( !pPager->exclusiveMode 
   && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
  ){
    rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
    pPager->changeCountDone = 0;
  }
  pPager->eState = PAGER_READER;
4074
4075
4076
4077
4078
4079
4080







4081
4082
4083
4084
4085
4086
4087
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115







+
+
+
+
+
+
+







  pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
  assert( db || pPager->pWal==0 );
  sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize,
      (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
  );
  pPager->pWal = 0;
#endif
#ifdef SQLITE_SERVER_EDITION
  if( pPager->pServer ){
    sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
    pPager->pServer = 0;
    sqlite3_free(pPager->zJournal);
  }
#endif
  pager_reset(pPager);
  if( MEMDB ){
    pager_unlock(pPager);
  }else{
    /* If it is open, sync the journal file before calling UnlockAndRollback.
    ** If this is not done, then an unsynced portion of the open journal 
5046
5047
5048
5049
5050
5051
5052








































































5053
5054
5055
5056
5057
5058
5059
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







        }
      }
    }
  }

  return rc;
}

#ifdef SQLITE_SERVER_EDITION
static int pagerServerConnect(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->tempFile==0 ){
    int iClient = 0;
    pPager->noLock = 1;
    pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
    rc = sqlite3ServerConnect(pPager, &pPager->pServer, &iClient);
    if( rc==SQLITE_OK ){
      pPager->zJournal = sqlite3_mprintf(
          "%s-journal%d", pPager->zFilename, iClient
      );
      if( pPager->zJournal==0 ){
        rc = SQLITE_NOMEM_BKPT;
      }
    }
  }
  return rc;
}

int sqlite3PagerRollbackJournal(Pager *pPager, int iClient){
  int rc;
  char *zJrnl = sqlite3_mprintf("%s-journal%d", pPager->zFilename, iClient);

  if( zJrnl ){
    int bExists = 0;
    sqlite3_file *jfd = 0;
    sqlite3_vfs * const pVfs = pPager->pVfs;

    rc = sqlite3OsAccess(pVfs, zJrnl, SQLITE_ACCESS_EXISTS, &bExists);
    if( rc==SQLITE_OK && bExists ){
      int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
      rc = sqlite3OsOpenMalloc(pVfs, zJrnl, &jfd, flags, &flags);
    }
    assert( rc==SQLITE_OK || jfd==0 );
    if( jfd ){
      sqlite3_file *saved_jfd = pPager->jfd;
      u8 saved_eState = pPager->eState;
      u8 saved_eLock = pPager->eLock;
      i64 saved_journalOff = pPager->journalOff;
      i64 saved_journalHdr = pPager->journalHdr;

      pPager->eLock = EXCLUSIVE_LOCK;
      pPager->eState = PAGER_WRITER_DBMOD;
      pPager->jfd = jfd;
      rc = pagerSyncHotJournal(pPager);
      if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);

      pPager->jfd = saved_jfd;
      pPager->eState = saved_eState;
      pPager->eLock = saved_eLock;
      pPager->journalOff = saved_journalOff;
      pPager->journalHdr = saved_journalHdr;

      sqlite3OsCloseFree(jfd);
      if( rc==SQLITE_OK ){
        rc = sqlite3OsDelete(pVfs, zJrnl, 0);
      }
    }
    sqlite3_free(zJrnl);
  }else{
    rc = SQLITE_NOMEM_BKPT;
  }

  return rc;
}

#else
# define pagerServerConnect(pPager) SQLITE_OK
#endif


/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerGet() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
5086
5087
5088
5089
5090
5091
5092
5093



5094
5095
5096
5097
5098
5099
5100
5186
5187
5188
5189
5190
5191
5192

5193
5194
5195
5196
5197
5198
5199
5200
5201
5202







-
+
+
+







  ** be OPEN or READER. READER is only possible if the pager is or was in 
  ** exclusive access mode.  */
  assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  assert( assert_pager_state(pPager) );
  assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
  assert( pPager->errCode==SQLITE_OK );

  if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
  if( !pagerUseWal(pPager) 
   && !pagerIsServer(pPager) 
   && pPager->eState==PAGER_OPEN ){
    int bHotJournal = 1;          /* True if there exists a hot journal-file */

    assert( !MEMDB );
    assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );

    rc = pager_wait_on_lock(pPager, SHARED_LOCK);
    if( rc!=SQLITE_OK ){
5265
5266
5267
5268
5269
5270
5271
5272
5273













5274
5275
5276
5277
5278
5279
5280
5367
5368
5369
5370
5371
5372
5373


5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
5391
5392
5393







-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







    /* If there is a WAL file in the file-system, open this database in WAL
    ** mode. Otherwise, the following function call is a no-op.
    */
    rc = pagerOpenWalIfPresent(pPager);
#ifndef SQLITE_OMIT_WAL
    assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
  }


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

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);
  }else
#endif
  if( pagerUseWal(pPager) ){
    assert( rc==SQLITE_OK );
    rc = pagerBeginReadTransaction(pPager);
  }

  if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
    rc = pagerPagecount(pPager, &pPager->dbSize);
5560
5561
5562
5563
5564
5565
5566






5567
5568
5569
5570
5571
5572
5573
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
5689
5690
5691
5692







+
+
+
+
+
+







*/
int sqlite3PagerGet(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    int rc = sqlite3ServerLock(pPager->pServer, pgno, 0);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif
  return pPager->xGet(pPager, pgno, ppPage, flags);
}

/*
** Acquire a page if it is already in the in-memory cache.  Do
** not read the page from disk.  Return a pointer to the page,
** or 0 if the page is not in cache. 
5861
5862
5863
5864
5865
5866
5867







5868
5869
5870
5871
5872
5873
5874
5980
5981
5982
5983
5984
5985
5986
5987
5988
5989
5990
5991
5992
5993
5994
5995
5996
5997
5998
5999
6000







+
+
+
+
+
+
+







       || pPager->eState==PAGER_WRITER_CACHEMOD
       || pPager->eState==PAGER_WRITER_DBMOD
  );
  assert( assert_pager_state(pPager) );
  assert( pPager->errCode==0 );
  assert( pPager->readOnly==0 );
  CHECK_PAGE(pPg);

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1);
    if( rc!=SQLITE_OK ) return rc;
  }
#endif

  /* The journal file needs to be opened. Higher level routines have already
  ** obtained the necessary locks to begin the write-transaction, but the
  ** rollback journal might not yet be open. Open it now if this is the case.
  **
  ** This is done before calling sqlite3PcacheMakeDirty() on the page. 
  ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then
6140
6141
6142
6143
6144
6145
6146


6147


6148
6149
6150
6151
6152
6153
6154
6266
6267
6268
6269
6270
6271
6272
6273
6274

6275
6276
6277
6278
6279
6280
6281
6282
6283







+
+
-
+
+







# define DIRECT_MODE 0
  assert( isDirectMode==0 );
  UNUSED_PARAMETER(isDirectMode);
#else
# define DIRECT_MODE isDirectMode
#endif

  if( 0==pagerIsServer(pPager) 
   && !pPager->changeCountDone 
  if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
   && ALWAYS(pPager->dbSize>0) 
  ){
    PgHdr *pPgHdr;                /* Reference to page 1 */

    assert( !pPager->tempFile && isOpen(pPager->fd) );

    /* Open page 1 of the file for writing. */
    rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
    assert( pPgHdr==0 || rc==SQLITE_OK );
Changes to src/pager.h.
231
232
233
234
235
236
237




238
239
231
232
233
234
235
236
237
238
239
240
241
242
243







+
+
+
+


  void sqlite3PagerRefdump(Pager*);
  void disable_simulated_io_errors(void);
  void enable_simulated_io_errors(void);
#else
# define disable_simulated_io_errors()
# define enable_simulated_io_errors()
#endif

#ifdef SQLITE_SERVER_EDITION
  int sqlite3PagerRollbackJournal(Pager*, int);
#endif

#endif /* SQLITE_PAGER_H */
Added src/server.c.





































































































































































































































































































































































































































































































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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
** 2017 April 24
**
** 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.
**
*************************************************************************
*/

#include "sqliteInt.h"

/*
** HMA file layout:
**
**      4 bytes - DMS slot. All connections read-lock this slot.
**
**   16*4 bytes - locking slots. Connections hold a read-lock on a locking slot
**                when they are connected, a write lock when they have an open
**                transaction.
**
**    N*4 bytes - Page locking slots. N is HMA_PAGELOCK_SLOTS.
**
** Page lock slot format:
**
**    Least significant HMA_CLIENT_SLOTS used for read-locks. If bit 0 is set,
**    client 0 holds a read-lock.
**
**    If (v) is the value of the locking slot and (v>>HMA_CLIENT_SLOTS) is
**    not zero, then the write-lock holder is client ((v>>HMA_CLIENT_SLOTS)-1).
**
*/

#ifdef SQLITE_SERVER_EDITION

#define HMA_CLIENT_SLOTS   16
#define HMA_PAGELOCK_SLOTS (256*1024)

#define HMA_FILE_SIZE (4 + 4*HMA_CLIENT_SLOTS + 4*HMA_PAGELOCK_SLOTS)

#include "unistd.h"
#include "fcntl.h"
#include "sys/mman.h"
#include "sys/types.h"
#include "sys/stat.h"

typedef struct ServerHMA ServerHMA;

struct ServerGlobal {
  sqlite3_mutex *mutex;
  ServerHMA *pHma;
};
static struct ServerGlobal g_server;

/*
** There is one instance of the following structure for each distinct 
** HMA file opened by clients within this process. 
*/
struct ServerHMA {
  char *zName;                         /* hma file path */
  int fd;                              /* Fd open on hma file */
  int nClient;                         /* Current number of clients */
  Server *aClient[HMA_CLIENT_SLOTS];   /* Local (this process) clients */
  u32 *aMap;                           /* MMapped hma file */
  ServerHMA *pNext;                    /* Next HMA in this process */

  dev_t st_dev;
  ino_t st_ino;
};

struct Server {
  ServerHMA *pHma;                /* Hma file object */
  int iClient;                    /* Client id */
  Pager *pPager;                  /* Associated pager object */

  int nAlloc;                     /* Allocated size of aLock[] array */
  int nLock;                      /* Number of entries in aLock[] */
  u32 *aLock;                     /* Mapped lock file */
};

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

static int posixLock(int fd, int iSlot, int eLock, int bBlock){
  int res;
  struct flock l;
  short aType[4] = {0, F_UNLCK, F_RDLCK, F_WRLCK};
  assert( eLock==SERVER_WRITE_LOCK 
       || eLock==SERVER_READ_LOCK 
       || eLock==SERVER_NO_LOCK 
  );
  memset(&l, 0, sizeof(l));
  l.l_type = aType[eLock];
  l.l_whence = SEEK_SET;
  l.l_start = iSlot*sizeof(u32);
  l.l_len = 1;

  res = fcntl(fd, (bBlock ? F_SETLKW : F_SETLK), &l);
  return (res==0 ? SQLITE_OK : SQLITE_BUSY);
}

static int serverMapFile(ServerHMA *p){
  assert( p->aMap==0 );
  p->aMap = mmap(0, HMA_FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
  if( p->aMap==0 ){
    return SQLITE_ERROR;
  }
  return SQLITE_OK;
}


static void serverDecrHmaRefcount(ServerHMA *pHma){
  if( pHma ){
    pHma->nClient--;
    if( pHma->nClient<=0 ){
      ServerHMA **pp;
      if( pHma->aMap ) munmap(pHma->aMap, HMA_FILE_SIZE);
      if( pHma->fd>=0 ) close(pHma->fd);
      for(pp=&g_server.pHma; *pp!=pHma; pp=&(*pp)->pNext);
      *pp = pHma->pNext;
      sqlite3_free(pHma);
    }
  }
}


static int serverOpenHma(Pager *pPager, const char *zPath, ServerHMA **ppHma){
  struct stat sStat;              /* Structure populated by stat() */
  int res;                        /* result of stat() */
  int rc = SQLITE_OK;             /* Return code */
  ServerHMA *pHma = 0;

  assert( sqlite3_mutex_held(g_server.mutex) );

  res = stat(zPath, &sStat);
  if( res!=0 ){
    sqlite3_log(SQLITE_CANTOPEN, "Failed to stat(%s)", zPath);
    rc = SQLITE_ERROR;
  }else{
    for(pHma=g_server.pHma; pHma; pHma=pHma->pNext){
      if( sStat.st_dev==pHma->st_dev && sStat.st_ino==pHma->st_ino ) break;
    }
    if( pHma==0 ){
      int nPath = strlen(zPath);
      int nByte = sizeof(ServerHMA) + nPath+1 + 4;

      pHma = (ServerHMA*)sqlite3_malloc(nByte);
      if( pHma==0 ){
        rc = SQLITE_NOMEM;
      }else{
        int i;
        memset(pHma, 0, nByte);
        pHma->zName = (char*)&pHma[1];
        pHma->nClient = 1;
        pHma->st_dev = sStat.st_dev;
        pHma->st_ino = sStat.st_ino;
        pHma->pNext = g_server.pHma;
        g_server.pHma = pHma;

        memcpy(pHma->zName, zPath, nPath);
        memcpy(&pHma->zName[nPath], "-hma", 5);

        pHma->fd = open(pHma->zName, O_RDWR|O_CREAT, 0644);
        if( pHma->fd<0 ){
          sqlite3_log(SQLITE_CANTOPEN, "Failed to open(%s)", pHma->zName);
          rc = SQLITE_ERROR;
        }

        if( rc==SQLITE_OK ){
          /* Write-lock the DMS slot. If successful, initialize the hma file. */
          rc = posixLock(pHma->fd, 0, SERVER_WRITE_LOCK, 0);
          if( rc==SQLITE_OK ){
            res = ftruncate(pHma->fd, HMA_FILE_SIZE);
            if( res!=0 ){
              sqlite3_log(SQLITE_CANTOPEN, 
                  "Failed to ftruncate(%s)", pHma->zName
              );
              rc = SQLITE_ERROR;
            }
            if( rc==SQLITE_OK ){
              rc = serverMapFile(pHma);
            }
            if( rc==SQLITE_OK ){
              memset(pHma->aMap, 0, HMA_FILE_SIZE);
            }else{
              rc = SQLITE_ERROR;
            }
          }else{
            rc = serverMapFile(pHma);
          }
          for(i=0; rc==SQLITE_OK && i<HMA_CLIENT_SLOTS; i++){
            rc = sqlite3PagerRollbackJournal(pPager, i);
          }
          if( rc==SQLITE_OK ){
            rc = posixLock(pHma->fd, 0, SERVER_READ_LOCK, 1);
          }
        }

        if( rc!=SQLITE_OK ){
          serverDecrHmaRefcount(pHma);
          pHma = 0;
        }
      }
    }else{
      pHma->nClient++;
    }
  }

  *ppHma = pHma;
  return rc;
}

static u32 *serverPageLockSlot(Server *p, Pgno pgno){
  int iSlot = pgno % HMA_PAGELOCK_SLOTS;
  return &p->pHma->aMap[1 + HMA_CLIENT_SLOTS + iSlot];
}
static u32 *serverClientSlot(Server *p, int iClient){
  return &p->pHma->aMap[1 + iClient];
}

/*
** Close the "connection" and *-hma file. This deletes the object passed
** as the first argument.
*/
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){
  if( p->pHma ){
    ServerHMA *pHma = p->pHma;
    sqlite3_mutex_enter(g_server.mutex);
    if( p->iClient>=0 ){
      u32 *pSlot = serverClientSlot(p, p->iClient);
      *pSlot = 0;
      assert( pHma->aClient[p->iClient]==p );
      pHma->aClient[p->iClient] = 0;
      posixLock(pHma->fd, p->iClient+1, SERVER_NO_LOCK, 0);
    }
    if( dbfd 
     && pHma->nClient==1 
     && SQLITE_OK==sqlite3OsLock(dbfd, SQLITE_LOCK_EXCLUSIVE)
    ){
      unlink(pHma->zName);
    }
    serverDecrHmaRefcount(pHma);
    sqlite3_mutex_leave(g_server.mutex);
  }
  sqlite3_free(p->aLock);
  sqlite3_free(p);
}

static int serverRollbackClient(Server *p, int iBlock){
  int rc;

  sqlite3_log(SQLITE_NOTICE, "Rolling back failed client %d", iBlock);

  /* Roll back any journal file for client iBlock. */
  rc = sqlite3PagerRollbackJournal(p->pPager, iBlock);

  /* Clear any locks held by client iBlock from the HMA file.  */
  if( rc==SQLITE_OK ){
    int i;
    for(i=0; i<HMA_PAGELOCK_SLOTS; i++){
      u32 *pSlot = serverPageLockSlot(p, (Pgno)i);
      u32 v = *pSlot;
      while( 1 ){
        u32 n = v & ~(1 << iBlock);
        if( (v>>HMA_CLIENT_SLOTS)==iBlock+1 ){
          n = n & ((1<<HMA_CLIENT_SLOTS)-1);
        }
        if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
        v = *pSlot;
      }
    }
  }

  return rc;
}


/*
** Open the *-hma file and "connect" to the system.
*/
int sqlite3ServerConnect(
  Pager *pPager, 
  Server **ppOut, 
  int *piClient
){
  const char *zPath = sqlite3PagerFilename(pPager, 0);
  int rc = SQLITE_OK;
  Server *p;

  p = (Server*)sqlite3_malloc(sizeof(Server));
  if( p==0 ){
    rc = SQLITE_NOMEM;
  }else{
    memset(p, 0, sizeof(Server));
    p->iClient = -1;
    p->pPager = pPager;

    sqlite3_mutex_enter(g_server.mutex);
    rc = serverOpenHma(pPager, zPath, &p->pHma);

    /* File is now mapped. Find a free client slot. */
    if( rc==SQLITE_OK ){
      int i;
      Server **aClient = p->pHma->aClient;
      int fd = p->pHma->fd;
      for(i=0; i<HMA_CLIENT_SLOTS; i++){
        if( aClient[i]==0 ){
          int res = posixLock(fd, i+1, SERVER_WRITE_LOCK, 0);
          if( res==SQLITE_OK ){
            u32 *pSlot = serverClientSlot(p, i);
            if( *pSlot ){
              rc = serverRollbackClient(p, i);
            }
            posixLock(fd, i+1, (!rc ? SERVER_READ_LOCK : SERVER_NO_LOCK), 0);
            break;
          }
        }
      }

      if( rc==SQLITE_OK ){
        if( i>HMA_CLIENT_SLOTS ){
          rc = SQLITE_BUSY;
        }else{
          u32 *pSlot = serverClientSlot(p, i);
          *piClient = p->iClient = i;
          aClient[i] = p;
          *pSlot = 1;
        }
      }
    }

    sqlite3_mutex_leave(g_server.mutex);
  }

  if( rc!=SQLITE_OK ){
    sqlite3ServerDisconnect(p, 0);
    p = 0;
  }
  *ppOut = p;
  return rc;
}

static int serverOvercomeLock(Server *p, int bWrite, u32 v, int *pbRetry){
  int rc = SQLITE_OK;
  int bLocal = 0;
  int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;

  if( iBlock<0 ){
    for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){
      if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break;
    }
  }
  assert( iBlock<HMA_CLIENT_SLOTS );

  sqlite3_mutex_enter(g_server.mutex);
  if( p->pHma->aClient[iBlock] ){
    bLocal = 1;
  }else{
    rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);
  }

  if( bLocal==0 && rc==SQLITE_OK ){
    rc = serverRollbackClient(p, iBlock);

    /* Release the lock on slot iBlock */
    posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
    if( rc==SQLITE_OK ){
      *pbRetry = 1;
    }
  }else{
    assert( rc==SQLITE_OK || rc==SQLITE_BUSY );
    rc = SQLITE_OK;
  }
  sqlite3_mutex_leave(g_server.mutex);

  return rc;
}

/*
** Begin a transaction.
*/
int sqlite3ServerBegin(Server *p){
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 0);
}

/*
** End a transaction (and release all locks).
*/
int sqlite3ServerEnd(Server *p){
  int i;
  for(i=0; i<p->nLock; i++){
    u32 *pSlot = serverPageLockSlot(p, p->aLock[i]);
    while( 1 ){
      u32 v = *pSlot;
      u32 n = v;
      if( (v>>HMA_CLIENT_SLOTS)==p->iClient+1 ){
        n = n & ((1 << HMA_CLIENT_SLOTS)-1);
      }
      n = n & ~(1 << p->iClient);
      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
    }
  }
  p->nLock = 0;
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
}

/*
** Release all write-locks.
*/
int sqlite3ServerReleaseWriteLocks(Server *p){
  int rc = SQLITE_OK;
  return rc;
}

/*
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite){
  int rc = SQLITE_OK;

  /* Grow the aLock[] array, if required */
  if( p->nLock==p->nAlloc ){
    int nNew = p->nAlloc ? p->nAlloc*2 : 128;
    u32 *aNew;
    aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew);
    if( aNew==0 ){
      rc = SQLITE_NOMEM_BKPT;
    }else{
      p->aLock = aNew;
      p->nAlloc = nNew;
    }
  }
  if( rc==SQLITE_OK ){
    u32 *pSlot = serverPageLockSlot(p, pgno);
    u32 v = *pSlot;

    /* Check if the required lock is already held. If so, exit this function
    ** early. Otherwise, add an entry to the aLock[] array to record the fact
    ** that the lock may need to be released.  */
    if( bWrite ){
      int iLock = ((int)(v>>HMA_CLIENT_SLOTS)) - 1;
      if( iLock==p->iClient ) goto server_lock_out;
      if( iLock<0 ){
        p->aLock[p->nLock++] = pgno;
      }
    }else{
      if( v & (1<<p->iClient) ) goto server_lock_out;
      p->aLock[p->nLock++] = pgno;
    }

    while( 1 ){
      u32 n;

      while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){
        int bRetry = 0;
        rc = serverOvercomeLock(p, bWrite, v, &bRetry);
        if( rc!=SQLITE_OK ) goto server_lock_out;
        if( bRetry==0 ){
          /* There is a conflicting lock. Cannot obtain this lock. */
          sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno);
          rc = SQLITE_BUSY_DEADLOCK;
          goto server_lock_out;
        }
        v = *pSlot;
      }

      if( bWrite ){
        n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS);
      }else{
        n = v | (1 << p->iClient);
      }
      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
      v = *pSlot;
    }
  }

server_lock_out:
  return rc;
}

#endif /* ifdef SQLITE_SERVER_EDITION */
Changes to src/sqlite.h.in.
493
494
495
496
497
498
499

500
501
502
503
504
505
506
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507







+







#define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE             (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH              (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
#define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
#define SQLITE_BUSY_DEADLOCK           (SQLITE_BUSY   |  (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
Changes to src/sqliteInt.h.
1097
1098
1099
1100
1101
1102
1103

1104
1105
1106
1107
1108
1109
1110
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111







+







*/
#include "btree.h"
#include "vdbe.h"
#include "pager.h"
#include "pcache.h"
#include "os.h"
#include "mutex.h"
#include "server.h"

/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
** synchronous setting to EXTRA.  It is no longer supported.
*/
#ifdef SQLITE_EXTRA_DURABLE
# warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE
# define SQLITE_DEFAULT_SYNCHRONOUS 3
Changes to src/vdbeaux.c.
2635
2636
2637
2638
2639
2640
2641
2642


2643
2644
2645
2646
2647




2648
2649
2650
2651
2652
2653
2654
2655
2656

2657
2658
2659
2660
2661
2662
2663
2635
2636
2637
2638
2639
2640
2641

2642
2643
2644




2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656

2657
2658
2659
2660
2661
2662
2663
2664







-
+
+

-
-
-
-
+
+
+
+








-
+








    /* Lock all btrees used by the statement */
    sqlite3VdbeEnter(p);

    /* Check for one of the special errors */
    mrc = p->rc & 0xff;
    isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL
                     || p->rc==SQLITE_BUSY_DEADLOCK;
    if( isSpecialError ){
      /* If the query was read-only and the error code is SQLITE_INTERRUPT, 
      ** no rollback is necessary. Otherwise, at least a savepoint 
      ** transaction must be rolled back to restore the database to a 
      ** consistent state.
      /* If the query was read-only and the error code is SQLITE_INTERRUPT
      ** or SQLITE_BUSY_SERVER, no rollback is necessary. Otherwise, at 
      ** least a savepoint transaction must be rolled back to restore the
      ** database to a consistent state.
      **
      ** Even if the statement is read-only, it is important to perform
      ** a statement or transaction rollback operation. If the error 
      ** occurred while writing to the journal, sub-journal or database
      ** file as part of an effort to free up cache space (see function
      ** pagerStress() in pager.c), the rollback is required to restore 
      ** the pager to a consistent state.
      */
      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
      if( !p->readOnly || (mrc!=SQLITE_INTERRUPT && mrc!=SQLITE_BUSY) ){
        if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
          eStatementOp = SAVEPOINT_ROLLBACK;
        }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);
Changes to test/permutations.test.
268
269
270
271
272
273
274








275
276
277
278
279
280
281
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289







+
+
+
+
+
+
+
+







  fts3corrupt3.test
  fts3misc.test
}

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

test_suite "server" -prefix "" -description {
  All server-edition tests.
} -files [
  test_set \
      [glob -nocomplain $::testdir/server*.test] \
      -exclude *server1.test
]

test_suite "fts5-light" -prefix "" -description {
  All FTS5 tests.
} -files [
  test_set \
      [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \
      -exclude *corrupt* *fault* *big* *fts5aj*
Added test/server2.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 2017 April 25
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the server mode of SQLite.
#


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

#-------------------------------------------------------------------------
# Check that the *-hma file is deleted correctly.
#
do_execsql_test 1.0 {
  CREATE TABLE t1(a, b);
} {}
do_test 1.1 {
  file exists test.db-hma
} {1}
do_test 1.2 {
  db close
  file exists test.db-hma
} {0}
do_test 1.3 {
  sqlite3 db test.db
  db eval { CREATE TABLE t2(a, b) }
  sqlite3 db2 test.db
  db2 eval { CREATE TABLE t3(a, b) }
  file exists test.db-hma
} {1}
do_test 1.4 {
  db2 close
  file exists test.db-hma
} {1}
do_test 1.5 {
  db close
  file exists test.db-hma
} {0}


#-------------------------------------------------------------------------
#
reset_db
sqlite3 db2 test.db

do_execsql_test 2.0 {
  CREATE TABLE t1(a, b);
  CREATE TABLE t2(c, d);
}

# Two concurrent transactions committed.
#
do_test 2.1 {
  db eval {
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
  }
  db2 eval {
    BEGIN;
      INSERT INTO t2 VALUES(3, 4);
  }
} {}
do_test 2.2 {
  lsort [glob test.db*]
} {test.db test.db-hma test.db-journal0 test.db-journal1}
do_test 2.3.1 { db eval COMMIT  } {}
do_test 2.3.2 { db2 eval COMMIT } {}
do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
do_test 2.5 {
  lsort [glob test.db*]
} {test.db test.db-hma test.db-journal0 test.db-journal1}

do_test 2.6 {
  execsql {BEGIN}
  execsql {INSERT INTO t1 VALUES(5, 6)}

  execsql {BEGIN} db2
  catchsql {INSERT INTO t1 VALUES(7, 8)} db2
} {1 {database is locked}}
do_test 2.7 {
  # Transaction is automatically rolled back in this case.
  sqlite3_get_autocommit db2
} {1}
do_test 2.8 {
  execsql COMMIT
  execsql { SELECT * FROM t1 } db2
} {1 2 5 6}



finish_test

Added test/server3.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 2017 April 25
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is testing the server mode of SQLite.
#


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

db close

do_multiclient_test tn {
  do_test $tn.1 {
    sql1 { CREATE TABLE t1(a, b) }
    sql2 { CREATE TABLE t2(a, b) }
  } {}

  do_test $tn.2 {
    sql1 {
      INSERT INTO t2 VALUES(1, 2);
      BEGIN;
        INSERT INTO t1 VALUES(1, 2);
    }
  } {}

  do_test $tn.3 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
  do_test $tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
  do_test $tn.5 {  sql2 { SELECT * FROM t2 } } {1 2}


}

finish_test

Added test/servercrash.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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# 2017 April 27
#
# 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 servercrash

ifcapable !crashtest {
  finish_test
  return
}
do_not_use_codec

do_execsql_test 1.0 {
  PRAGMA page_siBlockze = 4096;
  PRAGMA auto_vacuum = OFF;
  CREATE TABLE t1(a, b);
  CREATE TABLE t2(c, d);

  INSERT INTO t1 VALUES(1, 2), (3, 4);
  INSERT INTO t2 VALUES(1, 2), (3, 4);
}

for {set i 0} {$i < 10} {incr i} {
  do_test 1.$i.1 {
    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
  } {1 {child process exited abnormally}}

  do_execsql_test 1.$i.2 {
    SELECT * FROM t1
  } {1 2 3 4}
}

for {set i 0} {$i < 10} {incr i} {
  do_test 2.$i.1 {
    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
  } {1 {child process exited abnormally}}

  do_test 2.$i.2 {
    sqlite3 dbX test.db
    execsql { SELECT * FROM t1 } dbX
  } {1 2 3 4}
  dbX close
}

db close
for {set i 0} {$i < 10} {incr i} {
  do_test 3.$i.1 {
    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
  } {1 {child process exited abnormally}}

  sqlite3 db test.db
  do_execsql_test 3.$i.2 { SELECT * FROM t1 } {1 2 3 4}
  db close
}

finish_test

Changes to test/tester.tcl.
582
583
584
585
586
587
588




589
590
591
592
593
594
595
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599







+
+
+
+







# Create a test database
#
proc reset_db {} {
  catch {db close}
  forcedelete test.db
  forcedelete test.db-journal
  forcedelete test.db-wal
  for {set i 0} {$i < 16} {incr i} {
    forcedelete test.db-journal$i
  }

  sqlite3 db ./test.db
  set ::DB [sqlite3_connection_pointer db]
  if {[info exists ::SETUP_SQL]} {
    db eval $::SETUP_SQL
  }
}
reset_db
Changes to tool/mksqlite3c.tcl.
110
111
112
113
114
115
116

117
118
119
120
121
122
123
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124







+







   os_win.h
   os.h
   pager.h
   parse.h
   pcache.h
   pragma.h
   rtree.h
   server.h
   sqlite3session.h
   sqlite3.h
   sqlite3ext.h
   sqlite3rbu.h
   sqliteicu.h
   sqliteInt.h
   sqliteLimit.h
315
316
317
318
319
320
321

322
323
324
325
326
327
328
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330







+








   bitvec.c
   pcache.c
   pcache1.c
   rowset.c
   pager.c
   wal.c
   server.c

   btmutex.c
   btree.c
   backup.c

   vdbemem.c
   vdbeaux.c