/ Check-in [64ecf7c7]
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 | SQL 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
Context
2017-04-27
13:05
If possible, delete the journal file when a database connection is closed. check-in: d5b5326d user: dan tags: server-edition
2017-04-26
20:45
Experimental implementation of pessimistic page-level locking based on rollback mode. check-in: 64ecf7c7 user: dan tags: server-edition
17:21
Add new test file cachespill.test. check-in: 2d0b6431 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to main.mk.

    65     65            icu.o insert.o json1.o legacy.o loadext.o \
    66     66            main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \
    67     67            memjournal.o \
    68     68            mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \
    69     69            notify.o opcodes.o os.o os_unix.o os_win.o \
    70     70            pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \
    71     71            random.o resolve.o rowset.o rtree.o select.o sqlite3rbu.o status.o \
           72  +	 server.o \
    72     73            table.o threads.o tokenize.o treeview.o trigger.o \
    73     74            update.o userauth.o util.o vacuum.o \
    74     75            vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbesort.o \
    75     76   	 vdbetrace.o wal.o walker.o where.o wherecode.o whereexpr.o \
    76     77            utf.o vtab.o
    77     78   
    78     79   LIBOBJ += sqlite3session.o
................................................................................
   140    141     $(TOP)/src/pragma.h \
   141    142     $(TOP)/src/prepare.c \
   142    143     $(TOP)/src/printf.c \
   143    144     $(TOP)/src/random.c \
   144    145     $(TOP)/src/resolve.c \
   145    146     $(TOP)/src/rowset.c \
   146    147     $(TOP)/src/select.c \
          148  +  $(TOP)/src/server.c \
          149  +  $(TOP)/src/server.h \
   147    150     $(TOP)/src/status.c \
   148    151     $(TOP)/src/shell.c \
   149    152     $(TOP)/src/sqlite.h.in \
   150    153     $(TOP)/src/sqlite3ext.h \
   151    154     $(TOP)/src/sqliteInt.h \
   152    155     $(TOP)/src/sqliteLimit.h \
   153    156     $(TOP)/src/table.c \

Changes to src/pager.c.

   702    702   #endif
   703    703     char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
   704    704     PCache *pPCache;            /* Pointer to page cache object */
   705    705   #ifndef SQLITE_OMIT_WAL
   706    706     Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
   707    707     char *zWal;                 /* File name for write-ahead log */
   708    708   #endif
          709  +#ifdef SQLITE_SERVER_EDITION
          710  +  Server *pServer;
          711  +#endif
   709    712   };
   710    713   
   711    714   /*
   712    715   ** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
   713    716   ** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS 
   714    717   ** or CACHE_WRITE to sqlite3_db_status().
   715    718   */
................................................................................
   831    834   #else
   832    835   # define pagerUseWal(x) 0
   833    836   # define pagerRollbackWal(x) 0
   834    837   # define pagerWalFrames(v,w,x,y) 0
   835    838   # define pagerOpenWalIfPresent(z) SQLITE_OK
   836    839   # define pagerBeginReadTransaction(z) SQLITE_OK
   837    840   #endif
          841  +
          842  +#ifdef SQLITE_SERVER_EDITION
          843  +# define pagerIsServer(x) ((x)->pServer!=0)
          844  +#else
          845  +# define pagerIsServer(x) 0
          846  +#endif
   838    847   
   839    848   #ifndef NDEBUG 
   840    849   /*
   841    850   ** Usage:
   842    851   **
   843    852   **   assert( assert_pager_state(pPager) );
   844    853   **
................................................................................
  1128   1137   */
  1129   1138   static int pagerUnlockDb(Pager *pPager, int eLock){
  1130   1139     int rc = SQLITE_OK;
  1131   1140   
  1132   1141     assert( !pPager->exclusiveMode || pPager->eLock==eLock );
  1133   1142     assert( eLock==NO_LOCK || eLock==SHARED_LOCK );
  1134   1143     assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
         1144  +  assert( eLock!=NO_LOCK || pagerIsServer(pPager)==0 );
  1135   1145     if( isOpen(pPager->fd) ){
  1136   1146       assert( pPager->eLock>=eLock );
  1137   1147       rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
  1138   1148       if( pPager->eLock!=UNKNOWN_LOCK ){
  1139   1149         pPager->eLock = (u8)eLock;
  1140   1150       }
  1141   1151       IOTRACE(("UNLOCK %p %d\n", pPager, eLock))
................................................................................
  1803   1813          || pPager->eState==PAGER_ERROR 
  1804   1814     );
  1805   1815   
  1806   1816     sqlite3BitvecDestroy(pPager->pInJournal);
  1807   1817     pPager->pInJournal = 0;
  1808   1818     releaseAllSavepoints(pPager);
  1809   1819   
         1820  +#ifdef SQLITE_SERVER_EDITION
         1821  +  if( pagerIsServer(pPager) ){
         1822  +    sqlite3ServerEnd(pPager->pServer);
         1823  +    pPager->eState = PAGER_OPEN;
         1824  +  }else 
         1825  +#endif
  1810   1826     if( pagerUseWal(pPager) ){
  1811   1827       assert( !isOpen(pPager->jfd) );
  1812   1828       sqlite3WalEndReadTransaction(pPager->pWal);
  1813   1829       pPager->eState = PAGER_OPEN;
  1814   1830     }else if( !pPager->exclusiveMode ){
  1815   1831       int rc;                       /* Error code returned by pagerUnlockDb() */
  1816   1832       int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
................................................................................
  2101   2117     }
  2102   2118   
  2103   2119     if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
  2104   2120       rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
  2105   2121       if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
  2106   2122     }
  2107   2123   
         2124  +#ifdef SQLITE_SERVER_EDITION
         2125  +  if( pagerIsServer(pPager) ){
         2126  +    rc2 = sqlite3ServerReleaseWriteLocks(pPager->pServer);
         2127  +  }else
         2128  +#endif
  2108   2129     if( !pPager->exclusiveMode 
  2109   2130      && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
  2110   2131     ){
  2111   2132       rc2 = pagerUnlockDb(pPager, SHARED_LOCK);
  2112   2133       pPager->changeCountDone = 0;
  2113   2134     }
  2114   2135     pPager->eState = PAGER_READER;
