SQLite

Check-in [e77d29f644]
Login

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

Overview
Comment:Only open a db in server-mode if there is a directory named "db-journal" in the file-system and the VFS is an exclusive locking VFS.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | server-process-edition
Files: files | file ages | folders
SHA3-256: e77d29f64480179d7aae7d88adab2ba9ff042701e1f4e624a3a7775cb56a51cc
User & Date: dan 2017-07-24 19:23:20.202
Context
2017-07-24
19:43
Update this branch with latest changes from trunk. (check-in: d0719ad757 user: dan tags: server-process-edition)
19:23
Only open a db in server-mode if there is a directory named "db-journal" in the file-system and the VFS is an exclusive locking VFS. (check-in: e77d29f644 user: dan tags: server-process-edition)
2017-07-19
18:54
Merge latest trunk changes with this branch. (check-in: be0df0a65f user: dan tags: server-process-edition)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os_unix.c.
3773
3774
3775
3776
3777
3778
3779




















3780
3781
3782
3783
3784
3785
3786
3787
3788

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







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

|







3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808

/*
** 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_SERVER_MODE: {
      int rc = SQLITE_OK;
      int eServer = 0;
      if( pFile->ctrlFlags | UNIXFILE_EXCL ){
        char *zJrnl = sqlite3_mprintf("%s-journal", pFile->zPath);
        if( zJrnl==0 ){
          rc = SQLITE_NOMEM;
        }else{
          struct stat buf;        /* Used to hold return values of stat() */
          if( osStat(zJrnl, &buf) ){
            rc = SQLITE_IOERR_FSTAT;
          }else{
            eServer = ((buf.st_mode & S_IFDIR) ? 1 : 0);
          }
        }
        sqlite3_free(zJrnl);
      }
      *((int*)pArg) = eServer;
      return rc;
    }
    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;
5626
5627
5628
5629
5630
5631
5632









5633
5634
5635
5636
5637
5638
5639
#endif
      nDb--;
    }
    memcpy(zDb, zPath, nDb);
    zDb[nDb] = '\0';

    rc = getFileMode(zDb, pMode, pUid, pGid);









  }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
    *pMode = 0600;
  }else if( flags & SQLITE_OPEN_URI ){
    /* If this is a main database file and the file was opened using a URI
    ** filename, check for the "modeof" parameter. If present, interpret
    ** its value as a filename and try to copy the mode, uid and gid from
    ** that file.  */







>
>
>
>
>
>
>
>
>







5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
#endif
      nDb--;
    }
    memcpy(zDb, zPath, nDb);
    zDb[nDb] = '\0';

    rc = getFileMode(zDb, pMode, pUid, pGid);
#ifdef SQLITE_SERVER_EDITION
    if( rc==SQLITE_IOERR_FSTAT ){
      while( nDb && zDb[nDb]!='/' ) nDb--;
      if( nDb>8 && memcmp("-journal/", &zDb[nDb-8], 9)==0 ){
        zDb[nDb-8] = '\0';
        rc = getFileMode(zDb, pMode, pUid, pGid);
      }
    }
