/ Check-in [dfa9a4d5]
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:Support clients within a single process only.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | server-process-edition
Files: files | file ages | folders
SHA3-256: dfa9a4d53e0aa7f7911b11af57a4d37a1df2d835cf60eb9e76d28b6b39c2ba58
User & Date: dan 2017-06-27 20:23:07
Context
2017-06-28
20:21
Merge tserver fixes with this branch. check-in: 58a0aab8 user: dan tags: server-process-edition
2017-06-27
20:23
Support clients within a single process only. check-in: dfa9a4d5 user: dan tags: server-process-edition
2017-06-20
19:20
Ensure that test tool "tserver" finalizes all statements before attempting to close a database handle. check-in: d8568aac user: dan tags: server-edition
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

3773
3774
3775
3776
3777
3778
3779






3780
3781
3782
3783
3784
3785
3786

/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
  unixFile *pFile = (unixFile*)id;
  switch( op ){






    case SQLITE_FCNTL_LOCKSTATE: {
      *(int*)pArg = pFile->eFileLock;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_LAST_ERRNO: {
      *(int*)pArg = pFile->lastErrno;
      return SQLITE_OK;







>
>
>
>
>
>







3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792

/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
  unixFile *pFile = (unixFile*)id;
  switch( op ){
    case SQLITE_FCNTL_FILEID: {
      i64 *aId = (i64)pArg;
      aId[0] = (i64)(pFile->pInode->fileId.dev);
      aId[1] = (i64)(pFile->pInode->fileId.ino);
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_LOCKSTATE: {
      *(int*)pArg = pFile->eFileLock;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_LAST_ERRNO: {
      *(int*)pArg = pFile->lastErrno;
      return SQLITE_OK;

Changes to src/pager.c.

4112
4113
4114
4115
4116
4117
4118

4119
4120
4121
4122
4123
4124
4125

4126
4127
4128
4129
4130
4131
4132
4133
4134

4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
....
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
5160
5161
5162
....
5385
5386
5387
5388
5389
5390
5391

5392
5393



5394
5395
5396
5397
5398
5399
5400
    ** If an error occurs while trying to sync the journal, shift the pager
    ** into the ERROR state. This causes UnlockAndRollback to unlock the
    ** database and close the journal file without attempting to roll it
    ** back or finalize it. The next database user will have to do hot-journal
    ** rollback before accessing the database file.
    */
    if( isOpen(pPager->jfd) ){

      if( pagerIsServer(pPager) ){
        assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
        pPager->journalMode = PAGER_JOURNALMODE_DELETE;
        /* If necessary, change the pager state so that the journal file 
        ** is deleted by the call to pagerUnlockAndRollback() below.  */
        if( pPager->eState==PAGER_OPEN ) pPager->eState = PAGER_READER;
      }

      pager_error(pPager, pagerSyncHotJournal(pPager));
    }
    pagerUnlockAndRollback(pPager);
  }
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
    pPager->pServer = 0;
    sqlite3_free(pPager->zJournal);

  }
#endif
  sqlite3EndBenignMalloc();
  enable_simulated_io_errors();
  PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
  IOTRACE(("CLOSE %p\n", pPager))
  sqlite3OsClose(pPager->jfd);
  sqlite3OsClose(pPager->fd);
  sqlite3PageFree(pTmp);
  sqlite3PcacheClose(pPager->pPCache);

#ifdef SQLITE_HAS_CODEC
  if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
#endif

  assert( !pPager->aSavepoint && !pPager->pInJournal );
  assert( !isOpen(pPager->jfd) && !isOpen(pPager->sjfd) );

  sqlite3_free(pPager);
  return SQLITE_OK;
}

#if !defined(NDEBUG) || defined(SQLITE_TEST)
/*
................................................................................
  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;
      char *saved_zJournal = pPager->zJournal;



      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;
      pPager->zJournal = saved_zJournal;


      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


................................................................................
    assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );

    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);



  }
#endif
  if( rc==SQLITE_OK && pagerUseWal(pPager) ){
    assert( rc==SQLITE_OK );
    rc = pagerBeginReadTransaction(pPager);
  }








>







>








|
>





|
<









|







 







<


|
<
<
<
<
<
<
|
<
<



|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
|
|
|
|
<

>
>
|
|
|
|
|

>
|
|
|
|
|
<
>
|
<
<
<
|
<
>
>
|
<
<
<
<
<
>
>
>
>







 







>


>
>
>







4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143

4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
....
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
....
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
    ** If an error occurs while trying to sync the journal, shift the pager
    ** into the ERROR state. This causes UnlockAndRollback to unlock the
    ** database and close the journal file without attempting to roll it
    ** back or finalize it. The next database user will have to do hot-journal
    ** rollback before accessing the database file.
    */
    if( isOpen(pPager->jfd) ){
#if 0
      if( pagerIsServer(pPager) ){
        assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );
        pPager->journalMode = PAGER_JOURNALMODE_DELETE;
        /* If necessary, change the pager state so that the journal file 
        ** is deleted by the call to pagerUnlockAndRollback() below.  */
        if( pPager->eState==PAGER_OPEN ) pPager->eState = PAGER_READER;
      }
#endif
      pager_error(pPager, pagerSyncHotJournal(pPager));
    }
    pagerUnlockAndRollback(pPager);
  }
#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
    pPager->pServer = 0;
  }else{
    sqlite3OsClose(pPager->jfd);
  }
#endif
  sqlite3EndBenignMalloc();
  enable_simulated_io_errors();
  PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
  IOTRACE(("CLOSE %p\n", pPager));

  sqlite3OsClose(pPager->fd);
  sqlite3PageFree(pTmp);
  sqlite3PcacheClose(pPager->pPCache);

#ifdef SQLITE_HAS_CODEC
  if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec);