................................................................................
  4074   4095     pPager->exclusiveMode = 0;
  4075   4096   #ifndef SQLITE_OMIT_WAL
  4076   4097     assert( db || pPager->pWal==0 );
  4077   4098     sqlite3WalClose(pPager->pWal, db, pPager->ckptSyncFlags, pPager->pageSize,
  4078   4099         (db && (db->flags & SQLITE_NoCkptOnClose) ? 0 : pTmp)
  4079   4100     );
  4080   4101     pPager->pWal = 0;
         4102  +#endif
         4103  +#ifdef SQLITE_SERVER_EDITION
         4104  +  if( pPager->pServer ){
         4105  +    sqlite3ServerDisconnect(pPager->pServer, pPager->fd);
         4106  +    pPager->pServer = 0;
         4107  +    sqlite3_free(pPager->zJournal);
         4108  +  }
  4081   4109   #endif
  4082   4110     pager_reset(pPager);
  4083   4111     if( MEMDB ){
  4084   4112       pager_unlock(pPager);
  4085   4113     }else{
  4086   4114       /* If it is open, sync the journal file before calling UnlockAndRollback.
  4087   4115       ** If this is not done, then an unsynced portion of the open journal 
................................................................................
  5046   5074           }
  5047   5075         }
  5048   5076       }
  5049   5077     }
  5050   5078   
  5051   5079     return rc;
  5052   5080   }
         5081  +
         5082  +#ifdef SQLITE_SERVER_EDITION
         5083  +static int pagerServerConnect(Pager *pPager){
         5084  +  int rc = SQLITE_OK;
         5085  +  if( pPager->tempFile==0 ){
         5086  +    int iClient = 0;
         5087  +    pPager->noLock = 1;
         5088  +    pPager->journalMode = PAGER_JOURNALMODE_PERSIST;
         5089  +    rc = sqlite3ServerConnect(pPager, &pPager->pServer, &iClient);
         5090  +    if( rc==SQLITE_OK ){
         5091  +      pPager->zJournal = sqlite3_mprintf(
         5092  +          "%s-journal%d", pPager->zFilename, iClient
         5093  +      );
         5094  +      if( pPager->zJournal==0 ){
         5095  +        rc = SQLITE_NOMEM_BKPT;
         5096  +      }
         5097  +    }
         5098  +  }
         5099  +  return rc;
         5100  +}
         5101  +
         5102  +int sqlite3PagerRollbackJournal(Pager *pPager, int iClient){
         5103  +  int rc;
         5104  +  char *zJrnl = sqlite3_mprintf("%s-journal%d", pPager->zFilename, iClient);
         5105  +
         5106  +  if( zJrnl ){
         5107  +    int bExists = 0;
         5108  +    sqlite3_file *jfd = 0;
         5109  +    sqlite3_vfs * const pVfs = pPager->pVfs;
         5110  +
         5111  +    rc = sqlite3OsAccess(pVfs, zJrnl, SQLITE_ACCESS_EXISTS, &bExists);
         5112  +    if( rc==SQLITE_OK && bExists ){
         5113  +      int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_JOURNAL;
         5114  +      rc = sqlite3OsOpenMalloc(pVfs, zJrnl, &jfd, flags, &flags);
         5115  +    }
         5116  +    assert( rc==SQLITE_OK || jfd==0 );
         5117  +    if( jfd ){
         5118  +      sqlite3_file *saved_jfd = pPager->jfd;
         5119  +      u8 saved_eState = pPager->eState;
         5120  +      u8 saved_eLock = pPager->eLock;
         5121  +      i64 saved_journalOff = pPager->journalOff;
         5122  +      i64 saved_journalHdr = pPager->journalHdr;
         5123  +
         5124  +      pPager->eLock = EXCLUSIVE_LOCK;
         5125  +      pPager->eState = PAGER_WRITER_DBMOD;
         5126  +      pPager->jfd = jfd;
         5127  +      rc = pagerSyncHotJournal(pPager);
         5128  +      if( rc==SQLITE_OK ) rc = pager_playback(pPager, 1);
         5129  +
         5130  +      pPager->jfd = saved_jfd;
         5131  +      pPager->eState = saved_eState;
         5132  +      pPager->eLock = saved_eLock;
         5133  +      pPager->journalOff = saved_journalOff;
         5134  +      pPager->journalHdr = saved_journalHdr;
         5135  +
         5136  +      sqlite3OsCloseFree(jfd);
         5137  +      if( rc==SQLITE_OK ){
         5138  +        rc = sqlite3OsDelete(pVfs, zJrnl, 0);
         5139  +      }
         5140  +    }
         5141  +    sqlite3_free(zJrnl);
         5142  +  }else{
         5143  +    rc = SQLITE_NOMEM_BKPT;
         5144  +  }
         5145  +
         5146  +  return rc;
         5147  +}
         5148  +
         5149  +#else
         5150  +# define pagerServerConnect(pPager) SQLITE_OK
         5151  +#endif
         5152  +
  5053   5153   
  5054   5154   /*
  5055   5155   ** This function is called to obtain a shared lock on the database file.
  5056   5156   ** It is illegal to call sqlite3PagerGet() until after this function
  5057   5157   ** has been successfully called. If a shared-lock is already held when
  5058   5158   ** this function is called, it is a no-op.
  5059   5159   **
................................................................................
  5086   5186     ** be OPEN or READER. READER is only possible if the pager is or was in 
  5087   5187     ** exclusive access mode.  */
  5088   5188     assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
  5089   5189     assert( assert_pager_state(pPager) );
  5090   5190     assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
  5091   5191     assert( pPager->errCode==SQLITE_OK );
  5092   5192   
  5093         -  if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
         5193  +  if( !pagerUseWal(pPager) 
         5194  +   && !pagerIsServer(pPager) 
         5195  +   && pPager->eState==PAGER_OPEN ){
  5094   5196       int bHotJournal = 1;          /* True if there exists a hot journal-file */
  5095   5197   
  5096   5198       assert( !MEMDB );
  5097   5199       assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
  5098   5200   
  5099   5201       rc = pager_wait_on_lock(pPager, SHARED_LOCK);
  5100   5202       if( rc!=SQLITE_OK ){
................................................................................
  5265   5367       /* If there is a WAL file in the file-system, open this database in WAL
  5266   5368       ** mode. Otherwise, the following function call is a no-op.
  5267   5369       */
  5268   5370       rc = pagerOpenWalIfPresent(pPager);
  5269   5371   #ifndef SQLITE_OMIT_WAL
  5270   5372       assert( pPager->pWal==0 || rc==SQLITE_OK );
  5271   5373   #endif
         5374  +
         5375  +    if( rc==SQLITE_OK && pagerUseWal(pPager)==0 ){
         5376  +      rc = pagerServerConnect(pPager);
         5377  +    }
  5272   5378     }
  5273   5379   
         5380  +#ifdef SQLITE_SERVER_EDITION
         5381  +  if( pagerIsServer(pPager) ){
         5382  +    assert( rc==SQLITE_OK );
         5383  +    pager_reset(pPager);
         5384  +    rc = sqlite3ServerBegin(pPager->pServer);
         5385  +  }else
         5386  +#endif
  5274   5387     if( pagerUseWal(pPager) ){
  5275   5388       assert( rc==SQLITE_OK );
  5276   5389       rc = pagerBeginReadTransaction(pPager);
  5277   5390     }
  5278   5391   
  5279   5392     if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
  5280   5393       rc = pagerPagecount(pPager, &pPager->dbSize);
................................................................................
  5560   5673   */
  5561   5674   int sqlite3PagerGet(
  5562   5675     Pager *pPager,      /* The pager open on the database file */
  5563   5676     Pgno pgno,          /* Page number to fetch */
  5564   5677     DbPage **ppPage,    /* Write a pointer to the page here */
  5565   5678     int flags           /* PAGER_GET_XXX flags */
  5566   5679   ){
         5680  +#ifdef SQLITE_SERVER_EDITION
         5681  +  if( pagerIsServer(pPager) ){
         5682  +    int rc = sqlite3ServerLock(pPager->pServer, pgno, 0);
         5683  +    if( rc!=SQLITE_OK ) return rc;
         5684  +  }
         5685  +#endif
  5567   5686     return pPager->xGet(pPager, pgno, ppPage, flags);
  5568   5687   }
  5569   5688   
  5570   5689   /*
  5571   5690   ** Acquire a page if it is already in the in-memory cache.  Do
  5572   5691   ** not read the page from disk.  Return a pointer to the page,
  5573   5692   ** or 0 if the page is not in cache. 
................................................................................
  5861   5980          || pPager->eState==PAGER_WRITER_CACHEMOD
  5862   5981          || pPager->eState==PAGER_WRITER_DBMOD
  5863   5982     );
  5864   5983     assert( assert_pager_state(pPager) );
  5865   5984     assert( pPager->errCode==0 );
  5866   5985     assert( pPager->readOnly==0 );
  5867   5986     CHECK_PAGE(pPg);
         5987  +
         5988  +#ifdef SQLITE_SERVER_EDITION
         5989  +  if( pagerIsServer(pPager) ){
         5990  +    rc = sqlite3ServerLock(pPager->pServer, pPg->pgno, 1);
         5991  +    if( rc!=SQLITE_OK ) return rc;
         5992  +  }
         5993  +#endif
  5868   5994   
  5869   5995     /* The journal file needs to be opened. Higher level routines have already
  5870   5996     ** obtained the necessary locks to begin the write-transaction, but the
  5871   5997     ** rollback journal might not yet be open. Open it now if this is the case.
  5872   5998     **
  5873   5999     ** This is done before calling sqlite3PcacheMakeDirty() on the page. 
  5874   6000     ** Otherwise, if it were done after calling sqlite3PcacheMakeDirty(), then
................................................................................
  6140   6266   # define DIRECT_MODE 0
  6141   6267     assert( isDirectMode==0 );
  6142   6268     UNUSED_PARAMETER(isDirectMode);
  6143   6269   #else
  6144   6270   # define DIRECT_MODE isDirectMode
  6145   6271   #endif
  6146   6272   
  6147         -  if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
         6273  +  if( 0==pagerIsServer(pPager) 
         6274  +   && !pPager->changeCountDone 
         6275  +   && ALWAYS(pPager->dbSize>0) 
         6276  +  ){
  6148   6277       PgHdr *pPgHdr;                /* Reference to page 1 */
  6149   6278   
  6150   6279       assert( !pPager->tempFile && isOpen(pPager->fd) );
  6151   6280   
  6152   6281       /* Open page 1 of the file for writing. */
  6153   6282       rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
  6154   6283       assert( pPgHdr==0 || rc==SQLITE_OK );

Changes to src/pager.h.

   231    231     void sqlite3PagerRefdump(Pager*);
   232    232     void disable_simulated_io_errors(void);
   233    233     void enable_simulated_io_errors(void);
   234    234   #else
   235    235   # define disable_simulated_io_errors()
   236    236   # define enable_simulated_io_errors()
   237    237   #endif
          238  +
          239  +#ifdef SQLITE_SERVER_EDITION
          240  +  int sqlite3PagerRollbackJournal(Pager*, int);
          241  +#endif
   238    242   
   239    243   #endif /* SQLITE_PAGER_H */

Added src/server.c.

            1  +/*
            2  +** 2017 April 24
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +*/
           13  +
           14  +#include "sqliteInt.h"
           15  +
           16  +/*
           17  +** HMA file layout:
           18  +**
           19  +**      4 bytes - DMS slot. All connections read-lock this slot.
           20  +**
           21  +**   16*4 bytes - locking slots. Connections hold a read-lock on a locking slot
           22  +**                when they are connected, a write lock when they have an open
           23  +**                transaction.
           24  +**
           25  +**    N*4 bytes - Page locking slots. N is HMA_PAGELOCK_SLOTS.
           26  +**
           27  +** Page lock slot format:
           28  +**
           29  +**    Least significant HMA_CLIENT_SLOTS used for read-locks. If bit 0 is set,
           30  +**    client 0 holds a read-lock.
           31  +**
           32  +**    If (v) is the value of the locking slot and (v>>HMA_CLIENT_SLOTS) is
           33  +**    not zero, then the write-lock holder is client ((v>>HMA_CLIENT_SLOTS)-1).
           34  +**
           35  +*/
           36  +
           37  +#ifdef SQLITE_SERVER_EDITION
           38  +
           39  +#define HMA_CLIENT_SLOTS   16
           40  +#define HMA_PAGELOCK_SLOTS (256*1024)
           41  +
           42  +#define HMA_FILE_SIZE (4 + 4*HMA_CLIENT_SLOTS + 4*HMA_PAGELOCK_SLOTS)
           43  +
           44  +#include "unistd.h"
           45  +#include "fcntl.h"
           46  +#include "sys/mman.h"
           47  +#include "sys/types.h"
           48  +#include "sys/stat.h"
           49  +
           50  +typedef struct ServerHMA ServerHMA;
           51  +
           52  +struct ServerGlobal {
           53  +  sqlite3_mutex *mutex;
           54  +  ServerHMA *pHma;
           55  +};
           56  +static struct ServerGlobal g_server;
           57  +
           58  +/*
           59  +** There is one instance of the following structure for each distinct 
           60  +** HMA file opened by clients within this process. 
           61  +*/
           62  +struct ServerHMA {
           63  +  char *zName;                         /* hma file path */
           64  +  int fd;                              /* Fd open on hma file */
           65  +  int nClient;                         /* Current number of clients */
           66  +  Server *aClient[HMA_CLIENT_SLOTS];   /* Local (this process) clients */
           67  +  u32 *aMap;                           /* MMapped hma file */
           68  +  ServerHMA *pNext;                    /* Next HMA in this process */
           69  +
           70  +  dev_t st_dev;
           71  +  ino_t st_ino;
           72  +};
           73  +
           74  +struct Server {
           75  +  ServerHMA *pHma;                /* Hma file object */
           76  +  int iClient;                    /* Client id */
           77  +  Pager *pPager;                  /* Associated pager object */
           78  +
           79  +  int nAlloc;                     /* Allocated size of aLock[] array */
           80  +  int nLock;                      /* Number of entries in aLock[] */
           81  +  u32 *aLock;                     /* Mapped lock file */
           82  +};
           83  +
           84  +#define SERVER_WRITE_LOCK 3
           85  +#define SERVER_READ_LOCK  2
           86  +#define SERVER_NO_LOCK    1
           87  +
           88  +static int posixLock(int fd, int iSlot, int eLock, int bBlock){
           89  +  int res;
           90  +  struct flock l;
           91  +  short aType[4] = {0, F_UNLCK, F_RDLCK, F_WRLCK};
           92  +  assert( eLock==SERVER_WRITE_LOCK 
           93  +       || eLock==SERVER_READ_LOCK 
           94  +       || eLock==SERVER_NO_LOCK 
           95  +  );
           96  +  memset(&l, 0, sizeof(l));
           97  +  l.l_type = aType[eLock];
           98  +  l.l_whence = SEEK_SET;
           99  +  l.l_start = iSlot*sizeof(u32);
          100  +  l.l_len = 1;
          101  +
          102  +  res = fcntl(fd, (bBlock ? F_SETLKW : F_SETLK), &l);
          103  +  return (res==0 ? SQLITE_OK : SQLITE_BUSY);
          104  +}
          105  +
          106  +static int serverMapFile(ServerHMA *p){
          107  +  assert( p->aMap==0 );
          108  +  p->aMap = mmap(0, HMA_FILE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd, 0);
          109  +  if( p->aMap==0 ){
          110  +    return SQLITE_ERROR;
          111  +  }
          112  +  return SQLITE_OK;
          113  +}
          114  +
          115  +
          116  +static void serverDecrHmaRefcount(ServerHMA *pHma){
          117  +  if( pHma ){
          118  +    pHma->nClient--;
          119  +    if( pHma->nClient<=0 ){
          120  +      ServerHMA **pp;
          121  +      if( pHma->aMap ) munmap(pHma->aMap, HMA_FILE_SIZE);
          122  +      if( pHma->fd>=0 ) close(pHma->fd);
          123  +      for(pp=&g_server.pHma; *pp!=pHma; pp=&(*pp)->pNext);
          124  +      *pp = pHma->pNext;
          125  +      sqlite3_free(pHma);
          126  +    }
          127  +  }
          128  +}
          129  +
          130  +
          131  +static int serverOpenHma(Pager *pPager, const char *zPath, ServerHMA **ppHma){
          132  +  struct stat sStat;              /* Structure populated by stat() */
          133  +  int res;                        /* result of stat() */
          134  +  int rc = SQLITE_OK;             /* Return code */
          135  +  ServerHMA *pHma = 0;
          136  +
          137  +  assert( sqlite3_mutex_held(g_server.mutex) );
          138  +
          139  +  res = stat(zPath, &sStat);
          140  +  if( res!=0 ){
          141  +    sqlite3_log(SQLITE_CANTOPEN, "Failed to stat(%s)", zPath);
          142  +    rc = SQLITE_ERROR;
          143  +  }else{
          144  +    for(pHma=g_server.pHma; pHma; pHma=pHma->pNext){
          145  +      if( sStat.st_dev==pHma->st_dev && sStat.st_ino==pHma->st_ino ) break;
          146  +    }
          147  +    if( pHma==0 ){
          148  +      int nPath = strlen(zPath);
          149  +      int nByte = sizeof(ServerHMA) + nPath+1 + 4;
          150  +
          151  +      pHma = (ServerHMA*)sqlite3_malloc(nByte);
          152  +      if( pHma==0 ){
          153  +        rc = SQLITE_NOMEM;
          154  +      }else{
          155  +        int i;
          156  +        memset(pHma, 0, nByte);
          157  +        pHma->zName = (char*)&pHma[1];
          158  +        pHma->nClient = 1;
          159  +        pHma->st_dev = sStat.st_dev;
          160  +        pHma->st_ino = sStat.st_ino;
          161  +        pHma->pNext = g_server.pHma;
          162  +        g_server.pHma = pHma;
          163  +
          164  +        memcpy(pHma->zName, zPath, nPath);
          165  +        memcpy(&pHma->zName[nPath], "-hma", 5);
          166  +
          167  +        pHma->fd = open(pHma->zName, O_RDWR|O_CREAT, 0644);
          168  +        if( pHma->fd<0 ){
          169  +          sqlite3_log(SQLITE_CANTOPEN, "Failed to open(%s)", pHma->zName);
          170  +          rc = SQLITE_ERROR;
          171  +        }
          172  +
          173  +        if( rc==SQLITE_OK ){
          174  +          /* Write-lock the DMS slot. If successful, initialize the hma file. */
          175  +          rc = posixLock(pHma->fd, 0, SERVER_WRITE_LOCK, 0);
          176  +          if( rc==SQLITE_OK ){
          177  +            res = ftruncate(pHma->fd, HMA_FILE_SIZE);
          178  +            if( res!=0 ){
          179  +              sqlite3_log(SQLITE_CANTOPEN, 
          180  +                  "Failed to ftruncate(%s)", pHma->zName
          181  +              );
          182  +              rc = SQLITE_ERROR;
          183  +            }
          184  +            if( rc==SQLITE_OK ){
          185  +              rc = serverMapFile(pHma);
          186  +            }
          187  +            if( rc==SQLITE_OK ){
          188  +              memset(pHma->aMap, 0, HMA_FILE_SIZE);
          189  +            }else{
          190  +              rc = SQLITE_ERROR;
          191  +            }
          192  +          }else{
          193  +            rc = serverMapFile(pHma);
          194  +          }
          195  +          for(i=0; rc==SQLITE_OK && i<HMA_CLIENT_SLOTS; i++){
          196  +            rc = sqlite3PagerRollbackJournal(pPager, i);
          197  +          }
          198  +          if( rc==SQLITE_OK ){
          199  +            rc = posixLock(pHma->fd, 0, SERVER_READ_LOCK, 1);
          200  +          }
          201  +        }
          202  +
          203  +        if( rc!=SQLITE_OK ){
          204  +          serverDecrHmaRefcount(pHma);
          205  +          pHma = 0;
          206  +        }
          207  +      }
          208  +    }else{
          209  +      pHma->nClient++;
          210  +    }
          211  +  }
          212  +
          213  +  *ppHma = pHma;
          214  +  return rc;
          215  +}
          216  +
          217  +static u32 *serverPageLockSlot(Server *p, Pgno pgno){
          218  +  int iSlot = pgno % HMA_PAGELOCK_SLOTS;
          219  +  return &p->pHma->aMap[1 + HMA_CLIENT_SLOTS + iSlot];
          220  +}
          221  +static u32 *serverClientSlot(Server *p, int iClient){
          222  +  return &p->pHma->aMap[1 + iClient];
          223  +}
          224  +
          225  +/*
          226  +** Close the "connection" and *-hma file. This deletes the object passed
          227  +** as the first argument.
          228  +*/
          229  +void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){
          230  +  if( p->pHma ){
          231  +    ServerHMA *pHma = p->pHma;
          232  +    sqlite3_mutex_enter(g_server.mutex);
          233  +    if( p->iClient>=0 ){
          234  +      u32 *pSlot = serverClientSlot(p, p->iClient);
          235  +      *pSlot = 0;
          236  +      assert( pHma->aClient[p->iClient]==p );
          237  +      pHma->aClient[p->iClient] = 0;
          238  +      posixLock(pHma->fd, p->iClient+1, SERVER_NO_LOCK, 0);
          239  +    }
          240  +    if( dbfd 
          241  +     && pHma->nClient==1 
          242  +     && SQLITE_OK==sqlite3OsLock(dbfd, SQLITE_LOCK_EXCLUSIVE)
          243  +    ){
          244  +      unlink(pHma->zName);
          245  +    }
          246  +    serverDecrHmaRefcount(pHma);
          247  +    sqlite3_mutex_leave(g_server.mutex);
          248  +  }
          249  +  sqlite3_free(p->aLock);
          250  +  sqlite3_free(p);
          251  +}
          252  +
          253  +static int serverRollbackClient(Server *p, int iBlock){
          254  +  int rc;
          255  +
          256  +  sqlite3_log(SQLITE_NOTICE, "Rolling back failed client %d", iBlock);
          257  +
          258  +  /* Roll back any journal file for client iBlock. */
          259  +  rc = sqlite3PagerRollbackJournal(p->pPager, iBlock);
          260  +
          261  +  /* Clear any locks held by client iBlock from the HMA file.  */
          262  +  if( rc==SQLITE_OK ){
          263  +    int i;
          264  +    for(i=0; i<HMA_PAGELOCK_SLOTS; i++){
          265  +      u32 *pSlot = serverPageLockSlot(p, (Pgno)i);
          266  +      u32 v = *pSlot;
          267  +      while( 1 ){
          268  +        u32 n = v & ~(1 << iBlock);
          269  +        if( (v>>HMA_CLIENT_SLOTS)==iBlock+1 ){
          270  +          n = n & ((1<<HMA_CLIENT_SLOTS)-1);
          271  +        }
          272  +        if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
          273  +        v = *pSlot;
          274  +      }
          275  +    }
          276  +  }
          277  +
          278  +  return rc;
          279  +}
          280  +
          281  +
          282  +/*
          283  +** Open the *-hma file and "connect" to the system.
          284  +*/
          285  +int sqlite3ServerConnect(
          286  +  Pager *pPager, 
          287  +  Server **ppOut, 
          288  +  int *piClient
          289  +){
          290  +  const char *zPath = sqlite3PagerFilename(pPager, 0);
          291  +  int rc = SQLITE_OK;
          292  +  Server *p;
          293  +
          294  +  p = (Server*)sqlite3_malloc(sizeof(Server));
          295  +  if( p==0 ){
          296  +    rc = SQLITE_NOMEM;
          297  +  }else{
          298  +    memset(p, 0, sizeof(Server));
          299  +    p->iClient = -1;
          300  +    p->pPager = pPager;
          301  +
          302  +    sqlite3_mutex_enter(g_server.mutex);
          303  +    rc = serverOpenHma(pPager, zPath, &p->pHma);
          304  +
          305  +    /* File is now mapped. Find a free client slot. */
          306  +    if( rc==SQLITE_OK ){
          307  +      int i;
          308  +      Server **aClient = p->pHma->aClient;
          309  +      int fd = p->pHma->fd;
          310  +      for(i=0; i<HMA_CLIENT_SLOTS; i++){
          311  +        if( aClient[i]==0 ){
          312  +          int res = posixLock(fd, i+1, SERVER_WRITE_LOCK, 0);
          313  +          if( res==SQLITE_OK ){
          314  +            u32 *pSlot = serverClientSlot(p, i);
          315  +            if( *pSlot ){
          316  +              rc = serverRollbackClient(p, i);
          317  +            }
          318  +            posixLock(fd, i+1, (!rc ? SERVER_READ_LOCK : SERVER_NO_LOCK), 0);
          319  +            break;
          320  +          }
          321  +        }
          322  +      }
          323  +
          324  +      if( rc==SQLITE_OK ){
          325  +        if( i>HMA_CLIENT_SLOTS ){
          326  +          rc = SQLITE_BUSY;
          327  +        }else{
          328  +          u32 *pSlot = serverClientSlot(p, i);
          329  +          *piClient = p->iClient = i;
          330  +          aClient[i] = p;
          331  +          *pSlot = 1;
          332  +        }
          333  +      }
          334  +    }
          335  +
          336  +    sqlite3_mutex_leave(g_server.mutex);
          337  +  }
          338  +
          339  +  if( rc!=SQLITE_OK ){
          340  +    sqlite3ServerDisconnect(p, 0);
          341  +    p = 0;
          342  +  }
          343  +  *ppOut = p;
          344  +  return rc;
          345  +}
          346  +
          347  +static int serverOvercomeLock(Server *p, int bWrite, u32 v, int *pbRetry){
          348  +  int rc = SQLITE_OK;
          349  +  int bLocal = 0;
          350  +  int iBlock = ((int)(v>>HMA_CLIENT_SLOTS))-1;
          351  +
          352  +  if( iBlock<0 ){
          353  +    for(iBlock=0; iBlock<HMA_CLIENT_SLOTS; iBlock++){
          354  +      if( iBlock!=p->iClient && (v & (1<<iBlock)) ) break;
          355  +    }
          356  +  }
          357  +  assert( iBlock<HMA_CLIENT_SLOTS );
          358  +
          359  +  sqlite3_mutex_enter(g_server.mutex);
          360  +  if( p->pHma->aClient[iBlock] ){
          361  +    bLocal = 1;
          362  +  }else{
          363  +    rc = posixLock(p->pHma->fd, iBlock+1, SERVER_WRITE_LOCK, 0);
          364  +  }
          365  +
          366  +  if( bLocal==0 && rc==SQLITE_OK ){
          367  +    rc = serverRollbackClient(p, iBlock);
          368  +
          369  +    /* Release the lock on slot iBlock */
          370  +    posixLock(p->pHma->fd, iBlock+1, SERVER_NO_LOCK, 0);
          371  +    if( rc==SQLITE_OK ){
          372  +      *pbRetry = 1;
          373  +    }
          374  +  }else{
          375  +    assert( rc==SQLITE_OK || rc==SQLITE_BUSY );
          376  +    rc = SQLITE_OK;
          377  +  }
          378  +  sqlite3_mutex_leave(g_server.mutex);
          379  +
          380  +  return rc;
          381  +}
          382  +
          383  +/*
          384  +** Begin a transaction.
          385  +*/
          386  +int sqlite3ServerBegin(Server *p){
          387  +  return posixLock(p->pHma->fd, p->iClient+1, SERVER_WRITE_LOCK, 0);
          388  +}
          389  +
          390  +/*
          391  +** End a transaction (and release all locks).
          392  +*/
          393  +int sqlite3ServerEnd(Server *p){
          394  +  int i;
          395  +  for(i=0; i<p->nLock; i++){
          396  +    u32 *pSlot = serverPageLockSlot(p, p->aLock[i]);
          397  +    while( 1 ){
          398  +      u32 v = *pSlot;
          399  +      u32 n = v;
          400  +      if( (v>>HMA_CLIENT_SLOTS)==p->iClient+1 ){
          401  +        n = n & ((1 << HMA_CLIENT_SLOTS)-1);
          402  +      }
          403  +      n = n & ~(1 << p->iClient);
          404  +      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
          405  +    }
          406  +  }
          407  +  p->nLock = 0;
          408  +  return posixLock(p->pHma->fd, p->iClient+1, SERVER_READ_LOCK, 0);
          409  +}
          410  +
          411  +/*
          412  +** Release all write-locks.
          413  +*/
          414  +int sqlite3ServerReleaseWriteLocks(Server *p){
          415  +  int rc = SQLITE_OK;
          416  +  return rc;
          417  +}
          418  +
          419  +/*
          420  +** Lock page pgno for reading (bWrite==0) or writing (bWrite==1).
          421  +*/
          422  +int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite){
          423  +  int rc = SQLITE_OK;
          424  +
          425  +  /* Grow the aLock[] array, if required */
          426  +  if( p->nLock==p->nAlloc ){
          427  +    int nNew = p->nAlloc ? p->nAlloc*2 : 128;
          428  +    u32 *aNew;
          429  +    aNew = (u32*)sqlite3_realloc(p->aLock, sizeof(u32)*nNew);
          430  +    if( aNew==0 ){
          431  +      rc = SQLITE_NOMEM_BKPT;
          432  +    }else{
          433  +      p->aLock = aNew;
          434  +      p->nAlloc = nNew;
          435  +    }
          436  +  }
          437  +  if( rc==SQLITE_OK ){
          438  +    u32 *pSlot = serverPageLockSlot(p, pgno);
          439  +    u32 v = *pSlot;
          440  +
          441  +    /* Check if the required lock is already held. If so, exit this function
          442  +    ** early. Otherwise, add an entry to the aLock[] array to record the fact
          443  +    ** that the lock may need to be released.  */
          444  +    if( bWrite ){
          445  +      int iLock = ((int)(v>>HMA_CLIENT_SLOTS)) - 1;
          446  +      if( iLock==p->iClient ) goto server_lock_out;
          447  +      if( iLock<0 ){
          448  +        p->aLock[p->nLock++] = pgno;
          449  +      }
          450  +    }else{
          451  +      if( v & (1<<p->iClient) ) goto server_lock_out;
          452  +      p->aLock[p->nLock++] = pgno;
          453  +    }
          454  +
          455  +    while( 1 ){
          456  +      u32 n;
          457  +
          458  +      while( (bWrite && (v & ~(1 << p->iClient))) || (v >> HMA_CLIENT_SLOTS) ){
          459  +        int bRetry = 0;
          460  +        rc = serverOvercomeLock(p, bWrite, v, &bRetry);
          461  +        if( rc!=SQLITE_OK ) goto server_lock_out;
          462  +        if( bRetry==0 ){
          463  +          /* There is a conflicting lock. Cannot obtain this lock. */
          464  +          sqlite3_log(SQLITE_BUSY_DEADLOCK, "Conflict at page %d", (int)pgno);
          465  +          rc = SQLITE_BUSY_DEADLOCK;
          466  +          goto server_lock_out;
          467  +        }
          468  +        v = *pSlot;
          469  +      }
          470  +
          471  +      if( bWrite ){
          472  +        n = v | ((p->iClient+1) << HMA_CLIENT_SLOTS);
          473  +      }else{
          474  +        n = v | (1 << p->iClient);
          475  +      }
          476  +      if( __sync_val_compare_and_swap(pSlot, v, n)==v ) break;
          477  +      v = *pSlot;
          478  +    }
          479  +  }
          480  +
          481  +server_lock_out:
          482  +  return rc;
          483  +}
          484  +
          485  +#endif /* ifdef SQLITE_SERVER_EDITION */