#endif
  }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
    *pMode = 0600;
  }else if( flags & SQLITE_OPEN_URI ){
    /* If this is a main database file and the file was opened using a URI
    ** filename, check for the "modeof" parameter. If present, interpret
    ** its value as a filename and try to copy the mode, uid and gid from
    ** that file.  */
Changes to src/pager.c.
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088







5089
5090
5091
5092
5093
5094
5095
**
** If a hot-journal file is found to exist, *pExists is set to 1 and 
** SQLITE_OK returned. If no hot-journal file is present, *pExists is
** set to 0 and SQLITE_OK returned. If an IO error occurs while trying
** to determine whether or not a hot-journal file exists, the IO error
** code is returned and the value of *pExists is undefined.
*/
static int hasHotJournal(Pager *pPager, int *pExists){
  sqlite3_vfs * const pVfs = pPager->pVfs;
  int rc = SQLITE_OK;           /* Return code */
  int exists = 1;               /* True if a journal file is present */
  int jrnlOpen = !!isOpen(pPager->jfd);

  assert( pPager->useJournal );
  assert( isOpen(pPager->fd) );
  assert( pPager->eState==PAGER_OPEN );

  assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
    SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
  ));

  *pExists = 0;
  if( !jrnlOpen ){
    rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
  }
  if( rc==SQLITE_OK && exists ){
    int locked = 0;             /* True if some process holds a RESERVED lock */








    /* Race condition here:  Another process might have been holding the
    ** the RESERVED lock and have a journal open at the sqlite3OsAccess() 
    ** call above, but then delete the journal and drop the lock before
    ** we get to the following sqlite3OsCheckReservedLock() call.  If that
    ** is the case, this routine might think there is a hot journal when
    ** in fact there is none.  This results in a false-positive which will







|



















>
>
>
>
>
>
>







5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
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
**
** If a hot-journal file is found to exist, *pExists is set to 1 and 
** SQLITE_OK returned. If no hot-journal file is present, *pExists is
** set to 0 and SQLITE_OK returned. If an IO error occurs while trying
** to determine whether or not a hot-journal file exists, the IO error
** code is returned and the value of *pExists is undefined.
*/
static int hasHotJournal(Pager *pPager, int *pExists, int *peServer){
  sqlite3_vfs * const pVfs = pPager->pVfs;
  int rc = SQLITE_OK;           /* Return code */
  int exists = 1;               /* True if a journal file is present */
  int jrnlOpen = !!isOpen(pPager->jfd);

  assert( pPager->useJournal );
  assert( isOpen(pPager->fd) );
  assert( pPager->eState==PAGER_OPEN );

  assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
    SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
  ));

  *pExists = 0;
  if( !jrnlOpen ){
    rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
  }
  if( rc==SQLITE_OK && exists ){
    int locked = 0;             /* True if some process holds a RESERVED lock */

#ifdef SQLITE_SERVER_EDITION
    rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SERVER_MODE, peServer);
    if( rc!=SQLITE_NOTFOUND ){
      if( rc!=SQLITE_OK || *peServer ) return rc;
    }
#endif

    /* Race condition here:  Another process might have been holding the
    ** the RESERVED lock and have a journal open at the sqlite3OsAccess() 
    ** call above, but then delete the journal and drop the lock before
    ** we get to the following sqlite3OsCheckReservedLock() call.  If that
    ** is the case, this routine might think there is a hot journal when
    ** in fact there is none.  This results in a false-positive which will
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
}

void sqlite3PagerServerJournal(
  Pager *pPager, 
  sqlite3_file *jfd,
  const char *zJournal
){
  pPager->zJournal = zJournal;
  pPager->jfd = jfd;
}

#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







|


<
<
<







5203
5204
5205
5206
5207
5208
5209
5210
5211
5212



5213
5214
5215
5216
5217
5218
5219
}

void sqlite3PagerServerJournal(
  Pager *pPager, 
  sqlite3_file *jfd,
  const char *zJournal
){
  pPager->zJournal = (char*)zJournal;
  pPager->jfd = jfd;
}



#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
5234
5235
5236
5237
5238
5239
5240



5241
5242
5243
5244
5245
5246
5247
**
** If everything is successful, SQLITE_OK is returned. If an IO error 
** occurs while locking the database, checking for a hot-journal file or 
** rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
  int rc = SQLITE_OK;                /* Return code */




  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** 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) );







>
>
>







5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
**
** If everything is successful, SQLITE_OK is returned. If an IO error 
** occurs while locking the database, checking for a hot-journal file or 
** rolling back a journal file, the IO error code is returned.
*/
int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){
  int rc = SQLITE_OK;                /* Return code */
#ifdef SQLITE_SERVER_EDITION
  int eServer = 0;
#endif

  /* This routine is only called from b-tree and only when there are no
  ** outstanding pages. This implies that the pager state should either
  ** 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) );
5262
5263
5264
5265
5266
5267
5268
5269

5270
5271
5272
5273
5274
5275
5276
      goto failed;
    }

    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->eLock<=SHARED_LOCK ){
      rc = hasHotJournal(pPager, &bHotJournal);

    }
    if( rc!=SQLITE_OK ){
      goto failed;
    }
    if( bHotJournal ){
      if( pPager->readOnly ){
        rc = SQLITE_READONLY_ROLLBACK;







|
>







5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
      goto failed;
    }

    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->eLock<=SHARED_LOCK ){
      rc = hasHotJournal(pPager, &bHotJournal, &eServer);
      assert( bHotJournal==0 || eServer==0 );
    }
    if( rc!=SQLITE_OK ){
      goto failed;
    }
    if( bHotJournal ){
      if( pPager->readOnly ){
        rc = SQLITE_READONLY_ROLLBACK;
5395
5396
5397
5398
5399
5400
5401

5402
5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419


5420


5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436

5437
5438
5439
5440
5441
5442
5443

      IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
      rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
      if( rc!=SQLITE_OK ){
        if( rc!=SQLITE_IOERR_SHORT_READ ){
          goto failed;
        }

        memset(dbFileVers, 0, sizeof(dbFileVers));
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);

        /* Unmap the database file. It is possible that external processes
        ** may have truncated the database file and then extended it back
        ** to its original size while this process was not holding a lock.
        ** In this case there may exist a Pager.pMap mapping that appears
        ** to be the right size but is not actually valid. Avoid this
        ** possibility by unmapping the db here. */
        if( USEFETCH(pPager) ){
          sqlite3OsUnfetch(pPager->fd, 0, 0);
        }
      }
    }



    rc = pagerServerConnect(pPager);



    /* 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.
    */
    if( rc==SQLITE_OK ){
      rc = pagerOpenWalIfPresent(pPager);
    }