#endif

  assert( !pPager->aSavepoint && !pPager->pInJournal );
  assert( !isOpen(pPager->sjfd) );

  sqlite3_free(pPager);
  return SQLITE_OK;
}

#if !defined(NDEBUG) || defined(SQLITE_TEST)
/*
................................................................................
  return rc;
}

#ifdef SQLITE_SERVER_EDITION
static int pagerServerConnect(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->tempFile==0 ){

    pPager->noLock = 1;
    pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
    rc = sqlite3ServerConnect(pPager, &pPager->pServer);






  }


  return rc;
}

int sqlite3PagerRollbackJournal(Pager *pPager, sqlite3_file *pJfd){















  int rc;                         /* Return Code */
  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;


  assert( pPager->journalMode==PAGER_JOURNALMODE_PERSIST );

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

  assert( isOpen(pPager->jfd) );
  pPager->jfd = saved_jfd;
  pPager->eState = saved_eState;
  pPager->eLock = saved_eLock;
  pPager->journalOff = saved_journalOff;
  pPager->journalHdr = saved_journalHdr;

  return rc;
}





void sqlite3PagerServerJournal(
  Pager *pPager, 
  sqlite3_file *jfd,





  const char *zJournal
){
  pPager->zJournal = zJournal;
  pPager->jfd = jfd;
}

#else
# define pagerServerConnect(pPager) SQLITE_OK
#endif


................................................................................
    assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    assert( sqlite3PagerRefcount(pPager)==0 );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer);
    if( rc==SQLITE_OK){
      rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
    }
  }
#endif
  if( rc==SQLITE_OK && pagerUseWal(pPager) ){
    assert( rc==SQLITE_OK );
    rc = pagerBeginReadTransaction(pPager);
  }

Changes to src/pager.h.

233
234
235
236
237
238
239
240
241
242

243
244
245
  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);
  int sqlite3PagerIsServer(Pager *pPager);
  int sqlite3PagerPagelock(Pager *pPager, Pgno, int);

#endif

#endif /* SQLITE_PAGER_H */







|


>



233
234
235
236
237
238
239
240
241
242
243
244
245
246
  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*, sqlite3_file*);
  int sqlite3PagerIsServer(Pager *pPager);
  int sqlite3PagerPagelock(Pager *pPager, Pgno, int);
  void sqlite3PagerServerJournal(Pager*, sqlite3_file*, const char*);