Changes to src/sqlite.h.in.

   493    493   #define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
   494    494   #define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))
   495    495   #define SQLITE_IOERR_VNODE             (SQLITE_IOERR | (27<<8))
   496    496   #define SQLITE_IOERR_AUTH              (SQLITE_IOERR | (28<<8))
   497    497   #define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
   498    498   #define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
   499    499   #define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
          500  +#define SQLITE_BUSY_DEADLOCK           (SQLITE_BUSY   |  (3<<8))
   500    501   #define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
   501    502   #define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
   502    503   #define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
   503    504   #define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
   504    505   #define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
   505    506   #define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
   506    507   #define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))

Changes to src/sqliteInt.h.

  1097   1097   */
  1098   1098   #include "btree.h"
  1099   1099   #include "vdbe.h"
  1100   1100   #include "pager.h"
  1101   1101   #include "pcache.h"
  1102   1102   #include "os.h"
  1103   1103   #include "mutex.h"
         1104  +#include "server.h"
  1104   1105   
  1105   1106   /* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
  1106   1107   ** synchronous setting to EXTRA.  It is no longer supported.
  1107   1108   */
  1108   1109   #ifdef SQLITE_EXTRA_DURABLE
  1109   1110   # warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE
  1110   1111   # define SQLITE_DEFAULT_SYNCHRONOUS 3