#ifndef SQLITE_OMIT_WAL
    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, bReadonly);
    if( rc==SQLITE_OK ){
      rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
    }
  }
#endif







>


















>
>
|
>
>
















>







5403
5404
5405
5406
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
5422
5423
5424
5425
5426
5427
5428
5429
5430
5431
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
5448
5449
5450
5451
5452
5453
5454
5455
5456
5457

      IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
      rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
      if( rc!=SQLITE_OK ){
        if( rc!=SQLITE_IOERR_SHORT_READ ){
          goto failed;
        }
        rc = SQLITE_OK;
        memset(dbFileVers, 0, sizeof(dbFileVers));
      }

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);

        /* Unmap the database file. It is possible that external processes
        ** may have truncated the database file and then extended it back
        ** to its original size while this process was not holding a lock.
        ** In this case there may exist a Pager.pMap mapping that appears
        ** to be the right size but is not actually valid. Avoid this
        ** possibility by unmapping the db here. */
        if( USEFETCH(pPager) ){
          sqlite3OsUnfetch(pPager->fd, 0, 0);
        }
      }
    }

#ifdef SQLITE_SERVER_EDITION
    if( eServer ){
      rc = pagerServerConnect(pPager);
    }
#endif

    /* 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.
    */
    if( rc==SQLITE_OK ){
      rc = pagerOpenWalIfPresent(pPager);
    }
#ifndef SQLITE_OMIT_WAL
    assert( pPager->pWal==0 || rc==SQLITE_OK );
#endif
  }

#ifdef SQLITE_SERVER_EDITION
  if( pagerIsServer(pPager) ){
    assert( rc==SQLITE_OK );
    assert( sqlite3PagerRefcount(pPager)==0 );
    assert( pagerUseWal(pPager)==0 );
    pager_reset(pPager);
    rc = sqlite3ServerBegin(pPager->pServer, bReadonly);
    if( rc==SQLITE_OK ){
      rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0);
    }
  }
#endif
7730
7731
7732
7733
7734
7735
7736

7737
7738
7739
7740
7741
#endif

#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerIsServer(Pager *pPager){
  return pagerIsServer(pPager);
}
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){

  return sqlite3ServerLock(pPager->pServer, pgno, bWrite, 0);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */







>





7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
#endif

#ifdef SQLITE_SERVER_EDITION
int sqlite3PagerIsServer(Pager *pPager){
  return pagerIsServer(pPager);
}
int sqlite3PagerPagelock(Pager *pPager, Pgno pgno, int bWrite){
  if( pagerIsServer(pPager)==0 ) return SQLITE_OK;
  return sqlite3ServerLock(pPager->pServer, pgno, bWrite, 0);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */
Changes to src/server.c.
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  }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 ){







|







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  }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-journal", 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 ){
Changes to src/sqlite.h.in.
1041
1042
1043
1044
1045
1046
1047

1048
1049
1050
1051
1052
1053
1054
#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









>







1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
#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
#define SQLITE_FCNTL_SERVER_MODE            32