#endif

#endif /* SQLITE_PAGER_H */

Changes to src/server.c.

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
...
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
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503


504
505
506

507
508
509
510
511
512
513
514
515

516
517
518
519
520

521
522
523



524
525
526


527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563

564
565
566
567
568
569
570




571

572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587

588
589
590
591
592
593
594
595
596
597
598
599
600
601
**
*************************************************************************
*/

#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-locking slot format:
**

**   Each page-locking slot provides SHARED/RESERVED/EXCLUSIVE locks on a
**   single page. A RESERVED lock is similar to a RESERVED in SQLite's
**   rollback mode - existing SHARED locks may continue but new SHARED locks
**   may not be established. As in rollback mode, EXCLUSIVE and RESERVED 
**   locks are mutually exclusive.
**
**   Each 32-bit locking slot is divided into two sections - a bitmask for
**   read-locks and a single integer field for the write lock. The bitmask
**   occupies the least-significant 27 bits of the slot. The integer field
**   occupies the remaining 5 bits (so that it can store values from 0-31).
**
**   Each client has a unique integer client id. Currently these range from
**   0-15 (maximum of 16 concurrent connections). The page-locking slot format
**   allows this to be increased to 0-26 (maximum of 26 connections). To
**   take a SHARED lock, the corresponding bit is set in the locking slot
**   bitmask:
**
**     slot = slot | (1 << iClient);
**
**   To take an EXCLUSIVE or RESERVED lock, the integer part of the locking
**   slot is set to the client-id of the locker plus one (a value of zero 
**   indicates that no connection holds a RESERVED or EXCLUSIVE lock):
**
**     slot = slot | ((iClient+1) << 27)
*/

#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"
#include "errno.h"

typedef struct ServerHMA ServerHMA;


struct ServerGlobal {
  ServerHMA *pHma;                /* Linked list of all ServerHMA objects */
};
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 */
  i64 nUsWrite;                   /* Cumulative us holding WRITER lock */
  i64 iUsWrite;                   /* Time WRITER lock was taken */









  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
................................................................................
static void serverLeaveMutex(void){
  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1));
}
static void serverAssertMutexHeld(void){
  assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) );
}

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);
  if( res && bBlock && errno==EDEADLK ){
    return SQLITE_BUSY_DEADLOCK;
  }
  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;

  serverAssertMutexHeld();

  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;
            }
            for(i=0; rc==SQLITE_OK && i<HMA_CLIENT_SLOTS; i++){
              rc = sqlite3PagerRollbackJournal(pPager, i);
            }
          }else{
            rc = serverMapFile(pHma);
          }
          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;
    serverEnterMutex();
    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);
    serverLeaveMutex();
  }
  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;

    serverEnterMutex();
    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;

        }
      }
    }

    serverLeaveMutex();
  }

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

static int serverOvercomeLock(
  Server *p,                      /* Server connection */
  int bWrite,                     /* True for a write-lock */
  int bBlock,                     /* If true, block for this lock */
  u32 v,                          /* Value of blocking page locking slot */
  int *pbRetry                    /* OUT: True if caller should retry lock */
){




  int rc = SQLITE_OK;
  int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;

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




    }

  }
  assert( iBlock<HMA_CLIENT_SLOTS );









  serverEnterMutex();

  if( 0==p->pHma->aClient[iBlock] ){
    rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);
    if( 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 if( rc==SQLITE_BUSY ){
      if( bBlock ){
        rc = posixLock(p->pHma->fd, iBlock+1, SERVER_READ_LOCK, 1);
        if( rc==SQLITE_OK ){
          posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
          *pbRetry = 1;



        }
      }

      if( rc==SQLITE_BUSY ){
        rc = SQLITE_OK;

      }
    }




  }





  serverLeaveMutex();


  return rc;
}


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

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


    }
    if( p->aLock[i]==0 ){
      struct timeval t2;
      i64 nUs;
      gettimeofday(&t2, 0);
      nUs = (i64)t2.tv_sec * 1000000 + t2.tv_usec - p->iUsWrite; 
      p->nUsWrite += nUs;
      if( (p->nUsWrite / 1000000)!=((p->nUsWrite + nUs)/1000000) ){
        sqlite3_log(SQLITE_WARNING, 
            "Cumulative WRITER time: %lldms\n", p->nUsWrite/1000
        );
      }
    }
  }
  p->nLock = 0;