Changes to src/vdbeaux.c.

  2635   2635   
  2636   2636       /* Lock all btrees used by the statement */
  2637   2637       sqlite3VdbeEnter(p);
  2638   2638   
  2639   2639       /* Check for one of the special errors */
  2640   2640       mrc = p->rc & 0xff;
  2641   2641       isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
  2642         -                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
         2642  +                     || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL
         2643  +                     || p->rc==SQLITE_BUSY_DEADLOCK;
  2643   2644       if( isSpecialError ){
  2644         -      /* If the query was read-only and the error code is SQLITE_INTERRUPT, 
  2645         -      ** no rollback is necessary. Otherwise, at least a savepoint 
  2646         -      ** transaction must be rolled back to restore the database to a 
  2647         -      ** consistent state.
         2645  +      /* If the query was read-only and the error code is SQLITE_INTERRUPT
         2646  +      ** or SQLITE_BUSY_SERVER, no rollback is necessary. Otherwise, at 
         2647  +      ** least a savepoint transaction must be rolled back to restore the
         2648  +      ** database to a consistent state.
  2648   2649         **
  2649   2650         ** Even if the statement is read-only, it is important to perform
  2650   2651         ** a statement or transaction rollback operation. If the error 
  2651   2652         ** occurred while writing to the journal, sub-journal or database
  2652   2653         ** file as part of an effort to free up cache space (see function
  2653   2654         ** pagerStress() in pager.c), the rollback is required to restore 
  2654   2655         ** the pager to a consistent state.
  2655   2656         */
  2656         -      if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){
         2657  +      if( !p->readOnly || (mrc!=SQLITE_INTERRUPT && mrc!=SQLITE_BUSY) ){
  2657   2658           if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){
  2658   2659             eStatementOp = SAVEPOINT_ROLLBACK;
  2659   2660           }else{
  2660   2661             /* We are forced to roll back the active transaction. Before doing
  2661   2662             ** so, abort any other statements this handle currently has active.
  2662   2663             */
  2663   2664             sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);

Changes to test/permutations.test.

   268    268     fts3corrupt3.test
   269    269     fts3misc.test
   270    270   }
   271    271   
   272    272   test_suite "fts5" -prefix "" -description {
   273    273     All FTS5 tests.
   274    274   } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test]
          275  +
          276  +test_suite "server" -prefix "" -description {
          277  +  All server-edition tests.
          278  +} -files [
          279  +  test_set \
          280  +      [glob -nocomplain $::testdir/server*.test] \
          281  +      -exclude *server1.test
          282  +]
   275    283   
   276    284   test_suite "fts5-light" -prefix "" -description {
   277    285     All FTS5 tests.
   278    286   } -files [
   279    287     test_set \
   280    288         [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \
   281    289         -exclude *corrupt* *fault* *big* *fts5aj*

Added test/server2.test.

            1  +# 2017 April 25
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is testing the server mode of SQLite.
           13  +#
           14  +
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +set testprefix server2
           19  +
           20  +#-------------------------------------------------------------------------
           21  +# Check that the *-hma file is deleted correctly.
           22  +#
           23  +do_execsql_test 1.0 {
           24  +  CREATE TABLE t1(a, b);
           25  +} {}
           26  +do_test 1.1 {
           27  +  file exists test.db-hma
           28  +} {1}
           29  +do_test 1.2 {
           30  +  db close
           31  +  file exists test.db-hma
           32  +} {0}
           33  +do_test 1.3 {
           34  +  sqlite3 db test.db
           35  +  db eval { CREATE TABLE t2(a, b) }
           36  +  sqlite3 db2 test.db
           37  +  db2 eval { CREATE TABLE t3(a, b) }
           38  +  file exists test.db-hma
           39  +} {1}
           40  +do_test 1.4 {
           41  +  db2 close
           42  +  file exists test.db-hma
           43  +} {1}
           44  +do_test 1.5 {
           45  +  db close
           46  +  file exists test.db-hma
           47  +} {0}
           48  +
           49  +
           50  +#-------------------------------------------------------------------------
           51  +#
           52  +reset_db
           53  +sqlite3 db2 test.db
           54  +
           55  +do_execsql_test 2.0 {
           56  +  CREATE TABLE t1(a, b);
           57  +  CREATE TABLE t2(c, d);
           58  +}
           59  +
           60  +# Two concurrent transactions committed.
           61  +#
           62  +do_test 2.1 {
           63  +  db eval {
           64  +    BEGIN;
           65  +      INSERT INTO t1 VALUES(1, 2);
           66  +  }
           67  +  db2 eval {
           68  +    BEGIN;
           69  +      INSERT INTO t2 VALUES(3, 4);
           70  +  }
           71  +} {}
           72  +do_test 2.2 {
           73  +  lsort [glob test.db*]
           74  +} {test.db test.db-hma test.db-journal0 test.db-journal1}
           75  +do_test 2.3.1 { db eval COMMIT  } {}
           76  +do_test 2.3.2 { db2 eval COMMIT } {}
           77  +do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4}
           78  +do_test 2.5 {
           79  +  lsort [glob test.db*]
           80  +} {test.db test.db-hma test.db-journal0 test.db-journal1}
           81  +
           82  +do_test 2.6 {
           83  +  execsql {BEGIN}
           84  +  execsql {INSERT INTO t1 VALUES(5, 6)}
           85  +
           86  +  execsql {BEGIN} db2
           87  +  catchsql {INSERT INTO t1 VALUES(7, 8)} db2
           88  +} {1 {database is locked}}
           89  +do_test 2.7 {
           90  +  # Transaction is automatically rolled back in this case.
           91  +  sqlite3_get_autocommit db2
           92  +} {1}
           93  +do_test 2.8 {
           94  +  execsql COMMIT
           95  +  execsql { SELECT * FROM t1 } db2
           96  +} {1 2 5 6}
           97  +
           98  +
           99  +
          100  +finish_test
          101  +

Added test/server3.test.

            1  +# 2017 April 25
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.  The
           12  +# focus of this script is testing the server mode of SQLite.
           13  +#
           14  +
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +source $testdir/lock_common.tcl
           19  +set testprefix server3
           20  +
           21  +db close
           22  +
           23  +do_multiclient_test tn {
           24  +  do_test $tn.1 {
           25  +    sql1 { CREATE TABLE t1(a, b) }
           26  +    sql2 { CREATE TABLE t2(a, b) }
           27  +  } {}
           28  +
           29  +  do_test $tn.2 {
           30  +    sql1 {
           31  +      INSERT INTO t2 VALUES(1, 2);
           32  +      BEGIN;
           33  +        INSERT INTO t1 VALUES(1, 2);
           34  +    }
           35  +  } {}
           36  +
           37  +  do_test $tn.3 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
           38  +  do_test $tn.4 { csql2 { SELECT * FROM t1 } } {1 {database is locked}}
           39  +  do_test $tn.5 {  sql2 { SELECT * FROM t2 } } {1 2}
           40  +
           41  +
           42  +}
           43  +
           44  +finish_test
           45  +

Added test/servercrash.test.

            1  +# 2017 April 27
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +
           13  +
           14  +set testdir [file dirname $argv0]
           15  +source $testdir/tester.tcl
           16  +set testprefix servercrash
           17  +
           18  +ifcapable !crashtest {
           19  +  finish_test
           20  +  return
           21  +}
           22  +do_not_use_codec
           23  +
           24  +do_execsql_test 1.0 {
           25  +  PRAGMA page_siBlockze = 4096;
           26  +  PRAGMA auto_vacuum = OFF;
           27  +  CREATE TABLE t1(a, b);
           28  +  CREATE TABLE t2(c, d);
           29  +
           30  +  INSERT INTO t1 VALUES(1, 2), (3, 4);
           31  +  INSERT INTO t2 VALUES(1, 2), (3, 4);
           32  +}
           33  +
           34  +for {set i 0} {$i < 10} {incr i} {
           35  +  do_test 1.$i.1 {
           36  +    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
           37  +  } {1 {child process exited abnormally}}
           38  +
           39  +  do_execsql_test 1.$i.2 {
           40  +    SELECT * FROM t1
           41  +  } {1 2 3 4}
           42  +}
           43  +
           44  +for {set i 0} {$i < 10} {incr i} {
           45  +  do_test 2.$i.1 {
           46  +    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
           47  +  } {1 {child process exited abnormally}}
           48  +
           49  +  do_test 2.$i.2 {
           50  +    sqlite3 dbX test.db
           51  +    execsql { SELECT * FROM t1 } dbX
           52  +  } {1 2 3 4}
           53  +  dbX close
           54  +}
           55  +
           56  +db close
           57  +for {set i 0} {$i < 10} {incr i} {
           58  +  do_test 3.$i.1 {
           59  +    crashsql -delay 1 -file test.db { INSERT INTO t1 VALUES(5, 6) }
           60  +  } {1 {child process exited abnormally}}
           61  +
           62  +  sqlite3 db test.db
           63  +  do_execsql_test 3.$i.2 { SELECT * FROM t1 } {1 2 3 4}
           64  +  db close
           65  +}
           66  +
           67  +finish_test
           68  +

Changes to test/tester.tcl.

   582    582   # Create a test database
   583    583   #
   584    584   proc reset_db {} {
   585    585     catch {db close}
   586    586     forcedelete test.db
   587    587     forcedelete test.db-journal
   588    588     forcedelete test.db-wal
          589  +  for {set i 0} {$i < 16} {incr i} {
          590  +    forcedelete test.db-journal$i
          591  +  }
          592  +
   589    593     sqlite3 db ./test.db
   590    594     set ::DB [sqlite3_connection_pointer db]
   591    595     if {[info exists ::SETUP_SQL]} {
   592    596       db eval $::SETUP_SQL
   593    597     }
   594    598   }
   595    599   reset_db

Changes to tool/mksqlite3c.tcl.

   110    110      os_win.h
   111    111      os.h
   112    112      pager.h
   113    113      parse.h
   114    114      pcache.h
   115    115      pragma.h
   116    116      rtree.h
          117  +   server.h
   117    118      sqlite3session.h
   118    119      sqlite3.h
   119    120      sqlite3ext.h
   120    121      sqlite3rbu.h
   121    122      sqliteicu.h
   122    123      sqliteInt.h
   123    124      sqliteLimit.h
................................................................................
   315    316   
   316    317      bitvec.c
   317    318      pcache.c
   318    319      pcache1.c
   319    320      rowset.c
   320    321      pager.c
   321    322      wal.c
          323  +   server.c
   322    324   
   323    325      btmutex.c
   324    326      btree.c
   325    327      backup.c
   326    328   
   327    329      vdbemem.c
   328    330      vdbeaux.c