/* 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 src/test_config.c.
721
722
723
724
725
726
727






728
729
730
731
732
733
734
#endif

#ifdef SQLITE_ENABLE_URI_00_ERROR
  Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY);
#endif







#define LINKVAR(x) { \
    static const int cv_ ## x = SQLITE_ ## x; \
    Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
                TCL_LINK_INT | TCL_LINK_READ_ONLY); }

  LINKVAR( MAX_LENGTH );







>
>
>
>
>
>







721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
#endif

#ifdef SQLITE_ENABLE_URI_00_ERROR
  Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "uri_00_error", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_SERVER_EDITION
  Tcl_SetVar2(interp, "sqlite_options", "server", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "server", "0", TCL_GLOBAL_ONLY);
#endif

#define LINKVAR(x) { \
    static const int cv_ ## x = SQLITE_ ## x; \
    Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
                TCL_LINK_INT | TCL_LINK_READ_ONLY); }

  LINKVAR( MAX_LENGTH );
Changes to test/permutations.test.
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  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 server4.test
]

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







|







274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
  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 server4.test server5.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.
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
#


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 {
  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-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
} {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}
db2 close

#-------------------------------------------------------------------------
#
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








>
>
>








|





|
|



|
|

|




|














|
|



|
|



|
|



|
|



















|
|




|
|




















|





|
|



|
|
<


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
#


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

source $testdir/server_common.tcl
return_if_no_server

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

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

do_test 1.1 {
  lsort [glob -nocomplain test.db-journal/*]
} {test.db-journal/0-journal}

do_test 1.2 {
  db close
  lsort [glob -nocomplain test.db-journal/*]
} {}

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

server_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 -nocomplain test.db-journal/*]
} {test.db-journal/0-journal test.db-journal/1-journal}

do_test 1.6 {
  db close
  lsort [glob -nocomplain test.db-journal/*]
} {test.db-journal/0-journal test.db-journal/1-journal}

do_test 1.7 {
  db2 close
  lsort [glob -nocomplain test.db-journal/*]
} {}

#-------------------------------------------------------------------------
#
server_reset_db
server_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 -nocomplain test.db-journal/*]
} {test.db-journal/0-journal test.db-journal/1-journal}
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 -nocomplain test.db-journal/*]
} {test.db-journal/0-journal test.db-journal/1-journal}

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}
db2 close

#-------------------------------------------------------------------------
#
server_reset_db
do_execsql_test 3.0 {
  CREATE TABLE t1(a, b);
}

do_test 3.1 {
  lsort [glob -nocomplain test.db-journal/*]
} {test.db-journal/0-journal}

do_test 3.2 {
  db close
  lsort [glob -nocomplain test.db-journal/*]
} {}

finish_test

Changes to test/server3.test.
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


set testdir [file dirname $argv0]
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 {
      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








>
|

>
|
<

|
|
|
|

|
|
|
|
|
|
|

>
|
|
|
|
|
|
>
>
>



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


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

source $testdir/server_common.tcl
return_if_no_server

server_reset_db
server_sqlite3 db2 test.db


do_test 1.1 {
  db eval { CREATE TABLE t1(a, b) }
  db2 eval { CREATE TABLE t2(a, b) }
} {}

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

do_test 1.3 { 
  list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}
do_test 1.4 { 
  list [catch { db2 eval { SELECT * FROM t1 } } msg] $msg
} {1 {database is locked}}

do_test 1.4 { 
  db2 eval { SELECT * FROM t2 }
} {1 2}

finish_test

Changes to test/server4.test.
15
16
17
18
19
20
21
22
23




24
25
26
27
28
29
30
31
# transaction.
#


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





sqlite3 db2 test.db

do_execsql_test 1.0 {
  CREATE TABLE t1(x);
  INSERT INTO t1 VALUES(1);
  CREATE TABLE t2(x);
  INSERT INTO t2 VALUES(1);
  BEGIN;







|

>
>
>
>
|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# transaction.
#


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

source $testdir/server_common.tcl
return_if_no_server

server_reset_db
server_sqlite3 db2 test.db

do_execsql_test 1.0 {
  CREATE TABLE t1(x);
  INSERT INTO t1 VALUES(1);
  CREATE TABLE t2(x);
  INSERT INTO t2 VALUES(1);
  BEGIN;
52
53
54
55
56
57
58
59
60
61
62
  SELECT * FROM t2;
} {1}

do_execsql_test -db db2 1.4 {
  ROLLBACK;
  SELECT * FROM t1;
} {1 2 3}


finish_test








<



56
57
58
59
60
61
62

63
64
65
  SELECT * FROM t2;
} {1}

do_execsql_test -db db2 1.4 {
  ROLLBACK;
  SELECT * FROM t1;
} {1 2 3}


finish_test

Changes to test/server5.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
# 2017 July 09
#
# 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.
# Specifically, that "BEGIN READONLY" starts a read-only MVCC
# transaction.
#


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

do_execsql_test 1.0 {
  PRAGMA freelist_format;
} {1}

explain_i {
  PRAGMA freelist_format = 2;
}

do_execsql_test 1.1 {
  PRAGMA freelist_format = 2;
} {2}

do_execsql_test 1.2 {
  PRAGMA freelist_format;
} {2}













|
<











<
<
<
<







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
# 2017 July 09
#
# 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.
# Specifically, that "PRAGMA freelist_format" works.

#


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

do_execsql_test 1.0 {
  PRAGMA freelist_format;
} {1}





do_execsql_test 1.1 {
  PRAGMA freelist_format = 2;
} {2}

do_execsql_test 1.2 {
  PRAGMA freelist_format;
} {2}
52
53
54
55
56
57
58
59
60
61
62
63
do_execsql_test 1.6 {
  CREATE TABLE t2(y);
}

do_execsql_test 1.6 {
  PRAGMA freelist_format = 1;
} {2}



finish_test








<
<



47
48
49
50
51
52
53


54
55
56
do_execsql_test 1.6 {
  CREATE TABLE t2(y);
}

do_execsql_test 1.6 {
  PRAGMA freelist_format = 1;
} {2}



finish_test

Added test/server_common.tcl.






































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# 2017 July 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.
#
#***********************************************************************
#
#

ifcapable !server {
  proc return_if_no_server {} {
    finish_test
    return -code return
  }
  return
} else {
  proc return_if_no_server {} {}
}

proc server_sqlite3 {cmd file} {
  sqlite3 $cmd $file -vfs unix-excl
}

proc server_reset_db {} {
  catch {db close}
  forcedelete test.db test.db-journal test.db-wal
  file mkdir test.db-journal
  server_sqlite3 db test.db 
}


Deleted test/serverwal.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# 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 serverwal

# Check files are created and deleted as expected.
#
do_execsql_test 1.0 {
  PRAGMA journal_mode = wal;
} {wal}
do_execsql_test 1.1 {
  CREATE TABLE t1(a, b);
}
do_execsql_test 1.2 {
  SELECT * FROM t1;
} {}
do_test 1.3 {
  lsort [glob test.db*]
} {test.db test.db-hma test.db-shm test.db-wal}
do_test 1.4 {
  db close
  glob test.db*
} {test.db}

#-------------------------------------------------------------------------
# Two concurrent transactions.
#
do_test 2.0 {
  sqlite3 db  test.db
  sqlite3 db2 test.db
  db eval {
    CREATE TABLE t2(a, b);
  }
} {}
do_test 2.1 {
  execsql {
    BEGIN;
      INSERT INTO t1 VALUES(1, 2);
  } db
  execsql {
    BEGIN;
      INSERT INTO t2 VALUES(1, 2);
  } db2
} {}
do_test 2.2 {
  execsql COMMIT db
  execsql COMMIT db2
} {}
db close
db2 close

#-------------------------------------------------------------------------
# That the wal file can be wrapped around.
#
reset_db
do_execsql_test 3.0 {
  PRAGMA journal_mode = wal;
  CREATE TABLE ttt(a, b);
  INSERT INTO ttt VALUES(1, 2);
  INSERT INTO ttt VALUES(3, 4);
  INSERT INTO ttt VALUES(5, 6);
  INSERT INTO ttt VALUES(7, 8);
  INSERT INTO ttt VALUES(9, 10);
} {wal}

do_test 3.1 {
  set N [file size test.db-wal]
  execsql {
    PRAGMA wal_checkpoint = restart;
    INSERT INTO ttt VALUES(11, 12);
    INSERT INTO ttt VALUES(13, 14);
  }
  expr {$N == [file size test.db-wal]}
} {1}

#-------------------------------------------------------------------------
# That ROLLBACK appears to work.
#
reset_db
do_execsql_test 4.0 {
  PRAGMA cache_size = 10;
  CREATE TABLE ttt(a, b);
  CREATE INDEX yyy ON ttt(b, a);
  PRAGMA journal_mode = wal;
  WITH s(i) AS (
    SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<100
  )
  INSERT INTO ttt SELECT randomblob(100), randomblob(100) FROM s;
} {wal}

do_execsql_test 4.1 {
  PRAGMA integrity_check;
  BEGIN;
    UPDATE ttt SET b=a;
  ROLLBACK;
  PRAGMA integrity_check;
} {ok ok}

reset_db
do_execsql_test 5.1 {
  CREATE TABLE xyz(a);
  PRAGMA journal_mode = wal;
  INSERT INTO xyz VALUES(1);
  INSERT INTO xyz VALUES(2);
  INSERT INTO xyz VALUES(3);
} {wal}

breakpoint

do_test 5.2 {
  sqlite3 db2 test.db
  execsql { SELECT * FROM xyz } db2
} {1 2 3}

do_execsql_test 5.3 {
  PRAGMA wal_checkpoint = restart 
} {0 0 0}

do_test 5.4 {
  execsql { SELECT * FROM xyz } db2
} {1 2 3}

finish_test

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