#if 1
  return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
#endif


  return SQLITE_OK;
}

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

/*
** Return the client id of the client that currently holds the EXCLUSIVE
** or RESERVED lock according to page-locking slot value v. Or -1 if no
** client holds such a lock.
*/
int serverWriteLocker(u32 v){
  return ((int)(v >> HMA_CLIENT_SLOTS)) - 1;
}

/*
** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
**
** If parameter bBlock is non-zero, then make this a blocking lock if
** possible.
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
  int rc = SQLITE_OK;
  int bReserved = 0;


  u32 *pSlot = serverPageLockSlot(p, pgno);

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


    }else{
      if( v & (1<<p->iClient) ) goto server_lock_out;
    }
    p->aLock[p->nLock++] = pgno;

    while( 1 ){
      u32 n;
      int w;
      u32 mask = (bWrite ? (((1<<HMA_CLIENT_SLOTS)-1) & ~(1<<p->iClient)) : 0);

      while( ((w = serverWriteLocker(v))>=0 && w!=p->iClient) || (v & mask) ){
        int bRetry = 0;

        if( w<0 && bWrite && bBlock ){
          /* Attempt a RESERVED lock before anything else */
          n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS);
          assert( serverWriteLocker(n)==p->iClient );
          if( __sync_val_compare_and_swap(pSlot, v, n)!=v ){
            v = *pSlot;
            continue;
          }
          v = n;
          bReserved = 1;
        }

        rc = serverOvercomeLock(p, bWrite, bBlock, 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;
      }


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




  }


server_lock_out:
  if( rc!=SQLITE_OK && bReserved ){
    u32 n;
    u32 v;
    do{
      v = *pSlot;
      assert( serverWriteLocker(v)==p->iClient );
      n = v & ((1<<HMA_CLIENT_SLOTS)-1);
    }while( __sync_val_compare_and_swap(pSlot, v, n)!=v );
  }

  if( pgno==0 ){
    struct timeval t1;
    gettimeofday(&t1, 0);
    p->iUsWrite = ((i64)t1.tv_sec * 1000000) + (i64)t1.tv_usec;

  }
  assert( rc!=SQLITE_OK || sqlite3ServerHasLock(p, pgno, bWrite) );
  return rc;
}

int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite){
  u32 v = *serverPageLockSlot(p, pgno);
  if( bWrite ){
    return (v>>HMA_CLIENT_SLOTS)==(p->iClient+1);
  }
  return (v & (1 << p->iClient))!=0;
}

#endif /* ifdef SQLITE_SERVER_EDITION */







|

|

|
|
|

|
|
<

>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|


|
>
>
>
>
>
>
>
>
>
>








|
>


|



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







 







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



|
>
>
>
>



<
<
<
<
<
<
<
<

<
<
>


<
<
<
<
<
<
<
<
<
>
|
<
>
>
|
<
<
<
>
>
>
>
>
>
|
<
|
|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
>


|
|
<

>
|
>
|
<

>
>
|
|
<
<
<
<
|
<
<
<
<
<
>
>
|
<
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
|
<
<
>
|
<
<
<
<
<
<
|
<
<
<
<
>
|
|
|
<
<
<
<
<
<
<
<
|



<
<
<
<
<
<
<
>
>
>
>

<

<
<
<
>
>
>
>
|
>
|
<
>
>
>
>
>
>
>
>
|
<
|
<
<
<
<
>

<
<
<
<
>
|
<
<
<
|
<
<
>
>
>
|
|

<
<
>
|
|
>
>
>
>
|
>
>
>
>
|
<
>
|
<
|
>
|
<
<
<
<
<
<
<
<
<
|
<




<
<
<
<
<
<
<
<
>
>
>
|
<
<
>
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
|










