From: dan Date: Wed, 16 Aug 2017 16:52:14 +0000 (+0000) Subject: Enhance this branch to support page-level-locking (without MVCC) for X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ba5f0e7681b932fc4a56713939a41c3181b3188;p=thirdparty%2Fsqlite.git Enhance this branch to support page-level-locking (without MVCC) for multi-process deployments. FossilOrigin-Name: 04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876 --- diff --git a/manifest b/manifest index 4e44e1a82d..98bbf43966 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\scode\sfor\sPLL\ssupport\sin\swal\smode\sfrom\sthis\sbranch. -D 2017-08-14T07:16:18.805 +C Enhance\sthis\sbranch\sto\ssupport\spage-level-locking\s(without\sMVCC)\sfor\nmulti-process\sdeployments. +D 2017-08-16T16:52:14.156 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016 @@ -439,10 +439,10 @@ F src/os.c add02933b1dce7a39a005b00a2f5364b763e9a24 F src/os.h 8e976e59eb4ca1c0fca6d35ee803e38951cb0343 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 -F src/os_unix.c 24e4b7f6f89b742357068959255c4530014254579c09c1b56beff1014eb0c0c1 +F src/os_unix.c 6947c0fccc1b68404def7161553881b2efab6fcac4a58bf9f4ce36a4ad27d325 F src/os_win.c 964165b66cde03abc72fe948198b01be608436894732eadb94c8720d2467f223 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 0cfac2557de08ac12ec8d2edd3c4cb69f96ad49b923f1390619c310aa684366e +F src/pager.c e53f35d61f266c47cc3883e34c7d01eaca38a71451dd72efb3cf21e043b471a7 F src/pager.h 316dac0671fd7555af9e73d4357febd5f2d3ce6a185ffd8d77b7fc0423ac8b1a F src/parse.y 58a2de13e855aece3d7709440e6e86849f4cde97f5227c6a25e6bba2fc5e2976 F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870 @@ -456,11 +456,11 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c ea8921065512650a9df3f5bf9a9b531c6ef4fb193c0d57e09d7a479ef9b13992 -F src/server.c d69efa7284074e69d43c38d5d649ef3b20add1db31c9d8e7f01cabd95d0ee72b -F src/server.h d7555e68ee34008ca02705e44f861938a67b85a97ffe64d9326e03bd15dcd06b +F src/server.c e8d1c1a0e39d508c75e11eafa9a20edc85f5b619b27f7218950f3a77431f5a71 +F src/server.h cf1ede28aaa07a30550228582f211327b5ebe5517d2334e35ec09d00fd6d230d F src/shell.c bd6a37cbe8bf64ef6a6a74fdc50f067d3148149b4ce2b4d03154663e66ded55f F src/shell.c.in b5725acacba95ccefa57b6d068f710e29ba8239c3aa704628a1902a1f729c175 -F src/sqlite.h.in 0ebbd0299e4bd03841c0e24ea86c74a42082b20533b86b0f360424a3b3c145a5 +F src/sqlite.h.in 9dacf9a0959630bf5d286c0fbdaf2bb4bf408a9477f6d61db8edab3a859714d7 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a1fd3aa82f967da436164e0728a7d6841651fd0c6e27b9044e0eb9f6c8462e47 F src/sqliteInt.h eaa2c5e52a29da629bd50521a6b67cce7f952128a95ba4f20aac62c78d41b04d @@ -537,7 +537,7 @@ F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 F src/vtab.c f1d5c23132fb0247af3e86146404112283ddedb6c518de0d4edc91cfb36970ef F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344 -F src/wal.h 739d92494eb18b6d8f3e353e66c10eb8f94534bafd336ece9f3f60235317ea08 +F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71 F src/walker.c a7ca64ce08a83a20d32186fbe06bca9234e348cfcf07959ee322fdc3e8a6173a F src/where.c cbe8ddffbcec7ce86f7a800fe8fd10aee412c76c87e0dd3732a1682e68d74cd9 F src/whereInt.h 93bb90b77d39901eda31b44d8e90da1351193ccfe96876f89b58a93a33b84c3d @@ -1171,11 +1171,11 @@ F test/selectE.test a8730ca330fcf40ace158f134f4fe0eb00c7edbf F test/selectF.test 21c94e6438f76537b72532fa9fd4710cdd455fc3 F test/selectG.test e8600e379589e85e9fefd2fe4d44a4cdd63f6982 F test/server1.test 46803bd3fe8b99b30dbc5ff38ffc756f5c13a118 -F test/server2.test 38a33c71963d72aad64c6f4e89ed8f7de129b3b5bf75c5785e363dee8deabd35 -F test/server3.test ba6428594bbe32eafa015782a039dcacbf0f42db967e4093e4d2f6f74178a02d +F test/server2.test e7890fb1eb9a11a0f94cd0892279e0f3cd1ba8c3006fa343637ee9ff3c4689f6 +F test/server3.test c3ae4ca7a6e7df870bfcd2450a9815507eaa80b9cdc44ee6c7975d48311505d4 F test/server4.test 97040670597948a695b1973537d770417589f1998bcbb3959302aaee3c211250 F test/server5.test 2e554001145170094a19731a8ce2981d040cf44c947542b35d130e6e31256fca -F test/server_common.tcl 6e0e0348a87381770b548fcf8faf2d8c7b910120b08a1bfb565546f77293fa81 +F test/server_common.tcl 551923cf8d51fefcdb4444bfd72b88ca5c5228fe1525da5b6528ae4edb7a2f2e F test/servercrash.test 816c132b26af008067cab2913783f67006d4003e3988f3f3ee1075742f6e0a6c F test/session.test 78fa2365e93d3663a6e933f86e7afc395adf18be F test/shared.test 1da9dbad400cee0d93f252ccf76e1ae007a63746 @@ -1657,7 +1657,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P bc2498d60fa22b587ad463ec697abe82d1c87abb7fd0d2cb60cc7316cfd7cec7 -R 97b8be53027d87f1df1370ba323e39a9 +P 8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a +R 0a18526bebbb88ed2fe197ff12475de8 U dan -Z cc211de634879a22eac42d92c4a3bb35 +Z 121a6646ee66bffdec0d5750ccf213ab diff --git a/manifest.uuid b/manifest.uuid index a6a976c08a..521845bf4e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8e1b28ed3e83eba0b2113316a40ed1165e0e051220b68863cb70734c95a82c2a \ No newline at end of file +04e0cb571dbed00e269a890a755e252d7e8204d6d2ed5a7cfdb3d78d990a2876 \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index f7cbd7e299..00873ba121 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3787,38 +3787,247 @@ static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ /* Forward declaration */ static int unixGetTempname(int nBuf, char *zBuf); +#ifdef SQLITE_SERVER_EDITION + /* -** Information and control of an open file handle. +** Structure passed by SQLite through the (void*) argument to various +** fcntl operations. */ -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); - } +struct UnixServerArg { + void *h; /* Handle from SHMOPEN */ + void *p; /* Mapping */ + int i1; /* Integer value 1 */ + int i2; /* Integer value 2 */ +}; +typedef struct UnixServerArg UnixServerArg; + +/* +** Structure used as a server-shm handle. +*/ +struct UnixServerShm { + void *pMap; /* Pointer to mapping */ + int nMap; /* Size of mapping in bytes */ + int fd; /* File descriptor open on *-hma file */ +}; +typedef struct UnixServerShm UnixServerShm; + +/* +** Implementation of SQLITE_FCNTL_FILEID +*/ +static int unixFcntlServerFileid(unixFile *pFile, void *pArg){ + i64 *aId = (i64*)pArg; + aId[0] = (i64)(pFile->pInode->fileId.dev); + aId[1] = (i64)(pFile->pInode->fileId.ino); + return SQLITE_OK; +} + +/* +** Implementation of SQLITE_FCNTL_SERVER_MODE +*/ +static int unixFcntlServerMode(unixFile *pFile, void *pArg){ + int rc = SQLITE_OK; + int eServer = 0; + 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 if( buf.st_mode & S_IFDIR ){ + eServer = (pFile->ctrlFlags & UNIXFILE_EXCL) ? 1 : 2; + } + } + sqlite3_free(zJrnl); + *((int*)pArg) = eServer; + return rc; +} + +/* +** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN. +** +** The (void*) argument passed to this file control should actually be +** a pointer to a UnixServerArg or equivalent structure. Arguments are +** interpreted as follows: +** +** UnixServerArg.h - OUT: New server shm handle. +** UnixServerArg.p - OUT: New server shm mapping. +** UnixServerArg.i1 - Size of requested mapping in bytes. +** UnixServerArg.i2 - OUT: True if journal rollback + SHMOPEN2 are required. +*/ +static int unixFcntlServerShmopen(unixFile *pFd, void *pArg){ + int rc = SQLITE_OK; + UnixServerArg *pSArg = (UnixServerArg*)pArg; + UnixServerShm *p; + char *zHma; + + p = sqlite3_malloc(sizeof(UnixServerShm)); + if( p==0 ) return SQLITE_NOMEM; + memset(p, 0, sizeof(UnixServerShm)); + p->fd = -1; + + zHma = sqlite3_mprintf("%s-journal/hma", pFd->zPath); + if( zHma==0 ){ + rc = SQLITE_NOMEM; + }else{ + p->fd = osOpen(zHma, O_RDWR|O_CREAT, 0644); + p->nMap = pSArg->i1; + + if( p->fd<0 ){ + rc = SQLITE_CANTOPEN; + }else{ + int res = ftruncate(p->fd, p->nMap); + if( res!=0 ){ + rc = SQLITE_IOERR_TRUNCATE; + }else{ + p->pMap = osMmap(0, p->nMap, PROT_READ|PROT_WRITE, MAP_SHARED, p->fd,0); + if( p->pMap==0 ){ + rc = SQLITE_IOERR_MMAP; } - 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; + sqlite3_free(zHma); + } + + if( rc==SQLITE_OK ){ + int res; + struct flock lock; + memset(&lock, 0, sizeof(struct flock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = p->nMap; + lock.l_len = 1; + + res = osFcntl(p->fd, F_SETLK, &lock); + if( res==0 ){ + pSArg->i2 = 1; + memset(p->pMap, 0, p->nMap); + }else{ + pSArg->i2 = 0; + lock.l_type = F_RDLCK; + res = osFcntl(p->fd, F_SETLKW, &lock); + if( res!=0 ){ + rc = SQLITE_IOERR_LOCK; + } } + } + + if( rc!=SQLITE_OK ){ + if( p->pMap ) osMunmap(p->pMap, p->nMap); + if( p->fd>=0 ) close(p->fd); + sqlite3_free(p); + pSArg->h = pSArg->p = 0; + }else{ + pSArg->h = (void*)p; + pSArg->p = (void*)(p->pMap); + } + + return rc; +} + +/* +** Implementation of SQLITE_FCNTL_SERVER_SHMOPEN2. +** +** The (void*) argument passed to this file control should actually be +** a pointer to a UnixServerArg or equivalent structure. Arguments are +** interpreted as follows: +** +** UnixServerArg.h - Server shm handle (from SHMOPEN). +** UnixServerArg.p - unused. +** UnixServerArg.i1 - unused. +** UnixServerArg.i2 - unused. +*/ +static int unixFcntlServerShmopen2(unixFile *pFd, void *pArg){ + UnixServerArg *pSArg = (UnixServerArg*)pArg; + UnixServerShm *p = (UnixServerShm*)pSArg->h; + int res; + struct flock lock; + + memset(&lock, 0, sizeof(struct flock)); + lock.l_type = F_RDLCK; + lock.l_whence = SEEK_SET; + lock.l_start = p->nMap; + lock.l_len = 1; + res = osFcntl(p->fd, F_SETLK, &lock); + + return res ? SQLITE_IOERR_LOCK : SQLITE_OK; +} + +/* +** Implementation of SQLITE_FCNTL_SERVER_SHMCLOSE. +** +** The (void*) argument passed to this file control should actually be +** a pointer to a UnixServerArg or equivalent structure. Arguments are +** interpreted as follows: +** +** UnixServerArg.h - Server shm handle (from SHMOPEN). +** UnixServerArg.p - unused. +** UnixServerArg.i1 - unused. +** UnixServerArg.i2 - unused. +*/ +static int unixFcntlServerShmclose(unixFile *pFd, void *pArg){ + UnixServerArg *pSArg = (UnixServerArg*)pArg; + UnixServerShm *p = (UnixServerShm*)pSArg->h; + + if( p->pMap ) osMunmap(p->pMap, p->nMap); + if( p->fd>=0 ) close(p->fd); + sqlite3_free(p); + + return SQLITE_OK; +} + +/* +** Implementation of SQLITE_FCNTL_SERVER_SHMLOCK. +** +** The (void*) argument passed to this file control should actually be +** a pointer to a UnixServerArg or equivalent structure. Arguments are +** interpreted as follows: +** +** UnixServerArg.h - Server shm handle (from SHMOPEN). +** UnixServerArg.p - unused. +** UnixServerArg.i1 - slot to lock. +** UnixServerArg.i2 - true to take the lock, false to release it. +*/ +static int unixFcntlServerShmlock(unixFile *pFd, void *pArg){ + UnixServerArg *pSArg = (UnixServerArg*)pArg; + UnixServerShm *p = (UnixServerShm*)pSArg->h; + int res; + + struct flock lock; + memset(&lock, 0, sizeof(struct flock)); + lock.l_type = pSArg->i2 ? F_WRLCK : F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = p->nMap + pSArg->i1 + 1; + lock.l_len = 1; + + res = osFcntl(p->fd, F_SETLK, &lock); + + return (res==0 ? SQLITE_OK : SQLITE_BUSY); +} +#endif + +/* +** Information and control of an open file handle. +*/ +static int unixFileControl(sqlite3_file *id, int op, void *pArg){ + unixFile *pFile = (unixFile*)id; + switch( op ){ + +#ifdef SQLITE_SERVER_EDITION + case SQLITE_FCNTL_FILEID: + return unixFcntlServerFileid(pFile, pArg); + case SQLITE_FCNTL_SERVER_MODE: + return unixFcntlServerMode(pFile, pArg); + case SQLITE_FCNTL_SERVER_SHMOPEN: + return unixFcntlServerShmopen(pFile, pArg); + case SQLITE_FCNTL_SERVER_SHMOPEN2: + return unixFcntlServerShmopen2(pFile, pArg); + case SQLITE_FCNTL_SERVER_SHMCLOSE: + return unixFcntlServerShmclose(pFile, pArg); + case SQLITE_FCNTL_SERVER_SHMLOCK: + return unixFcntlServerShmlock(pFile, pArg); +#endif + #if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE) case SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: { int rc = osIoctl(pFile->h, F2FS_IOC_START_ATOMIC_WRITE); diff --git a/src/pager.c b/src/pager.c index c45fe29c74..00bceddcf8 100644 --- a/src/pager.c +++ b/src/pager.c @@ -842,6 +842,7 @@ int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){ #ifdef SQLITE_SERVER_EDITION # define pagerIsServer(x) ((x)->pServer!=0) +# define pagerIsProcessServer(x) sqlite3ServerIsSingleProcess((x)->pServer) #else # define pagerIsServer(x) 0 #endif @@ -1807,6 +1808,9 @@ static int addToSavepointBitvecs(Pager *pPager, Pgno pgno){ } #ifdef SQLITE_SERVER_EDITION +/* +** Free the linked list of ServerPage objects headed at Pager.pServerPage. +*/ static void pagerFreeServerPage(Pager *pPager){ ServerPage *pPg; ServerPage *pNext; @@ -4434,7 +4438,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ assert( isOpen(pPager->fd) || pList->pDirty==0 ); #ifdef SQLITE_SERVER_EDITION - if( pagerIsServer(pPager) ){ + if( pagerIsProcessServer(pPager) ){ rc = sqlite3ServerPreCommit(pPager->pServer, pPager->pServerPage); pPager->pServerPage = 0; if( rc!=SQLITE_OK ) return rc; @@ -5192,12 +5196,12 @@ static int hasHotJournal(Pager *pPager, int *pExists, int *peServer){ } #ifdef SQLITE_SERVER_EDITION -static int pagerServerConnect(Pager *pPager){ +static int pagerServerConnect(Pager *pPager, int eServer){ int rc = SQLITE_OK; if( pPager->tempFile==0 ){ pPager->noLock = 1; pPager->journalMode = PAGER_JOURNALMODE_PERSIST; - rc = sqlite3ServerConnect(pPager, &pPager->pServer); + rc = sqlite3ServerConnect(pPager, eServer, &pPager->pServer); } return rc; } @@ -5453,7 +5457,7 @@ int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){ #ifdef SQLITE_SERVER_EDITION if( eServer ){ - rc = pagerServerConnect(pPager); + rc = pagerServerConnect(pPager, eServer); } #endif @@ -6016,7 +6020,7 @@ static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){ i64 iOff = pPager->journalOff; #ifdef SQLITE_SERVER_EDITION - if( pagerIsServer(pPager) ){ + if( pagerIsProcessServer(pPager) ){ ServerPage *p = sqlite3ServerBuffer(pPager->pServer); if( p==0 ){ int nByte = sizeof(ServerPage) + pPager->pageSize; diff --git a/src/server.c b/src/server.c index 26193640ab..beeffa5e3a 100644 --- a/src/server.c +++ b/src/server.c @@ -36,7 +36,6 @@ #define HMA_SLOT_RLWL_BITS (HMA_SLOT_RL_BITS + HMA_SLOT_WL_BITS) - #define HMA_SLOT_RL_MASK ((1 << HMA_SLOT_RL_BITS)-1) #define HMA_SLOT_WL_MASK (((1 << HMA_SLOT_WL_BITS)-1) << HMA_SLOT_RL_BITS) #define HMA_SLOT_TR_MASK (((1 << HMA_SLOT_TR_BITS)-1) << HMA_SLOT_RLWL_BITS) @@ -48,7 +47,7 @@ /* Maximum concurrent read/write transactions */ #define HMA_MAX_TRANSACTIONID 16 - +/* Number of buckets in hash table used for MVCC in single-process mode */ #define HMA_HASH_SIZE 512 /* @@ -66,18 +65,27 @@ #define slotReaderMask(v) ((v) & HMA_SLOT_RL_MASK) -#include "unistd.h" -#include "fcntl.h" -#include "sys/mman.h" -#include "sys/types.h" -#include "sys/stat.h" -#include "errno.h" + +/* +** Atomic CAS primitive used in multi-process mode. Equivalent to: +** +** int serverCompareAndSwap(u32 *ptr, u32 oldval, u32 newval){ +** if( *ptr==oldval ){ +** *ptr = newval; +** return 1; +** } +** return 0; +** } +*/ +#define serverCompareAndSwap(ptr,oldval,newval) \ + __sync_bool_compare_and_swap(ptr,oldval,newval) + typedef struct ServerDb ServerDb; typedef struct ServerJournal ServerJournal; struct ServerGlobal { - ServerDb *pDb; /* Linked list of all ServerHMA objects */ + ServerDb *pDb; /* Linked list of all ServerDb objects */ }; static struct ServerGlobal g_server; @@ -103,13 +111,14 @@ struct ServerDb { ServerJournal aJrnl[HMA_MAX_TRANSACTIONID]; u8 *aJrnlFdSpace; + void *pServerShm; + int iNextCommit; /* Commit id for next pre-commit call */ Server *pCommit; /* List of connections currently commiting */ Server *pReader; /* Connections in slower-reader transaction */ ServerPage *pPgFirst; /* First (oldest) in list of pages */ ServerPage *pPgLast; /* Last (newest) in list of pages */ - ServerPage *apPg[HMA_HASH_SIZE]; - + ServerPage *apPg[HMA_HASH_SIZE];/* Hash table of "old" page data */ ServerPage *pFree; /* List of free page buffers */ }; @@ -125,10 +134,18 @@ struct Server { int iCommitId; /* Current commit id (or 0) */ int nAlloc; /* Allocated size of aLock[] array */ int nLock; /* Number of entries in aLock[] */ - u32 *aLock; /* Mapped lock file */ + u32 *aLock; /* Array of held locks */ Server *pNext; /* Next in pCommit or pReader list */ }; +struct ServerFcntlArg { + void *h; /* Handle from SHMOPEN */ + void *p; /* Mapping */ + int i1; /* Integer value 1 */ + int i2; /* Integer value 2 */ +}; +typedef struct ServerFcntlArg ServerFcntlArg; + /* ** Possible values for Server.eTrans. */ @@ -153,6 +170,12 @@ static void serverAssertMutexHeld(void){ assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_APP1)) ); } +/* +** Locate the ServerDb object shared by all connections to the db identified +** by aFileId[2], increment its ref count and set pNew->pDb to point to it. +** In this context "locate" may mean to find an existing object or to +** allocate a new one. +*/ static int serverFindDatabase(Server *pNew, i64 *aFileId){ ServerDb *p; int rc = SQLITE_OK; @@ -165,18 +188,11 @@ static int serverFindDatabase(Server *pNew, i64 *aFileId){ 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); + p->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); #if SQLITE_THREADSAFE!=0 - if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT; + if( p->mutex==0 ) rc = SQLITE_NOMEM_BKPT; #endif - } - if( rc==SQLITE_NOMEM ){ - sqlite3_free(p->aSlot); sqlite3_free(p); p = 0; }else{ @@ -202,14 +218,18 @@ static int serverFindDatabase(Server *pNew, i64 *aFileId){ ** Free all resources allocated by serverInitDatabase() associated with the ** object passed as the only argument. */ -static void serverShutdownDatabase(ServerDb *pDb){ +static void serverShutdownDatabase( + ServerDb *pDb, + sqlite3_file *dbfd, + int bDelete +){ int i; for(i=0; iaJrnl[i]; if( pJ->jfd ){ sqlite3OsClose(pJ->jfd); - sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0); + if( bDelete ) sqlite3OsDelete(pDb->pVfs, pJ->zJournal, 0); } sqlite3_free(pJ->zJournal); } @@ -220,7 +240,15 @@ static void serverShutdownDatabase(ServerDb *pDb){ pDb->aJrnlFdSpace = 0; } - sqlite3_free(pDb->aSlot); + if( pDb->pServerShm ){ + ServerFcntlArg arg; + memset(&arg, 0, sizeof(ServerFcntlArg)); + arg.h = pDb->pServerShm; + sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMCLOSE, (void*)&arg); + }else{ + sqlite3_free(pDb->aSlot); + } + pDb->aSlot = 0; pDb->bInit = 0; } @@ -229,24 +257,48 @@ static void serverShutdownDatabase(ServerDb *pDb){ ** is established. It is responsible for rolling back any hot journal ** files found in the file-system. */ -static int serverInitDatabase(Server *pNew){ +static int serverInitDatabase(Server *pNew, int eServer){ int nByte; int rc = SQLITE_OK; ServerDb *pDb = pNew->pDb; sqlite3_vfs *pVfs; + sqlite3_file *dbfd = sqlite3PagerFile(pNew->pPager); const char *zFilename = sqlite3PagerFilename(pNew->pPager, 0); + int bRollback = 0; assert( zFilename ); + assert( eServer==1 || eServer==2 ); + 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{ + if( eServer==2 ){ + ServerFcntlArg arg; + arg.h = 0; + arg.p = 0; + arg.i1 = sizeof(u32)*HMA_PAGELOCK_SLOTS; + arg.i2 = 0; + + rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMOPEN, (void*)&arg); + if( rc==SQLITE_OK ){ + pDb->aSlot = (u32*)arg.p; + pDb->pServerShm = arg.h; + bRollback = arg.i2; + } + }else{ + pDb->aSlot = (u32*)sqlite3MallocZero(sizeof(u32)*HMA_PAGELOCK_SLOTS); + if( pDb->aSlot==0 ) rc = SQLITE_NOMEM_BKPT; + bRollback = 1; + } + } + + if( rc==SQLITE_OK ){ u8 *a = pDb->aJrnlFdSpace; int i; for(i=0; rc==SQLITE_OK && iaJrnl[i]; pJ->jfd = (sqlite3_file*)&a[ROUND8(pVfs->szOsFile)*i]; pJ->zJournal = sqlite3_mprintf("%s-journal/%d-journal", zFilename, i); @@ -255,37 +307,92 @@ static int serverInitDatabase(Server *pNew){ 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( bRollback ){ + int bExist = 0; + rc = sqlite3OsAccess(pVfs, pJ->zJournal, SQLITE_ACCESS_EXISTS, &bExist); + if( rc==SQLITE_OK && bExist ){ + 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->pServerShm && bRollback ){ + ServerFcntlArg arg; + arg.h = pDb->pServerShm; + arg.p = 0; + arg.p = 0; + arg.i2 = 0; + rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMOPEN2, (void*)&arg); + } + if( rc==SQLITE_OK ){ pDb->bInit = 1; }else{ - serverShutdownDatabase(pNew->pDb); + serverShutdownDatabase(pNew->pDb, dbfd, eServer==1); } return rc; } +/* +** Take (bLock==1) or release (bLock==0) a server shmlock on slot iSlot. +** Return SQLITE_OK if successful, or SQLITE_BUSY if the lock cannot be +** obtained. +*/ +static int serverFcntlLock(Server *p, int iSlot, int bLock){ + sqlite3_file *dbfd = sqlite3PagerFile(p->pPager); + int rc; + ServerFcntlArg arg; + arg.h = p->pDb->pServerShm; + arg.p = 0; + arg.i1 = iSlot; + arg.i2 = bLock; + rc = sqlite3OsFileControl(dbfd, SQLITE_FCNTL_SERVER_SHMLOCK, (void*)&arg); + return rc; +} + /* ** Close the connection. */ void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){ ServerDb *pDb = p->pDb; + /* In a multi-process setup, release the lock on the client slot and + ** clear the bit in the ServerDb.transmask bitmask. */ + if( pDb->pServerShm && p->iTransId>=0 ){ + sqlite3_mutex_enter(pDb->mutex); + pDb->transmask &= ~((u32)1 << p->iTransId); + sqlite3_mutex_leave(pDb->mutex); + serverFcntlLock(p, p->iTransId, 0); + } + serverEnterMutex(); pDb->nClient--; if( pDb->nClient==0 ){ + sqlite3_file *dbfd = sqlite3PagerFile(p->pPager); ServerPage *pFree; ServerDb **pp; - serverShutdownDatabase(pDb); + + /* Delete the journal files on shutdown if an EXCLUSIVE lock is already + ** held (single process mode) or can be obtained (multi process mode) + ** on the database file. + ** + ** TODO: Need to account for disk-full errors and the like here. It + ** is not necessarily safe to delete journal files here. */ + int bDelete = 0; + if( pDb->pServerShm ){ + int res; + res = sqlite3OsLock(dbfd, EXCLUSIVE_LOCK); + if( res==SQLITE_OK ) bDelete = 1; + }else{ + bDelete = 1; + } + serverShutdownDatabase(pDb, dbfd, bDelete); + for(pp=&g_server.pDb; *pp!=pDb; pp=&((*pp)->pNext)); *pp = pDb->pNext; sqlite3_mutex_free(pDb->mutex); @@ -305,7 +412,8 @@ void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd){ ** Connect to the system. */ int sqlite3ServerConnect( - Pager *pPager, + Pager *pPager, /* Pager object */ + int eServer, /* 1 -> single process, 2 -> multi process */ Server **ppOut /* OUT: Server handle */ ){ Server *pNew = 0; @@ -324,9 +432,26 @@ int sqlite3ServerConnect( sqlite3_free(pNew); pNew = 0; }else{ + ServerDb *pDb = pNew->pDb; sqlite3_mutex_enter(pNew->pDb->mutex); - if( pNew->pDb->bInit==0 ){ - rc = serverInitDatabase(pNew); + if( pDb->bInit==0 ){ + rc = serverInitDatabase(pNew, eServer); + } + + /* If this is a multi-process connection, need to lock a + ** client locking-slot before continuing. */ + if( rc==SQLITE_OK && pDb->pServerShm ){ + int i; + rc = SQLITE_BUSY; + for(i=0; rc==SQLITE_BUSY && itransmask & ((u32)1 << i)) ){ + rc = serverFcntlLock(pNew, i, 1); + if( rc==SQLITE_OK ){ + pNew->iTransId = i; + pDb->transmask |= ((u32)1 << i); + } + } + } } sqlite3_mutex_leave(pNew->pDb->mutex); } @@ -335,6 +460,11 @@ int sqlite3ServerConnect( } } + if( rc!=SQLITE_OK && pNew ){ + sqlite3ServerDisconnect(pNew, dbfd); + pNew = 0; + } + *ppOut = pNew; return rc; } @@ -346,148 +476,176 @@ int sqlite3ServerBegin(Server *p, int bReadonly){ int rc = SQLITE_OK; if( p->eTrans==SERVER_TRANS_NONE ){ - int id; ServerDb *pDb = p->pDb; u32 t; - assert( p->iTransId<0 ); assert( p->pNext==0 ); - sqlite3_mutex_enter(pDb->mutex); - - if( bReadonly ){ - Server *pIter; - p->iCommitId = pDb->iNextCommit; - for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ - if( pIter->iCommitIdiCommitId ){ - p->iCommitId = pIter->iCommitId; - } - } - p->pNext = pDb->pReader; - pDb->pReader = p; - p->eTrans = SERVER_TRANS_READONLY; + if( pDb->pServerShm ){ + p->eTrans = SERVER_TRANS_READWRITE; }else{ - /* Find a transaction id to use */ - rc = SQLITE_BUSY; - t = pDb->transmask; - for(id=0; idiTransId<0 ); + sqlite3_mutex_enter(pDb->mutex); + if( bReadonly ){ + Server *pIter; + p->iCommitId = pDb->iNextCommit; + for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ + if( pIter->iCommitIdiCommitId ){ + p->iCommitId = pIter->iCommitId; + } + } + p->pNext = pDb->pReader; + pDb->pReader = p; + p->eTrans = SERVER_TRANS_READONLY; + }else{ + int id; + + /* Find a transaction id to use */ + rc = SQLITE_BUSY; + t = pDb->transmask; + for(id=0; idtransmask = t; + p->eTrans = SERVER_TRANS_READWRITE; + if( rc==SQLITE_OK ){ + p->iTransId = id; } } - pDb->transmask = t; - p->eTrans = SERVER_TRANS_READWRITE; + sqlite3_mutex_leave(pDb->mutex); } - sqlite3_mutex_leave(pDb->mutex); - - if( rc==SQLITE_OK && bReadonly==0 ){ - ServerJournal *pJrnl = &pDb->aJrnl[id]; + if( rc==SQLITE_OK && p->eTrans==SERVER_TRANS_READWRITE ){ + ServerJournal *pJrnl = &pDb->aJrnl[p->iTransId]; sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal); - p->iTransId = id; } } return rc; } +static u32 *serverLockingSlot(ServerDb *pDb, u32 pgno){ + return &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; +} + static void serverReleaseLocks(Server *p){ ServerDb *pDb = p->pDb; int i; - assert( sqlite3_mutex_held(pDb->mutex) ); + + assert( pDb->pServerShm || sqlite3_mutex_held(pDb->mutex) ); for(i=0; inLock; i++){ - u32 *pSlot = &pDb->aSlot[p->aLock[i] % HMA_PAGELOCK_SLOTS]; - if( slotGetWriter(*pSlot)==p->iTransId ){ - *pSlot -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID); + while( 1 ){ + u32 *pSlot = serverLockingSlot(pDb, p->aLock[i]); + u32 o = *pSlot; + u32 n = o & ~((u32)1 << p->iTransId); + if( slotGetWriter(n)==p->iTransId ){ + n -= ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID); + } + if( serverCompareAndSwap(pSlot, o, n) ) break; } - *pSlot &= ~((u32)1 << p->iTransId); } p->nLock = 0; } /* -** End a transaction (and release all locks). +** End a transaction (and release all locks). This version runs in +** single process mode only. */ -int sqlite3ServerEnd(Server *p){ - int rc = SQLITE_OK; - if( p->eTrans!=SERVER_TRANS_NONE ){ - Server **pp; - ServerDb *pDb = p->pDb; - ServerPage *pPg = 0; +static void serverEndSingle(Server *p){ + Server **pp; + ServerDb *pDb = p->pDb; + ServerPage *pPg = 0; - sqlite3_mutex_enter(pDb->mutex); + assert( p->eTrans!=SERVER_TRANS_NONE ); + assert( pDb->pServerShm==0 ); - if( p->eTrans==SERVER_TRANS_READONLY ){ - /* Remove the connection from the readers list */ - for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext)); - *pp = p->pNext; - }else{ - serverReleaseLocks(p); + sqlite3_mutex_enter(pDb->mutex); - /* Clear the bit in the transaction mask. */ - pDb->transmask &= ~((u32)1 << p->iTransId); + if( p->eTrans==SERVER_TRANS_READONLY ){ + /* Remove the connection from the readers list */ + for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext)); + *pp = p->pNext; + }else{ + serverReleaseLocks(p); - /* If this connection is in the committers list, remove it. */ - for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){ - if( *pp==p ){ - *pp = p->pNext; - break; - } + /* Clear the bit in the transaction mask. */ + pDb->transmask &= ~((u32)1 << p->iTransId); + + /* If this connection is in the committers list, remove it. */ + for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){ + if( *pp==p ){ + *pp = p->pNext; + break; } } + } - /* See if it is possible to free any ServerPage records. If so, remove - ** them from the linked list and hash table, but do not call sqlite3_free() - ** on them until the mutex has been released. */ - if( pDb->pPgFirst ){ - ServerPage *pLast = 0; - Server *pIter; - int iOldest = 0x7FFFFFFF; - for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){ - iOldest = MIN(iOldest, pIter->iCommitId); - } - for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ - iOldest = MIN(iOldest, pIter->iCommitId); - } + /* See if it is possible to free any ServerPage records. If so, remove + ** them from the linked list and hash table, but do not call sqlite3_free() + ** on them until the mutex has been released. */ + if( pDb->pPgFirst ){ + ServerPage *pLast = 0; + Server *pIter; + int iOldest = 0x7FFFFFFF; + for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){ + iOldest = MIN(iOldest, pIter->iCommitId); + } + for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ + iOldest = MIN(iOldest, pIter->iCommitId); + } - for(pPg=pDb->pPgFirst; pPg && pPg->iCommitIdpNext){ - if( pPg->pHashPrev ){ - pPg->pHashPrev->pHashNext = pPg->pHashNext; - }else{ - int iHash = pPg->pgno % HMA_HASH_SIZE; - assert( pDb->apPg[iHash]==pPg ); - pDb->apPg[iHash] = pPg->pHashNext; - } - if( pPg->pHashNext ){ - pPg->pHashNext->pHashPrev = pPg->pHashPrev; - } - pLast = pPg; + for(pPg=pDb->pPgFirst; pPg && pPg->iCommitIdpNext){ + if( pPg->pHashPrev ){ + pPg->pHashPrev->pHashNext = pPg->pHashNext; + }else{ + int iHash = pPg->pgno % HMA_HASH_SIZE; + assert( pDb->apPg[iHash]==pPg ); + pDb->apPg[iHash] = pPg->pHashNext; } - - if( pLast ){ - assert( pLast->pNext==pPg ); - pLast->pNext = pDb->pFree; - pDb->pFree = pDb->pPgFirst; + if( pPg->pHashNext ){ + pPg->pHashNext->pHashPrev = pPg->pHashPrev; } + pLast = pPg; + } - if( pPg==0 ){ - pDb->pPgFirst = pDb->pPgLast = 0; - }else{ - pDb->pPgFirst = pPg; - } + if( pLast ){ + assert( pLast->pNext==pPg ); + pLast->pNext = pDb->pFree; + pDb->pFree = pDb->pPgFirst; } - sqlite3_mutex_leave(pDb->mutex); + if( pPg==0 ){ + pDb->pPgFirst = pDb->pPgLast = 0; + }else{ + pDb->pPgFirst = pPg; + } + } - p->pNext = 0; - p->eTrans = SERVER_TRANS_NONE; - p->iTransId = -1; - p->iCommitId = 0; + sqlite3_mutex_leave(pDb->mutex); + + p->pNext = 0; + p->eTrans = SERVER_TRANS_NONE; + p->iTransId = -1; + p->iCommitId = 0; +} + +/* +** End a transaction (and release all locks). +*/ +int sqlite3ServerEnd(Server *p){ + if( p->eTrans!=SERVER_TRANS_NONE ){ + if( p->pDb->pServerShm ){ + serverReleaseLocks(p); + }else{ + serverEndSingle(p); + } } - return rc; + return SQLITE_OK; } int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){ @@ -495,6 +653,8 @@ int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){ int rc = SQLITE_OK; ServerPage *pIter; + /* This should never be called in multi-process mode */ + assert( pDb->pServerShm==0 ); if( pPg==0 ) return SQLITE_OK; sqlite3_mutex_enter(pDb->mutex); @@ -569,7 +729,7 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ int rc = SQLITE_OK; assert( p->eTrans==SERVER_TRANS_READWRITE - || p->eTrans==SERVER_TRANS_READONLY + || (p->eTrans==SERVER_TRANS_READONLY && p->pDb->pServerShm==0) ); if( p->eTrans==SERVER_TRANS_READWRITE ){ ServerDb *pDb = p->pDb; @@ -577,6 +737,7 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ int bSkip = 0; u32 *pSlot; + /* Grow the aLock[] array if required */ assert( p->iTransId>=0 ); assert( p->nLock<=p->nAlloc ); if( p->nLock==p->nAlloc ){ @@ -588,37 +749,48 @@ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ p->aLock = aNew; } - sqlite3_mutex_enter(pDb->mutex); + /* Find the locking slot for the page in question */ + pSlot = serverLockingSlot(pDb, pgno); - pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; - assert( slotGetWriter(*pSlot)<0 - || slotReaderMask(*pSlot)==0 - || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot)) - ); - - iWriter = slotGetWriter(*pSlot); - if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<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{ + if( pDb->pServerShm==0 ) sqlite3_mutex_enter(pDb->mutex); + + while( 1 ){ + u32 o = *pSlot; + u32 n = o; + + assert( slotGetWriter(o)<0 + || slotReaderMask(o)==0 + || slotReaderMask(o)==(1 << slotGetWriter(o)) + ); + + iWriter = slotGetWriter(o); + if( iWriter==p->iTransId || (bWrite==0 && (o & (1<iTransId))) ){ + bSkip = 1; + break; + }else if( iWriter>=0 ){ rc = SQLITE_BUSY_DEADLOCK; + }else if( bWrite ){ + if( (slotReaderMask(o) & ~(1 << p->iTransId))==0 ){ + n += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID); + }else{ + rc = SQLITE_BUSY_DEADLOCK; + } + }else{ + n |= (1 << p->iTransId); } - }else{ - *pSlot |= (1 << p->iTransId); - } - assert( slotGetWriter(*pSlot)<0 - || slotReaderMask(*pSlot)==0 - || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot)) - ); + assert( slotGetWriter(n)<0 + || slotReaderMask(n)==0 + || slotReaderMask(n)==(1 << slotGetWriter(n)) + ); + if( rc!=SQLITE_OK || serverCompareAndSwap(pSlot, o, n) ) break; + } - sqlite3_mutex_leave(pDb->mutex); + if( pDb->pServerShm==0 ){ + sqlite3_mutex_leave(pDb->mutex); + } - if( bSkip==0 ){ + if( bSkip==0 && rc==SQLITE_OK ){ p->aLock[p->nLock++] = pgno; } } @@ -643,6 +815,8 @@ void sqlite3ServerReadPage(Server *p, Pgno pgno, u8 **ppData){ ServerPage *pBest = 0; int iHash = pgno % HMA_HASH_SIZE; + /* There are no READONLY transactions in a multi process system */ + assert( pDb->pServerShm==0 ); sqlite3_mutex_enter(pDb->mutex); /* Search the hash table for the oldest version of page pgno with @@ -671,6 +845,7 @@ void sqlite3ServerEndReadPage(Server *p, Pgno pgno){ if( p->eTrans==SERVER_TRANS_READONLY ){ ServerDb *pDb = p->pDb; u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; + assert( pDb->pServerShm==0 ); sqlite3_mutex_enter(pDb->mutex); serverIncrSlowReader(pSlot, -1); assert( slotGetSlowReaders(*pSlot)>=0 ); @@ -681,6 +856,7 @@ void sqlite3ServerEndReadPage(Server *p, Pgno pgno){ ServerPage *sqlite3ServerBuffer(Server *p){ ServerDb *pDb = p->pDb; ServerPage *pRet = 0; + assert( pDb->pServerShm==0 ); sqlite3_mutex_enter(pDb->mutex); if( pDb->pFree ){ pRet = pDb->pFree; @@ -701,4 +877,13 @@ int sqlite3ServerIsReadonly(Server *p){ return (p && p->eTrans==SERVER_TRANS_READONLY); } +/* +** Return true if the argument is non-NULL and connects to a single-process +** server system. Return false if the argument is NULL or the system supports +** multiple processes. +*/ +int sqlite3ServerIsSingleProcess(Server *p){ + return (p && p->pDb->pServerShm==0); +} + #endif /* ifdef SQLITE_SERVER_EDITION */ diff --git a/src/server.h b/src/server.h index 30bdf5dd3a..17fdc8ce81 100644 --- a/src/server.h +++ b/src/server.h @@ -31,7 +31,7 @@ struct ServerPage { ServerPage *pHashPrev; }; -int sqlite3ServerConnect(Pager *pPager, Server **ppOut); +int sqlite3ServerConnect(Pager *pPager, int eServer, Server **ppOut); void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd); int sqlite3ServerBegin(Server *p, int bReadonly); @@ -46,6 +46,8 @@ int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite); ServerPage *sqlite3ServerBuffer(Server*); +int sqlite3ServerIsSingleProcess(Server*); + /* For "BEGIN READONLY" clients. */ int sqlite3ServerIsReadonly(Server*); void sqlite3ServerReadPage(Server*, Pgno, u8**); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index ed01ad51c5..89b41175a7 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -1093,6 +1093,10 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_FILEID 34 #define SQLITE_FCNTL_SERVER_MODE 35 +#define SQLITE_FCNTL_SERVER_SHMOPEN 36 +#define SQLITE_FCNTL_SERVER_SHMOPEN2 37 +#define SQLITE_FCNTL_SERVER_SHMLOCK 38 +#define SQLITE_FCNTL_SERVER_SHMCLOSE 39 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE diff --git a/src/wal.h b/src/wal.h index 4eea6ef65d..4f6d01dad6 100644 --- a/src/wal.h +++ b/src/wal.h @@ -144,9 +144,5 @@ int sqlite3WalFramesize(Wal *pWal); /* Return the sqlite3_file object for the WAL file */ sqlite3_file *sqlite3WalFile(Wal *pWal); -#ifdef SQLITE_SERVER_EDITION -int sqlite3WalServer(Wal *pWal, Server *pServer); -#endif - #endif /* ifndef SQLITE_OMIT_WAL */ #endif /* SQLITE_WAL_H */ diff --git a/test/server2.test b/test/server2.test index a1d7969855..d0888369d1 100644 --- a/test/server2.test +++ b/test/server2.test @@ -19,125 +19,130 @@ 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); -} {} +foreach {tn vfs} {1 unix-excl 2 unix} { + server_set_vfs $vfs -server_sqlite3 db2 test.db -do_test 1.4 { - db eval { - BEGIN; - INSERT INTO t1 VALUES(1, 2); + foreach f [glob -nocomplain test.db*] { + forcedelete $f } - 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); + #------------------------------------------------------------------------- + # Check that the *-journal* files are deleted correctly. + # + server_reset_db + do_execsql_test 1.0 { + CREATE TABLE t1(a, b); + } {} + + do_test $tn.1.1 { + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal} + + do_test $tn.1.2 { + db close + lsort [glob -nocomplain test.db-journal/*-journal] + } {} + + server_sqlite3 db test.db + do_execsql_test $tn.1.3 { + CREATE TABLE t2(a, b); + } {} + + server_sqlite3 db2 test.db + do_test $tn.1.4 { + db eval { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } + db2 eval { + BEGIN; + INSERT INTO t2 VALUES(3, 4); + } + } {} + + do_test $tn.1.5 { + db2 eval COMMIT + db eval COMMIT + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal test.db-journal/1-journal} + + do_test $tn.1.6 { + db close + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal test.db-journal/1-journal} + + do_test $tn.1.7 { + db2 close + lsort [glob -nocomplain test.db-journal/*-journal] + } {} + + #------------------------------------------------------------------------- + # + server_reset_db + server_sqlite3 db2 test.db + + do_execsql_test $tn.2.0 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); } - db2 eval { - BEGIN; - INSERT INTO t2 VALUES(3, 4); + + # Two concurrent transactions committed. + # + do_test $tn.2.1 { + db eval { + BEGIN; + INSERT INTO t1 VALUES(1, 2); + } + db2 eval { + BEGIN; + INSERT INTO t2 VALUES(3, 4); + } + } {} + do_test $tn.2.2 { + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal test.db-journal/1-journal} + do_test $tn.2.3.1 { db eval COMMIT } {} + do_test $tn.2.3.2 { db2 eval COMMIT } {} + do_execsql_test 2.4 {SELECT * FROM t1, t2} {1 2 3 4} + do_test $tn.2.5 { + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal test.db-journal/1-journal} + + do_test $tn.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 $tn.2.7 { + # Transaction is automatically rolled back in this case. + sqlite3_get_autocommit db2 + } {1} + do_test $tn.2.8 { + execsql COMMIT + execsql { SELECT * FROM t1 } db2 + } {1 2 5 6} + db2 close + + #------------------------------------------------------------------------- + # + server_reset_db + do_execsql_test $tn.3.0 { + CREATE TABLE t1(a, b); } -} {} -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 $tn.3.1 { + lsort [glob -nocomplain test.db-journal/*-journal] + } {test.db-journal/0-journal} + + do_test $tn.3.2 { + db close + lsort [glob -nocomplain test.db-journal/*-journal] + } {} } -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 diff --git a/test/server3.test b/test/server3.test index 19b72a54a0..484d2e96e9 100644 --- a/test/server3.test +++ b/test/server3.test @@ -21,32 +21,37 @@ 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} +foreach {tn vfs} {1 unix-excl 2 unix} { + server_set_vfs $vfs + + 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 diff --git a/test/server_common.tcl b/test/server_common.tcl index 84cd6b27e4..d21c60085d 100644 --- a/test/server_common.tcl +++ b/test/server_common.tcl @@ -22,7 +22,7 @@ ifcapable !server { } proc server_sqlite3 {cmd file} { - sqlite3 $cmd $file -vfs unix-excl + sqlite3 $cmd $file -vfs $::server_vfs } proc server_reset_db {} { @@ -32,4 +32,8 @@ proc server_reset_db {} { server_sqlite3 db test.db } +set ::server_vfs unix-excl +proc server_set_vfs {vfs} { + set ::server_vfs $vfs +}