<
<
<
<
<
<
<
<
<








|
>
>
|

<
>

|
<
|
<
|
|
<
|
>
|
|
|
<

>
|
|
|
>
>
>
|
<
<
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
<
>
|
<
<
|
<
<
|
>
>
>
>
|
>

<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
>

<




|
|
<
<
<



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

#include "sqliteInt.h"

/*
** Page-locking slot format:
**
**   Assuming HMA_MAX_TRANSACTIONID is set to 16.
**
**   The least-significant 16 bits are used for read locks. When a read
**   lock is taken, the client sets the bit associated with its 
**   transaction-id.
**
**   The next 8 bits are set to the number of transient-read locks 
**   currently held on the page.

**
**   The next 5 bits are set to 0 if no client currently holds a write
**   lock. Or to (transaction-id + 1) if a write lock is held.























*/

#ifdef SQLITE_SERVER_EDITION

/* Number of page-locking slots */
#define HMA_PAGELOCK_SLOTS (256*1024)

/* Maximum concurrent read/write transactions */
#define HMA_MAX_TRANSACTIONID 16

/*
** The argument to this macro is the value of a locking slot. It returns
** -1 if no client currently holds the write lock, or the transaction-id
** of the locker otherwise.
*/
#define slotGetWriter(v) (((int)((v) >> HMA_MAX_TRANSACTIONID) & 0x1f) -1)

#define slotReaderMask(v) ((v) & ((1 << HMA_MAX_TRANSACTIONID)-1))

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

typedef struct ServerDb ServerDb;
typedef struct ServerJournal ServerJournal;

struct ServerGlobal {
  ServerDb *pDb;                  /* Linked list of all ServerHMA objects */
};
static struct ServerGlobal g_server;

struct ServerJournal {
  char *zJournal;
  sqlite3_file *jfd;
};

/*
** There is one instance of the following structure for each distinct 
** database file opened in server mode by this process.
*/
struct ServerDb {
  sqlite3_mutex *mutex;           /* Non-recursive mutex */
  int nClient;                    /* Current number of clients */
  int bInit;                      /* True once initialized */
  u32 transmask;                  /* Bitmask of taken transaction ids */
  u32 *aSlot;                     /* Array of page locking slots */
  i64 aFileId[2];                 /* Opaque VFS file-id */
  ServerDb *pNext;                /* Next db in this process */

  sqlite3_vfs *pVfs;
  ServerJournal aJrnl[HMA_MAX_TRANSACTIONID];
  u8 *aJrnlFdSpace;
};

/*
** Once instance for each client connection open on a server mode database
** in this process.
*/
struct Server {
  ServerDb *pDb;                  /* Database object */
  Pager *pPager;                  /* Associated pager object */
  int iTransId;                   /* Current transaction id (or -1) */
  int 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
................................................................................
static void serverLeaveMutex(void){
  sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1));
}
static void serverAssertMutexHeld(void){
  assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) );
}

static int serverFindDatabase(Server *pNew, i64 *aFileId){
  ServerDb *p;
  int rc = SQLITE_OK;
  serverEnterMutex();
  for(p=g_server.pDb; p; p=p->pNext){
    if( p->aFileId[0]==aFileId[0] && p->aFileId[1]==aFileId[1] ){
      break;
    }
  }
  if( p==0 ){
    p = (ServerDb*)sqlite3MallocZero(sizeof(ServerDb));
    if( p ){
      p->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS);
      if( p->aSlot==0 ){
        rc = SQLITE_NOMEM_BKPT;
      }else{
        p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
#if SQLITE_THREADSAFE!=0
        if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT;
#endif
      }

      if( rc==SQLITE_NOMEM ){
        sqlite3_free(p->aSlot);
        sqlite3_free(p);
        p = 0;
      }else{
        p->nClient = 1;
        p->aFileId[0] = aFileId[0];
        p->aFileId[1] = aFileId[1];
        p->pNext = g_server.pDb;
        g_server.pDb = p;
      }
    }else{
      rc = SQLITE_NOMEM_BKPT;
    }
  }else{
    p->nClient++;
  }
  pNew->pDb = p;
  serverLeaveMutex();
  return rc;
}

/*
** Free all resources allocated by serverInitDatabase() associated with the
** object passed as the only argument.
*/
static void serverShutdownDatabase(ServerDb *pDb){
  int i;

  for(i=0; i<HMA_MAX_TRANSACTIONID; i++){
    ServerJournal *pJ = &pDb->aJrnl[i];
    if( pJ->jfd ){
      sqlite3OsClose(pJ->jfd);
      sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0);
    }
    sqlite3_free(pJ->zJournal);
  }
  memset(pDb->aJrnl, 0, sizeof(ServerJournal)*HMA_MAX_TRANSACTIONID);

  if( pDb->aJrnlFdSpace ){
    sqlite3_free(pDb->aJrnlFdSpace);
    pDb->aJrnlFdSpace = 0;
  }

  sqlite3_free(pDb->aSlot);
  pDb->bInit = 0;
}

/*
** This function is called when the very first connection to a database
** is established. It is responsible for rolling back any hot journal
** files found in the file-system.
*/
static int serverInitDatabase(Server *pNew){
  int nByte;
  int rc = SQLITE_OK;
  ServerDb *pDb = pNew->pDb;
  sqlite3_vfs *pVfs;
  const char *zFilename = sqlite3PagerFilename(pNew->pPager, 0);

  assert( zFilename );
  pVfs = pDb->pVfs = sqlite3PagerVfs(pNew->pPager);
  nByte = ROUND8(pVfs->szOsFile) * HMA_MAX_TRANSACTIONID;
  pDb->aJrnlFdSpace = (u8*)sqlite3MallocZero(nByte);
  if( pDb->aJrnlFdSpace==0 ){
    rc = SQLITE_NOMEM_BKPT;
  }else{
    u8 *a = pDb->aJrnlFdSpace;
    int i;
    for(i=0; rc==SQLITE_OK && i<HMA_MAX_TRANSACTIONID; i++){
      int bExists = 0;
      ServerJournal *pJ = &pDb->aJrnl[i];
      pJ->jfd = (sqlite3_file*)&a[ROUND8(pVfs->szOsFile)*i];
      pJ->zJournal = sqlite3_mprintf("%s-journal%d", zFilename, i);
      if( pJ->zJournal==0 ){
        rc = SQLITE_NOMEM_BKPT;
        break;
      }

      rc = sqlite3OsAccess(pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS, &bExists);
      if( rc==SQLITE_OK && bExists ){
        int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
        rc = sqlite3OsOpen(pVfs, pJ->zJournal, pJ->jfd, flags, &flags);
        if( rc==SQLITE_OK ){
          rc = sqlite3PagerRollbackJournal(pNew->pPager, pJ->jfd);
        }
      }
















    }
  }

  if( rc==SQLITE_OK ){
    pDb->bInit = 1;
  }else{
    serverShutdownDatabase(pNew->pDb);
  }
  return rc;
}









/*


** Close the connection.
*/
void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){









  ServerDb *pDb = p->pDb;


  serverEnterMutex();
  pDb->nClient--;
  if( pDb->nClient==0 ){



    ServerDb **pp;
    serverShutdownDatabase(pDb);
    for(pp=&g_server.pDb; *pp!=pDb; pp=&((*pp)->pNext));
    *pp = pDb->pNext;
    sqlite3_mutex_free(pDb->mutex);
    sqlite3_free(pDb);
  }

  serverLeaveMutex();

  sqlite3_free(p->aLock);
  sqlite3_free(p);
}






























/*

** Connect to the system.
*/
int sqlite3ServerConnect(
  Pager *pPager,
  Server **ppOut                  /* OUT: Server handle */

){
  Server *pNew = 0;
  sqlite3_file *dbfd = sqlite3PagerFile(pPager);
  i64 aFileId[2];
  int rc;


  rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_FILEID, (void*)aFileId);
  if( rc==SQLITE_OK ){
    pNew = (Server*)sqlite3MallocZero(sizeof(Server));
    if( pNew ){




      pNew->pPager = pPager;





      pNew->iTransId = -1;
      rc = serverFindDatabase(pNew, aFileId);
      if( rc!=SQLITE_OK ){










        sqlite3_free(pNew);
        pNew = 0;
      }else{
        sqlite3_mutex_enter(pNew->pDb->mutex);
        if( pNew->pDb->bInit==0 ){
          rc = serverInitDatabase(pNew);
        }


        sqlite3_mutex_leave(pNew->pDb->mutex);
      }






    }else{




      rc = SQLITE_NOMEM_BKPT;
    }
  }









  *ppOut = pNew;
  return rc;
}








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





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

    sqlite3_mutex_enter(pDb->mutex);


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

    }




    pDb->transmask = t;





    sqlite3_mutex_leave(pDb->mutex);




    if( rc==SQLITE_OK ){


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



  return rc;
}

static void serverReleaseLocks(Server *p){
  ServerDb *pDb = p->pDb;
  int i;
  assert( sqlite3_mutex_held(pDb->mutex) );

  for(i=0; i<p->nLock; i++){
    u32 *pSlot = &pDb->aSlot[p->aLock[i] % HMA_PAGELOCK_SLOTS];
    if( slotGetWriter(*pSlot)==p->iTransId ){
      *pSlot -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
    }

    *pSlot &= ~((u32)1 << p->iTransId);
  }


  p->nLock = 0;
}











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








  int rc = SQLITE_OK;
  ServerDb *pDb = p->pDb;
  sqlite3_mutex_enter(pDb->mutex);



  serverReleaseLocks(p);
  pDb->transmask &= ~((u32)1 << p->iTransId);


















  sqlite3_mutex_leave(pDb->mutex);
  p->iTransId = -1;
  return rc;
}

/*
** 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).
**
** If parameter bBlock is non-zero, then make this a blocking lock if
** possible.
*/
int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){
  int rc = SQLITE_OK;
  ServerDb *pDb = p->pDb;
  int iWriter;
  int bSkip = 0;
  u32 *pSlot;


  assert( p->nLock<=p->nAlloc );
  if( p->nLock==p->nAlloc ){
    int nNew = p->nLock ? p->nLock*2 : 256;

    u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32));

    if( aNew==0 ) return SQLITE_NOMEM_BKPT;
    memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock));

    p->nAlloc = nNew;
    p->aLock = aNew;
  }

  assert( p->iTransId>=0 );


  sqlite3_mutex_enter(pDb->mutex);
  pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS];
  iWriter = slotGetWriter(*pSlot);
  if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){
    bSkip = 1;
  }else if( iWriter>=0 ){
    rc = SQLITE_BUSY_DEADLOCK;
  }else if( bWrite ){


    if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){
      *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID);
    }else{





























      rc = SQLITE_BUSY_DEADLOCK;

    }




  }else{
    *pSlot |= (1 << p->iTransId);


  }



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

  sqlite3_mutex_leave(pDb->mutex);












  if( bSkip==0 ){



    p->aLock[p->nLock++] = pgno;
  }

  return rc;
}

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



}

#endif /* ifdef SQLITE_SERVER_EDITION */

Changes to src/server.h.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#ifndef SQLITE_SERVER_H
#define SQLITE_SERVER_H


typedef struct Server Server;

int sqlite3ServerConnect(Pager *pPager, Server **ppOut, int *piClient);

void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);

int sqlite3ServerBegin(Server *p);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerReleaseWriteLocks(Server *p);








|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

#ifndef SQLITE_SERVER_H
#define SQLITE_SERVER_H


typedef struct Server Server;

int sqlite3ServerConnect(Pager *pPager, Server **ppOut);

void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd);

int sqlite3ServerBegin(Server *p);
int sqlite3ServerEnd(Server *p);
int sqlite3ServerReleaseWriteLocks(Server *p);

Changes to src/sqlite.h.in.

1040
1041
1042
1043
1044
1045
1046

1047
1048
1049
1050
1051
1052
1053
#define SQLITE_FCNTL_WAL_BLOCK              24
#define SQLITE_FCNTL_ZIPVFS                 25
#define SQLITE_FCNTL_RBU                    26
#define SQLITE_FCNTL_VFS_POINTER            27
#define SQLITE_FCNTL_JOURNAL_POINTER        28
#define SQLITE_FCNTL_WIN32_GET_HANDLE       29
#define SQLITE_FCNTL_PDB                    30


/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO









>







1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
#define SQLITE_FCNTL_WAL_BLOCK              24
#define SQLITE_FCNTL_ZIPVFS                 25
#define SQLITE_FCNTL_RBU                    26
#define SQLITE_FCNTL_VFS_POINTER            27
#define SQLITE_FCNTL_JOURNAL_POINTER        28
#define SQLITE_FCNTL_WIN32_GET_HANDLE       29
#define SQLITE_FCNTL_PDB                    30
#define SQLITE_FCNTL_FILEID                 31

/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO


Changes to test/permutations.test.

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

test_suite "server" -prefix "" -description {
  All server-edition tests.
} -files [
  test_set \
      select1.test \
      [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] \







|
<
<







273
274
275
276
277
278
279
280


281
282
283
284
285
286
287
  All FTS5 tests.
} -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]

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


]

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

Changes to test/server2.test.

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
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# 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}
integrity_check 1.5







do_test 1.6 {
  db close
  file exists test.db-hma
} {0}








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

do_execsql_test 2.0 {
................................................................................
  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
................................................................................
reset_db
do_execsql_test 3.0 {
  CREATE TABLE t1(a, b);
}

do_test 3.1 {
  glob test.db*
} {test.db-journal0 test.db test.db-hma}

do_test 3.2 {
  db close
  glob test.db*
} {test.db}

finish_test









>
>
>
>
>

|

>



>

<
|
>
>


>
|
<
<
>
|
>
|
>
>
|
<
<
<

>
>
>
>
|
<
>
>
>
|
<
>
>
>
>
>
>
>


<
<
>
>

>
>
>
>







 







|





|







 







|








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
..
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# focus of this script is testing the server mode of SQLite.
#


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

db close
foreach f [glob -nocomplain test.db*] {
  forcedelete $f
}

#-------------------------------------------------------------------------
# Check that the *-journal* files are deleted correctly.
#
reset_db
do_execsql_test 1.0 {
  CREATE TABLE t1(a, b);
} {}

do_test 1.1 {

  lsort [glob test.db*]
} {test.db test.db-journal0}

do_test 1.2 {
  db close
  lsort [glob test.db*]
} {test.db}



sqlite3 db test.db
do_execsql_test 1.3 {
  CREATE TABLE t2(a, b);
} {}

sqlite3 db2 test.db



do_test 1.4 {
  db eval {
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
  }
  db2 eval {

    BEGIN;
      INSERT INTO t2 VALUES(3, 4);
  }
} {}


do_test 1.5 {
  db2 eval COMMIT
  db eval COMMIT
  lsort [glob test.db*]
} {test.db test.db-journal0 test.db-journal1}

do_test 1.6 {
  db close


  lsort [glob test.db*]
} {test.db test.db-journal0 test.db-journal1}

do_test 1.7 {
  db2 close
  lsort [glob test.db*]
} {test.db}

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

do_execsql_test 2.0 {
................................................................................
  db2 eval {
    BEGIN;
      INSERT INTO t2 VALUES(3, 4);
  }
} {}
do_test 2.2 {
  lsort [glob test.db*]
} {test.db 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-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
................................................................................
reset_db
do_execsql_test 3.0 {
  CREATE TABLE t1(a, b);
}

do_test 3.1 {
  glob test.db*
} {test.db-journal0 test.db}

do_test 3.2 {
  db close
  glob test.db*
} {test.db}

finish_test

Changes to test/server3.test.

17
18
19
20
21
22
23


24
25
26
27
28
29
30
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 {







>
>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
source $testdir/tester.tcl
source $testdir/lock_common.tcl
set testprefix server3

db close

do_multiclient_test tn {
  if {$tn==1} continue

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

  do_test $tn.2 {
    sql1 {