18a922f75Sshaneh /* 28a922f75Sshaneh ** 2010 October 28 38a922f75Sshaneh ** 48a922f75Sshaneh ** The author disclaims copyright to this source code. In place of 58a922f75Sshaneh ** a legal notice, here is a blessing: 68a922f75Sshaneh ** 78a922f75Sshaneh ** May you do good and not evil. 88a922f75Sshaneh ** May you find forgiveness for yourself and forgive others. 98a922f75Sshaneh ** May you share freely, never taking more than you give. 108a922f75Sshaneh ** 118a922f75Sshaneh ************************************************************************* 128a922f75Sshaneh ** 138a922f75Sshaneh ** This file contains a VFS "shim" - a layer that sits in between the 148a922f75Sshaneh ** pager and the real VFS. 158a922f75Sshaneh ** 168a922f75Sshaneh ** This particular shim enforces a multiplex system on DB files. 178a922f75Sshaneh ** This shim shards/partitions a single DB file into smaller 188a922f75Sshaneh ** "chunks" such that the total DB file size may exceed the maximum 198a922f75Sshaneh ** file size of the underlying file system. 208a922f75Sshaneh ** 218a922f75Sshaneh */ 228a922f75Sshaneh #include "sqlite3.h" 238a922f75Sshaneh #include <string.h> 248a922f75Sshaneh #include <assert.h> 258a922f75Sshaneh #include "sqliteInt.h" 268a922f75Sshaneh 278a922f75Sshaneh /************************ Shim Definitions ******************************/ 288a922f75Sshaneh 29050d09abSshaneh /* This is the limit on the chunk size. It may be changed by calling 30050d09abSshaneh ** the sqlite3_multiplex_set() interface. 31050d09abSshaneh */ 32b5830294Sshaneh #define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000 33050d09abSshaneh /* Default limit on number of chunks. Care should be taken 34050d09abSshaneh ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT 35050d09abSshaneh ** format specifier. It may be changed by calling 36050d09abSshaneh ** the sqlite3_multiplex_set() interface. 37050d09abSshaneh */ 388a922f75Sshaneh #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 39050d09abSshaneh 40050d09abSshaneh /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the 41050d09abSshaneh ** last SQLITE_MULTIPLEX_EXT_SZ characters of the 42050d09abSshaneh ** filename will be overwritten, otherwise, the 43050d09abSshaneh ** multiplex extension is simply appended to the filename. 44050d09abSshaneh ** Ex. (undefined) test.db -> test.db01 45050d09abSshaneh ** (defined) test.db -> test.01 46050d09abSshaneh ** Chunk 0 does not have a modified extension. 47050d09abSshaneh */ 48050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_FMT "%02d" 49050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_SZ 2 508a922f75Sshaneh 518a922f75Sshaneh /************************ Object Definitions ******************************/ 528a922f75Sshaneh 538a922f75Sshaneh /* Forward declaration of all object types */ 548a922f75Sshaneh typedef struct multiplexGroup multiplexGroup; 558a922f75Sshaneh typedef struct multiplexConn multiplexConn; 568a922f75Sshaneh 578a922f75Sshaneh /* 588a922f75Sshaneh ** A "multiplex group" is a collection of files that collectively 598a922f75Sshaneh ** makeup a single SQLite DB file. This allows the size of the DB 608a922f75Sshaneh ** to exceed the limits imposed by the file system. 618a922f75Sshaneh ** 628a922f75Sshaneh ** There is an instance of the following object for each defined multiplex 638a922f75Sshaneh ** group. 648a922f75Sshaneh */ 658a922f75Sshaneh struct multiplexGroup { 66050d09abSshaneh sqlite3_file **pReal; /* Handles to each chunk */ 67050d09abSshaneh char *bOpen; /* 0 if chunk not opened */ 688a922f75Sshaneh char *zName; /* Base filename of this group */ 698a922f75Sshaneh int nName; /* Length of base filename */ 708a922f75Sshaneh int flags; /* Flags used for original opening */ 718a922f75Sshaneh multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ 728a922f75Sshaneh }; 738a922f75Sshaneh 748a922f75Sshaneh /* 758a922f75Sshaneh ** An instance of the following object represents each open connection 768a922f75Sshaneh ** to a file that is multiplex'ed. This object is a 778a922f75Sshaneh ** subclass of sqlite3_file. The sqlite3_file object for the underlying 788a922f75Sshaneh ** VFS is appended to this structure. 798a922f75Sshaneh */ 808a922f75Sshaneh struct multiplexConn { 818a922f75Sshaneh sqlite3_file base; /* Base class - must be first */ 828a922f75Sshaneh multiplexGroup *pGroup; /* The underlying group of files */ 838a922f75Sshaneh }; 848a922f75Sshaneh 858a922f75Sshaneh /************************* Global Variables **********************************/ 868a922f75Sshaneh /* 878a922f75Sshaneh ** All global variables used by this file are containing within the following 888a922f75Sshaneh ** gMultiplex structure. 898a922f75Sshaneh */ 908a922f75Sshaneh static struct { 918a922f75Sshaneh /* The pOrigVfs is the real, original underlying VFS implementation. 928a922f75Sshaneh ** Most operations pass-through to the real VFS. This value is read-only 938a922f75Sshaneh ** during operation. It is only modified at start-time and thus does not 948a922f75Sshaneh ** require a mutex. 958a922f75Sshaneh */ 968a922f75Sshaneh sqlite3_vfs *pOrigVfs; 978a922f75Sshaneh 988a922f75Sshaneh /* The sThisVfs is the VFS structure used by this shim. It is initialized 998a922f75Sshaneh ** at start-time and thus does not require a mutex 1008a922f75Sshaneh */ 1018a922f75Sshaneh sqlite3_vfs sThisVfs; 1028a922f75Sshaneh 1038a922f75Sshaneh /* The sIoMethods defines the methods used by sqlite3_file objects 1048a922f75Sshaneh ** associated with this shim. It is initialized at start-time and does 1058a922f75Sshaneh ** not require a mutex. 1068a922f75Sshaneh ** 1078a922f75Sshaneh ** When the underlying VFS is called to open a file, it might return 1088a922f75Sshaneh ** either a version 1 or a version 2 sqlite3_file object. This shim 1098a922f75Sshaneh ** has to create a wrapper sqlite3_file of the same version. Hence 1108a922f75Sshaneh ** there are two I/O method structures, one for version 1 and the other 1118a922f75Sshaneh ** for version 2. 1128a922f75Sshaneh */ 1138a922f75Sshaneh sqlite3_io_methods sIoMethodsV1; 1148a922f75Sshaneh sqlite3_io_methods sIoMethodsV2; 1158a922f75Sshaneh 116fd1552f2Sshaneh /* True when this shim has been initialized. 1178a922f75Sshaneh */ 1188a922f75Sshaneh int isInitialized; 1198a922f75Sshaneh 1208a922f75Sshaneh /* For run-time access any of the other global data structures in this 1218a922f75Sshaneh ** shim, the following mutex must be held. 1228a922f75Sshaneh */ 1238a922f75Sshaneh sqlite3_mutex *pMutex; 1248a922f75Sshaneh 1258a922f75Sshaneh /* List of multiplexGroup objects. 1268a922f75Sshaneh */ 1278a922f75Sshaneh multiplexGroup *pGroups; 1288a922f75Sshaneh 129fd1552f2Sshaneh /* Chunk params. 1308a922f75Sshaneh */ 1318a922f75Sshaneh int nChunkSize; 1328a922f75Sshaneh int nMaxChunks; 1338a922f75Sshaneh 134b5830294Sshaneh /* Storage for temp file names. Allocated during 135b5830294Sshaneh ** initialization to the max pathname of the underlying VFS. 136b5830294Sshaneh */ 137b5830294Sshaneh char *zName; 138b5830294Sshaneh 1398a922f75Sshaneh } gMultiplex; 1408a922f75Sshaneh 1418a922f75Sshaneh /************************* Utility Routines *********************************/ 1428a922f75Sshaneh /* 1438a922f75Sshaneh ** Acquire and release the mutex used to serialize access to the 1448a922f75Sshaneh ** list of multiplexGroups. 1458a922f75Sshaneh */ 1468a922f75Sshaneh static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } 1478a922f75Sshaneh static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } 1488a922f75Sshaneh 1498a922f75Sshaneh /* Translate an sqlite3_file* that is really a multiplexGroup* into 1508a922f75Sshaneh ** the sqlite3_file* for the underlying original VFS. 1518a922f75Sshaneh */ 1528a922f75Sshaneh static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ 1538a922f75Sshaneh multiplexGroup *pGroup = pConn->pGroup; 1548a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 1558a922f75Sshaneh if( iChunk<gMultiplex.nMaxChunks ){ 1568a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ 1578a922f75Sshaneh if( !pGroup->bOpen[iChunk] ){ 158050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 159050d09abSshaneh if( iChunk ){ 160050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 161050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk); 162050d09abSshaneh #else 163050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk); 164050d09abSshaneh #endif 165050d09abSshaneh } 166050d09abSshaneh *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags); 1678a922f75Sshaneh if( *rc==SQLITE_OK ){ 1688a922f75Sshaneh pGroup->bOpen[iChunk] = -1; 1698a922f75Sshaneh return pSubOpen; 1708a922f75Sshaneh } 1718a922f75Sshaneh return NULL; 1728a922f75Sshaneh } 1738a922f75Sshaneh *rc = SQLITE_OK; 1748a922f75Sshaneh return pSubOpen; 1758a922f75Sshaneh } 176b5830294Sshaneh *rc = SQLITE_FULL; 1778a922f75Sshaneh return NULL; 1788a922f75Sshaneh } 1798a922f75Sshaneh 1808a922f75Sshaneh /************************* VFS Method Wrappers *****************************/ 181b5830294Sshaneh 1828a922f75Sshaneh /* 1838a922f75Sshaneh ** This is the xOpen method used for the "multiplex" VFS. 1848a922f75Sshaneh ** 1858a922f75Sshaneh ** Most of the work is done by the underlying original VFS. This method 1868a922f75Sshaneh ** simply links the new file into the appropriate multiplex group if it is a 1878a922f75Sshaneh ** file that needs to be tracked. 1888a922f75Sshaneh */ 1898a922f75Sshaneh static int multiplexOpen( 1908a922f75Sshaneh sqlite3_vfs *pVfs, /* The multiplex VFS */ 1918a922f75Sshaneh const char *zName, /* Name of file to be opened */ 1928a922f75Sshaneh sqlite3_file *pConn, /* Fill in this file descriptor */ 1938a922f75Sshaneh int flags, /* Flags to control the opening */ 1948a922f75Sshaneh int *pOutFlags /* Flags showing results of opening */ 1958a922f75Sshaneh ){ 1968a922f75Sshaneh int rc; /* Result code */ 1978a922f75Sshaneh multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ 1988a922f75Sshaneh multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ 1998a922f75Sshaneh sqlite3_file *pSubOpen; /* Real file descriptor */ 2008a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 2018a922f75Sshaneh int nName = sqlite3Strlen30(zName); 2028a922f75Sshaneh int i; 203050d09abSshaneh int sz; 2048a922f75Sshaneh 2058a922f75Sshaneh UNUSED_PARAMETER(pVfs); 2068a922f75Sshaneh 2078a922f75Sshaneh /* We need to create a group structure and manage 2088a922f75Sshaneh ** access to this group of files. 2098a922f75Sshaneh */ 2108a922f75Sshaneh multiplexEnter(); 2118a922f75Sshaneh pMultiplexOpen = (multiplexConn*)pConn; 212050d09abSshaneh /* allocate space for group */ 213050d09abSshaneh sz = sizeof(multiplexGroup) /* multiplexGroup */ 214050d09abSshaneh + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */ 215050d09abSshaneh + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) /* *pReal */ 216050d09abSshaneh + gMultiplex.nMaxChunks /* bOpen[] */ 217050d09abSshaneh + nName + 1; /* zName */ 218050d09abSshaneh #ifndef SQLITE_MULTIPLEX_EXT_OVWR 219050d09abSshaneh sz += SQLITE_MULTIPLEX_EXT_SZ; 220050d09abSshaneh assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); 221050d09abSshaneh #else 222050d09abSshaneh assert(nName >= SQLITE_MULTIPLEX_EXT_SZ); 223050d09abSshaneh assert(nName < pOrigVfs->mxPathname); 224050d09abSshaneh #endif 225050d09abSshaneh pGroup = sqlite3_malloc( sz ); 2268a922f75Sshaneh if( pGroup==0 ){ 2278a922f75Sshaneh rc=SQLITE_NOMEM; 2288a922f75Sshaneh }else{ 229050d09abSshaneh /* assign pointers to extra space allocated */ 230050d09abSshaneh char *p = (char *)&pGroup[1]; 2318a922f75Sshaneh pMultiplexOpen->pGroup = pGroup; 232050d09abSshaneh memset(pGroup, 0, sz); 233050d09abSshaneh pGroup->pReal = (sqlite3_file **)p; 234050d09abSshaneh p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks); 2358a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 236050d09abSshaneh pGroup->pReal[i] = (sqlite3_file *)p; 237050d09abSshaneh p += pOrigVfs->szOsFile; 2388a922f75Sshaneh } 239050d09abSshaneh pGroup->bOpen = p; 240050d09abSshaneh p += gMultiplex.nMaxChunks; 241050d09abSshaneh pGroup->zName = p; 242050d09abSshaneh /* save off base filename, name length, and original open flags */ 2438a922f75Sshaneh memcpy(pGroup->zName, zName, nName+1); 2448a922f75Sshaneh pGroup->nName = nName; 2458a922f75Sshaneh pGroup->flags = flags; 2468a922f75Sshaneh pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); 2478a922f75Sshaneh if( pSubOpen ){ 2488a922f75Sshaneh if( pSubOpen->pMethods->iVersion==1 ){ 2498a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; 2508a922f75Sshaneh }else{ 2518a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; 2528a922f75Sshaneh } 2538a922f75Sshaneh /* place this group at the head of our list */ 2548a922f75Sshaneh pGroup->pNext = gMultiplex.pGroups; 2558a922f75Sshaneh if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; 2568a922f75Sshaneh gMultiplex.pGroups = pGroup; 2578a922f75Sshaneh }else{ 2588a922f75Sshaneh sqlite3_free(pGroup); 2598a922f75Sshaneh } 2608a922f75Sshaneh } 2618a922f75Sshaneh multiplexLeave(); 2628a922f75Sshaneh return rc; 2638a922f75Sshaneh } 2648a922f75Sshaneh 265b5830294Sshaneh /* 266b5830294Sshaneh ** This is the xDelete method used for the "multiplex" VFS. 267b5830294Sshaneh ** It attempts to delete the filename specified, as well 268050d09abSshaneh ** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension. 269b5830294Sshaneh */ 270b5830294Sshaneh static int multiplexDelete( 271b5830294Sshaneh sqlite3_vfs *pVfs, /* The multiplex VFS */ 272b5830294Sshaneh const char *zName, /* Name of file to delete */ 273b5830294Sshaneh int syncDir 274b5830294Sshaneh ){ 275b5830294Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 276b5830294Sshaneh int rc = SQLITE_OK; 277b5830294Sshaneh int nName = sqlite3Strlen30(zName); 278b5830294Sshaneh int i; 279b5830294Sshaneh 280b5830294Sshaneh UNUSED_PARAMETER(pVfs); 281b5830294Sshaneh 282b5830294Sshaneh multiplexEnter(); 283b5830294Sshaneh memcpy(gMultiplex.zName, zName, nName+1); 284b5830294Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 285b5830294Sshaneh int rc2; 286b5830294Sshaneh int exists = 0; 287050d09abSshaneh if( i ){ 288050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 289050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); 290050d09abSshaneh #else 291050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i); 292050d09abSshaneh #endif 293050d09abSshaneh } 294b5830294Sshaneh rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); 295b5830294Sshaneh if( rc2==SQLITE_OK && exists){ 296b5830294Sshaneh /* if it exists, delete it */ 297b5830294Sshaneh rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); 298b5830294Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 299b5830294Sshaneh }else{ 300b5830294Sshaneh /* stop at first "gap" */ 301b5830294Sshaneh break; 302b5830294Sshaneh } 303b5830294Sshaneh } 304b5830294Sshaneh multiplexLeave(); 305b5830294Sshaneh return rc; 306b5830294Sshaneh } 307b5830294Sshaneh 308fffadc52Sdan static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){ 309fffadc52Sdan return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d); 310fffadc52Sdan } 311fffadc52Sdan static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){ 312fffadc52Sdan return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d); 313fffadc52Sdan } 314fffadc52Sdan static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){ 315fffadc52Sdan return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b); 316fffadc52Sdan } 317fffadc52Sdan static void multiplexDlError(sqlite3_vfs *a, int b, char *c){ 318fffadc52Sdan gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c); 319fffadc52Sdan } 320fffadc52Sdan static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){ 321fffadc52Sdan return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c); 322fffadc52Sdan } 323fffadc52Sdan static void multiplexDlClose(sqlite3_vfs *a, void *b){ 324fffadc52Sdan gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b); 325fffadc52Sdan } 326fffadc52Sdan static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){ 327fffadc52Sdan return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c); 328fffadc52Sdan } 329fffadc52Sdan static int multiplexSleep(sqlite3_vfs *a, int b){ 330fffadc52Sdan return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b); 331fffadc52Sdan } 332fffadc52Sdan static int multiplexCurrentTime(sqlite3_vfs *a, double *b){ 333fffadc52Sdan return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b); 334fffadc52Sdan } 335fffadc52Sdan static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){ 336fffadc52Sdan return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c); 337fffadc52Sdan } 338fffadc52Sdan static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){ 339fffadc52Sdan return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b); 340fffadc52Sdan } 341fffadc52Sdan 3428a922f75Sshaneh /************************ I/O Method Wrappers *******************************/ 3438a922f75Sshaneh 3448a922f75Sshaneh /* xClose requests get passed through to the original VFS. 3458a922f75Sshaneh ** We loop over all open chunk handles and close them. 3468a922f75Sshaneh ** The group structure for this file is unlinked from 3478a922f75Sshaneh ** our list of groups and freed. 3488a922f75Sshaneh */ 3498a922f75Sshaneh static int multiplexClose(sqlite3_file *pConn){ 3508a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3518a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 3528a922f75Sshaneh int rc = SQLITE_OK; 3538a922f75Sshaneh int i; 3548a922f75Sshaneh multiplexEnter(); 355b5830294Sshaneh /* close any open handles */ 3568a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 3578a922f75Sshaneh if( pGroup->bOpen[i] ){ 3588a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 3598a922f75Sshaneh int rc2 = pSubOpen->pMethods->xClose(pSubOpen); 3608a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 3618a922f75Sshaneh pGroup->bOpen[i] = 0; 3628a922f75Sshaneh } 3638a922f75Sshaneh } 364b5830294Sshaneh /* remove from linked list */ 3658a922f75Sshaneh if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; 3668a922f75Sshaneh if( pGroup->pPrev ){ 3678a922f75Sshaneh pGroup->pPrev->pNext = pGroup->pNext; 3688a922f75Sshaneh }else{ 3698a922f75Sshaneh gMultiplex.pGroups = pGroup->pNext; 3708a922f75Sshaneh } 3718a922f75Sshaneh sqlite3_free(pGroup); 3728a922f75Sshaneh multiplexLeave(); 3738a922f75Sshaneh return rc; 3748a922f75Sshaneh } 3758a922f75Sshaneh 3768a922f75Sshaneh /* Pass xRead requests thru to the original VFS after 3778a922f75Sshaneh ** determining the correct chunk to operate on. 378fd1552f2Sshaneh ** Break up reads across chunk boundaries. 3798a922f75Sshaneh */ 3808a922f75Sshaneh static int multiplexRead( 3818a922f75Sshaneh sqlite3_file *pConn, 3828a922f75Sshaneh void *pBuf, 3838a922f75Sshaneh int iAmt, 3848a922f75Sshaneh sqlite3_int64 iOfst 3858a922f75Sshaneh ){ 3868a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3878a922f75Sshaneh int rc = SQLITE_OK; 3888a922f75Sshaneh multiplexEnter(); 3898a922f75Sshaneh while( iAmt > 0 ){ 3908a922f75Sshaneh int i = (int)(iOfst/gMultiplex.nChunkSize); 3918a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 3928a922f75Sshaneh if( pSubOpen ){ 3938a922f75Sshaneh int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; 3948a922f75Sshaneh if( extra<0 ) extra = 0; 3958a922f75Sshaneh iAmt -= extra; 3968a922f75Sshaneh rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); 3978a922f75Sshaneh if( rc!=SQLITE_OK ) break; 3988a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 3998a922f75Sshaneh iOfst += iAmt; 4008a922f75Sshaneh iAmt = extra; 4018a922f75Sshaneh }else{ 4028a922f75Sshaneh rc = SQLITE_IOERR_READ; 4038a922f75Sshaneh break; 4048a922f75Sshaneh } 4058a922f75Sshaneh } 4068a922f75Sshaneh multiplexLeave(); 4078a922f75Sshaneh return rc; 4088a922f75Sshaneh } 4098a922f75Sshaneh 4108a922f75Sshaneh /* Pass xWrite requests thru to the original VFS after 4118a922f75Sshaneh ** determining the correct chunk to operate on. 412fd1552f2Sshaneh ** Break up writes across chunk boundaries. 4138a922f75Sshaneh */ 4148a922f75Sshaneh static int multiplexWrite( 4158a922f75Sshaneh sqlite3_file *pConn, 4168a922f75Sshaneh const void *pBuf, 4178a922f75Sshaneh int iAmt, 4188a922f75Sshaneh sqlite3_int64 iOfst 4198a922f75Sshaneh ){ 4208a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4218a922f75Sshaneh int rc = SQLITE_OK; 4228a922f75Sshaneh multiplexEnter(); 4238a922f75Sshaneh while( iAmt > 0 ){ 4248a922f75Sshaneh int i = (int)(iOfst/gMultiplex.nChunkSize); 4258a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 4268a922f75Sshaneh if( pSubOpen ){ 4278a922f75Sshaneh int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; 4288a922f75Sshaneh if( extra<0 ) extra = 0; 4298a922f75Sshaneh iAmt -= extra; 4308a922f75Sshaneh rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); 4318a922f75Sshaneh if( rc!=SQLITE_OK ) break; 4328a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 4338a922f75Sshaneh iOfst += iAmt; 4348a922f75Sshaneh iAmt = extra; 4358a922f75Sshaneh }else{ 4368a922f75Sshaneh rc = SQLITE_IOERR_WRITE; 4378a922f75Sshaneh break; 4388a922f75Sshaneh } 4398a922f75Sshaneh } 4408a922f75Sshaneh multiplexLeave(); 4418a922f75Sshaneh return rc; 4428a922f75Sshaneh } 4438a922f75Sshaneh 4448a922f75Sshaneh /* Pass xTruncate requests thru to the original VFS after 4458a922f75Sshaneh ** determining the correct chunk to operate on. Delete any 4468a922f75Sshaneh ** chunks above the truncate mark. 4478a922f75Sshaneh */ 4488a922f75Sshaneh static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ 4498a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4508a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 4518a922f75Sshaneh int rc = SQLITE_OK; 4528a922f75Sshaneh int rc2; 4538a922f75Sshaneh int i; 4548a922f75Sshaneh sqlite3_file *pSubOpen; 4558a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 4568a922f75Sshaneh multiplexEnter(); 457050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 4588a922f75Sshaneh /* delete the chunks above the truncate limit */ 4598a922f75Sshaneh for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){ 460fd1552f2Sshaneh /* close any open chunks before deleting them */ 4618a922f75Sshaneh if( pGroup->bOpen[i] ){ 4628a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 4638a922f75Sshaneh rc2 = pSubOpen->pMethods->xClose(pSubOpen); 4648a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 4650596beecSshaneh pGroup->bOpen[i] = 0; 4668a922f75Sshaneh } 467050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 468050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); 469050d09abSshaneh #else 470050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); 471050d09abSshaneh #endif 472050d09abSshaneh rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); 4738a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 4748a922f75Sshaneh } 4758a922f75Sshaneh pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL); 4768a922f75Sshaneh if( pSubOpen ){ 4778a922f75Sshaneh rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize); 4788a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 4798a922f75Sshaneh }else{ 4808a922f75Sshaneh rc = SQLITE_IOERR_TRUNCATE; 4818a922f75Sshaneh } 4828a922f75Sshaneh multiplexLeave(); 4838a922f75Sshaneh return rc; 4848a922f75Sshaneh } 4858a922f75Sshaneh 4868a922f75Sshaneh /* Pass xSync requests through to the original VFS without change 4878a922f75Sshaneh */ 4888a922f75Sshaneh static int multiplexSync(sqlite3_file *pConn, int flags){ 4898a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4908a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 4918a922f75Sshaneh int rc = SQLITE_OK; 4928a922f75Sshaneh int i; 4938a922f75Sshaneh multiplexEnter(); 4948a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 4958a922f75Sshaneh /* if we don't have it open, we don't need to sync it */ 4968a922f75Sshaneh if( pGroup->bOpen[i] ){ 4978a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 4988a922f75Sshaneh int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); 4998a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 5008a922f75Sshaneh } 5018a922f75Sshaneh } 5028a922f75Sshaneh multiplexLeave(); 5038a922f75Sshaneh return rc; 5048a922f75Sshaneh } 5058a922f75Sshaneh 5068a922f75Sshaneh /* Pass xFileSize requests through to the original VFS. 5078a922f75Sshaneh ** Aggregate the size of all the chunks before returning. 5088a922f75Sshaneh */ 5098a922f75Sshaneh static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ 5108a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5118a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 5128a922f75Sshaneh int rc = SQLITE_OK; 5138a922f75Sshaneh int rc2; 5148a922f75Sshaneh int i; 5158a922f75Sshaneh multiplexEnter(); 5168a922f75Sshaneh *pSize = 0; 5178a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 5188a922f75Sshaneh sqlite3_file *pSubOpen = NULL; 5198a922f75Sshaneh /* if not opened already, check to see if the chunk exists */ 5208a922f75Sshaneh if( pGroup->bOpen[i] ){ 5218a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 5228a922f75Sshaneh }else{ 5238a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 5248a922f75Sshaneh int exists = 0; 525050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 526050d09abSshaneh if( i ){ 527050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 528050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); 529050d09abSshaneh #else 530050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); 531050d09abSshaneh #endif 532050d09abSshaneh } 533050d09abSshaneh rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); 5348a922f75Sshaneh if( rc2==SQLITE_OK && exists){ 5358a922f75Sshaneh /* if it exists, open it */ 5368a922f75Sshaneh pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 5378a922f75Sshaneh }else{ 5388a922f75Sshaneh /* stop at first "gap" */ 5398a922f75Sshaneh break; 5408a922f75Sshaneh } 5418a922f75Sshaneh } 5428a922f75Sshaneh if( pSubOpen ){ 543fd1552f2Sshaneh sqlite3_int64 sz; 5448a922f75Sshaneh rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); 5458a922f75Sshaneh if( rc2!=SQLITE_OK ){ 5468a922f75Sshaneh rc = rc2; 5478a922f75Sshaneh }else{ 548*8fcfc344Sshaneh if( sz>gMultiplex.nChunkSize ){ 549*8fcfc344Sshaneh rc = SQLITE_IOERR_FSTAT; 550*8fcfc344Sshaneh } 5518a922f75Sshaneh *pSize += sz; 5528a922f75Sshaneh } 5538a922f75Sshaneh }else{ 5548a922f75Sshaneh break; 5558a922f75Sshaneh } 5568a922f75Sshaneh } 5578a922f75Sshaneh multiplexLeave(); 5588a922f75Sshaneh return rc; 5598a922f75Sshaneh } 5608a922f75Sshaneh 5618a922f75Sshaneh /* Pass xLock requests through to the original VFS unchanged. 5628a922f75Sshaneh */ 5638a922f75Sshaneh static int multiplexLock(sqlite3_file *pConn, int lock){ 5648a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5658a922f75Sshaneh int rc; 5668a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5678a922f75Sshaneh if( pSubOpen ){ 5688a922f75Sshaneh return pSubOpen->pMethods->xLock(pSubOpen, lock); 5698a922f75Sshaneh } 5708a922f75Sshaneh return SQLITE_BUSY; 5718a922f75Sshaneh } 5728a922f75Sshaneh 5738a922f75Sshaneh /* Pass xUnlock requests through to the original VFS unchanged. 5748a922f75Sshaneh */ 5758a922f75Sshaneh static int multiplexUnlock(sqlite3_file *pConn, int lock){ 5768a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5778a922f75Sshaneh int rc; 5788a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5798a922f75Sshaneh if( pSubOpen ){ 5808a922f75Sshaneh return pSubOpen->pMethods->xUnlock(pSubOpen, lock); 5818a922f75Sshaneh } 5828a922f75Sshaneh return SQLITE_IOERR_UNLOCK; 5838a922f75Sshaneh } 5848a922f75Sshaneh 5858a922f75Sshaneh /* Pass xCheckReservedLock requests through to the original VFS unchanged. 5868a922f75Sshaneh */ 5878a922f75Sshaneh static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ 5888a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5898a922f75Sshaneh int rc; 5908a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5918a922f75Sshaneh if( pSubOpen ){ 5928a922f75Sshaneh return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); 5938a922f75Sshaneh } 5948a922f75Sshaneh return SQLITE_IOERR_CHECKRESERVEDLOCK; 5958a922f75Sshaneh } 5968a922f75Sshaneh 5978a922f75Sshaneh /* Pass xFileControl requests through to the original VFS unchanged. 5988a922f75Sshaneh */ 5998a922f75Sshaneh static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ 6008a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6018a922f75Sshaneh int rc; 6028a922f75Sshaneh sqlite3_file *pSubOpen; 6038a922f75Sshaneh if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK; 6048a922f75Sshaneh pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6058a922f75Sshaneh if( pSubOpen ){ 6068a922f75Sshaneh return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); 6078a922f75Sshaneh } 6088a922f75Sshaneh return SQLITE_ERROR; 6098a922f75Sshaneh } 6108a922f75Sshaneh 6118a922f75Sshaneh /* Pass xSectorSize requests through to the original VFS unchanged. 6128a922f75Sshaneh */ 6138a922f75Sshaneh static int multiplexSectorSize(sqlite3_file *pConn){ 6148a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6158a922f75Sshaneh int rc; 6168a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6178a922f75Sshaneh if( pSubOpen ){ 6188a922f75Sshaneh return pSubOpen->pMethods->xSectorSize(pSubOpen); 6198a922f75Sshaneh } 6208a922f75Sshaneh return SQLITE_DEFAULT_SECTOR_SIZE; 6218a922f75Sshaneh } 6228a922f75Sshaneh 6238a922f75Sshaneh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. 6248a922f75Sshaneh */ 6258a922f75Sshaneh static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ 6268a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6278a922f75Sshaneh int rc; 6288a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6298a922f75Sshaneh if( pSubOpen ){ 6308a922f75Sshaneh return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); 6318a922f75Sshaneh } 6328a922f75Sshaneh return 0; 6338a922f75Sshaneh } 6348a922f75Sshaneh 6358a922f75Sshaneh /* Pass xShmMap requests through to the original VFS unchanged. 6368a922f75Sshaneh */ 6378a922f75Sshaneh static int multiplexShmMap( 6388a922f75Sshaneh sqlite3_file *pConn, /* Handle open on database file */ 6398a922f75Sshaneh int iRegion, /* Region to retrieve */ 6408a922f75Sshaneh int szRegion, /* Size of regions */ 6418a922f75Sshaneh int bExtend, /* True to extend file if necessary */ 6428a922f75Sshaneh void volatile **pp /* OUT: Mapped memory */ 6438a922f75Sshaneh ){ 6448a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6458a922f75Sshaneh int rc; 6468a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6478a922f75Sshaneh if( pSubOpen ){ 6488a922f75Sshaneh return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); 6498a922f75Sshaneh } 6508a922f75Sshaneh return SQLITE_IOERR; 6518a922f75Sshaneh } 6528a922f75Sshaneh 6538a922f75Sshaneh /* Pass xShmLock requests through to the original VFS unchanged. 6548a922f75Sshaneh */ 6558a922f75Sshaneh static int multiplexShmLock( 6568a922f75Sshaneh sqlite3_file *pConn, /* Database file holding the shared memory */ 6578a922f75Sshaneh int ofst, /* First lock to acquire or release */ 6588a922f75Sshaneh int n, /* Number of locks to acquire or release */ 6598a922f75Sshaneh int flags /* What to do with the lock */ 6608a922f75Sshaneh ){ 6618a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6628a922f75Sshaneh int rc; 6638a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6648a922f75Sshaneh if( pSubOpen ){ 6658a922f75Sshaneh return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); 6668a922f75Sshaneh } 6678a922f75Sshaneh return SQLITE_BUSY; 6688a922f75Sshaneh } 6698a922f75Sshaneh 6708a922f75Sshaneh /* Pass xShmBarrier requests through to the original VFS unchanged. 6718a922f75Sshaneh */ 6728a922f75Sshaneh static void multiplexShmBarrier(sqlite3_file *pConn){ 6738a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6748a922f75Sshaneh int rc; 6758a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6768a922f75Sshaneh if( pSubOpen ){ 6778a922f75Sshaneh pSubOpen->pMethods->xShmBarrier(pSubOpen); 6788a922f75Sshaneh } 6798a922f75Sshaneh } 6808a922f75Sshaneh 6818a922f75Sshaneh /* Pass xShmUnmap requests through to the original VFS unchanged. 6828a922f75Sshaneh */ 6838a922f75Sshaneh static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ 6848a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6858a922f75Sshaneh int rc; 6868a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 6878a922f75Sshaneh if( pSubOpen ){ 6888a922f75Sshaneh return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); 6898a922f75Sshaneh } 6908a922f75Sshaneh return SQLITE_OK; 6918a922f75Sshaneh } 6928a922f75Sshaneh 6938a922f75Sshaneh /************************** Public Interfaces *****************************/ 6948a922f75Sshaneh /* 6958a922f75Sshaneh ** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName 6968a922f75Sshaneh ** as the VFS that does the actual work. Use the default if 6978a922f75Sshaneh ** zOrigVfsName==NULL. 6988a922f75Sshaneh ** 6998a922f75Sshaneh ** The multiplex VFS shim is named "multiplex". It will become the default 7008a922f75Sshaneh ** VFS if makeDefault is non-zero. 7018a922f75Sshaneh ** 7028a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once 7038a922f75Sshaneh ** during start-up. 7048a922f75Sshaneh */ 7058a922f75Sshaneh int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ 7068a922f75Sshaneh sqlite3_vfs *pOrigVfs; 7078a922f75Sshaneh if( gMultiplex.isInitialized ) return SQLITE_MISUSE; 7088a922f75Sshaneh pOrigVfs = sqlite3_vfs_find(zOrigVfsName); 7098a922f75Sshaneh if( pOrigVfs==0 ) return SQLITE_ERROR; 7108a922f75Sshaneh assert( pOrigVfs!=&gMultiplex.sThisVfs ); 7118a922f75Sshaneh gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 7128a922f75Sshaneh if( !gMultiplex.pMutex ){ 7138a922f75Sshaneh return SQLITE_NOMEM; 7148a922f75Sshaneh } 715b5830294Sshaneh gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); 716b5830294Sshaneh if( !gMultiplex.zName ){ 717b5830294Sshaneh sqlite3_mutex_free(gMultiplex.pMutex); 718b5830294Sshaneh return SQLITE_NOMEM; 719b5830294Sshaneh } 7208a922f75Sshaneh gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; 7218a922f75Sshaneh gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; 7228a922f75Sshaneh gMultiplex.pGroups = NULL; 7238a922f75Sshaneh gMultiplex.isInitialized = 1; 7248a922f75Sshaneh gMultiplex.pOrigVfs = pOrigVfs; 7258a922f75Sshaneh gMultiplex.sThisVfs = *pOrigVfs; 7268a922f75Sshaneh gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); 7278a922f75Sshaneh gMultiplex.sThisVfs.zName = "multiplex"; 7288a922f75Sshaneh gMultiplex.sThisVfs.xOpen = multiplexOpen; 729b5830294Sshaneh gMultiplex.sThisVfs.xDelete = multiplexDelete; 730fffadc52Sdan gMultiplex.sThisVfs.xAccess = multiplexAccess; 731fffadc52Sdan gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; 732fffadc52Sdan gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; 733fffadc52Sdan gMultiplex.sThisVfs.xDlError = multiplexDlError; 734fffadc52Sdan gMultiplex.sThisVfs.xDlSym = multiplexDlSym; 735fffadc52Sdan gMultiplex.sThisVfs.xDlClose = multiplexDlClose; 736fffadc52Sdan gMultiplex.sThisVfs.xRandomness = multiplexRandomness; 737fffadc52Sdan gMultiplex.sThisVfs.xSleep = multiplexSleep; 738fffadc52Sdan gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime; 739fffadc52Sdan gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError; 740fffadc52Sdan gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64; 741fffadc52Sdan 7428a922f75Sshaneh gMultiplex.sIoMethodsV1.iVersion = 1; 7438a922f75Sshaneh gMultiplex.sIoMethodsV1.xClose = multiplexClose; 7448a922f75Sshaneh gMultiplex.sIoMethodsV1.xRead = multiplexRead; 7458a922f75Sshaneh gMultiplex.sIoMethodsV1.xWrite = multiplexWrite; 7468a922f75Sshaneh gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate; 7478a922f75Sshaneh gMultiplex.sIoMethodsV1.xSync = multiplexSync; 7488a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize; 7498a922f75Sshaneh gMultiplex.sIoMethodsV1.xLock = multiplexLock; 7508a922f75Sshaneh gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock; 7518a922f75Sshaneh gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock; 7528a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl; 7538a922f75Sshaneh gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize; 7548a922f75Sshaneh gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics; 7558a922f75Sshaneh gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; 7568a922f75Sshaneh gMultiplex.sIoMethodsV2.iVersion = 2; 7578a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; 7588a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; 7598a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; 7608a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; 7618a922f75Sshaneh sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); 7628a922f75Sshaneh return SQLITE_OK; 7638a922f75Sshaneh } 7648a922f75Sshaneh 7658a922f75Sshaneh /* 7668a922f75Sshaneh ** Shutdown the multiplex system. 7678a922f75Sshaneh ** 7688a922f75Sshaneh ** All SQLite database connections must be closed before calling this 7698a922f75Sshaneh ** routine. 7708a922f75Sshaneh ** 7718a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while 7728a922f75Sshaneh ** shutting down in order to free all remaining multiplex groups. 7738a922f75Sshaneh */ 7748a922f75Sshaneh int sqlite3_multiplex_shutdown(void){ 7758a922f75Sshaneh if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; 7768a922f75Sshaneh if( gMultiplex.pGroups ) return SQLITE_MISUSE; 7778a922f75Sshaneh gMultiplex.isInitialized = 0; 778b5830294Sshaneh sqlite3_free(gMultiplex.zName); 7798a922f75Sshaneh sqlite3_mutex_free(gMultiplex.pMutex); 7808a922f75Sshaneh sqlite3_vfs_unregister(&gMultiplex.sThisVfs); 7818a922f75Sshaneh memset(&gMultiplex, 0, sizeof(gMultiplex)); 7828a922f75Sshaneh return SQLITE_OK; 7838a922f75Sshaneh } 7848a922f75Sshaneh 7858a922f75Sshaneh /* 7868a922f75Sshaneh ** Adjust chunking params. VFS should be initialized first. 7878a922f75Sshaneh ** No files should be open. Re-intializing will reset these 7888a922f75Sshaneh ** to the default. 7898a922f75Sshaneh */ 7908a922f75Sshaneh int sqlite3_multiplex_set( 7918a922f75Sshaneh int nChunkSize, /* Max chunk size */ 7928a922f75Sshaneh int nMaxChunks /* Max number of chunks */ 7938a922f75Sshaneh ){ 7948a922f75Sshaneh if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; 7958a922f75Sshaneh if( gMultiplex.pGroups ) return SQLITE_MISUSE; 796e5a6ad6dSshaneh if( nChunkSize<32 ) return SQLITE_MISUSE; 797e5a6ad6dSshaneh if( nMaxChunks<1 ) return SQLITE_MISUSE; 798050d09abSshaneh if( nMaxChunks>99 ) return SQLITE_MISUSE; 7998a922f75Sshaneh multiplexEnter(); 8008a922f75Sshaneh gMultiplex.nChunkSize = nChunkSize; 8018a922f75Sshaneh gMultiplex.nMaxChunks = nMaxChunks; 8028a922f75Sshaneh multiplexLeave(); 8038a922f75Sshaneh return SQLITE_OK; 8048a922f75Sshaneh } 8058a922f75Sshaneh 8068a922f75Sshaneh /***************************** Test Code ***********************************/ 8078a922f75Sshaneh #ifdef SQLITE_TEST 8088a922f75Sshaneh #include <tcl.h> 8098a922f75Sshaneh 8108a922f75Sshaneh extern const char *sqlite3TestErrorName(int); 8118a922f75Sshaneh 8128a922f75Sshaneh 8138a922f75Sshaneh /* 8148a922f75Sshaneh ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT 8158a922f75Sshaneh */ 8168a922f75Sshaneh static int test_multiplex_initialize( 8178a922f75Sshaneh void * clientData, 8188a922f75Sshaneh Tcl_Interp *interp, 8198a922f75Sshaneh int objc, 8208a922f75Sshaneh Tcl_Obj *CONST objv[] 8218a922f75Sshaneh ){ 8228a922f75Sshaneh const char *zName; /* Name of new multiplex VFS */ 8238a922f75Sshaneh int makeDefault; /* True to make the new VFS the default */ 8248a922f75Sshaneh int rc; /* Value returned by multiplex_initialize() */ 8258a922f75Sshaneh 8268a922f75Sshaneh UNUSED_PARAMETER(clientData); 8278a922f75Sshaneh 8288a922f75Sshaneh /* Process arguments */ 8298a922f75Sshaneh if( objc!=3 ){ 8308a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); 8318a922f75Sshaneh return TCL_ERROR; 8328a922f75Sshaneh } 8338a922f75Sshaneh zName = Tcl_GetString(objv[1]); 8348a922f75Sshaneh if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; 8358a922f75Sshaneh if( zName[0]=='\0' ) zName = 0; 8368a922f75Sshaneh 8378a922f75Sshaneh /* Call sqlite3_multiplex_initialize() */ 8388a922f75Sshaneh rc = sqlite3_multiplex_initialize(zName, makeDefault); 8398a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 8408a922f75Sshaneh 8418a922f75Sshaneh return TCL_OK; 8428a922f75Sshaneh } 8438a922f75Sshaneh 8448a922f75Sshaneh /* 8458a922f75Sshaneh ** tclcmd: sqlite3_multiplex_shutdown 8468a922f75Sshaneh */ 8478a922f75Sshaneh static int test_multiplex_shutdown( 8488a922f75Sshaneh void * clientData, 8498a922f75Sshaneh Tcl_Interp *interp, 8508a922f75Sshaneh int objc, 8518a922f75Sshaneh Tcl_Obj *CONST objv[] 8528a922f75Sshaneh ){ 8538a922f75Sshaneh int rc; /* Value returned by multiplex_shutdown() */ 8548a922f75Sshaneh 8558a922f75Sshaneh UNUSED_PARAMETER(clientData); 8568a922f75Sshaneh 8578a922f75Sshaneh if( objc!=1 ){ 8588a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, ""); 8598a922f75Sshaneh return TCL_ERROR; 8608a922f75Sshaneh } 8618a922f75Sshaneh 8628a922f75Sshaneh /* Call sqlite3_multiplex_shutdown() */ 8638a922f75Sshaneh rc = sqlite3_multiplex_shutdown(); 8648a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 8658a922f75Sshaneh 8668a922f75Sshaneh return TCL_OK; 8678a922f75Sshaneh } 8688a922f75Sshaneh 8698a922f75Sshaneh /* 8708a922f75Sshaneh ** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS 8718a922f75Sshaneh */ 8728a922f75Sshaneh static int test_multiplex_set( 8738a922f75Sshaneh void * clientData, 8748a922f75Sshaneh Tcl_Interp *interp, 8758a922f75Sshaneh int objc, 8768a922f75Sshaneh Tcl_Obj *CONST objv[] 8778a922f75Sshaneh ){ 8788a922f75Sshaneh int nChunkSize; /* Max chunk size */ 8798a922f75Sshaneh int nMaxChunks; /* Max number of chunks */ 8808a922f75Sshaneh int rc; /* Value returned by sqlite3_multiplex_set() */ 8818a922f75Sshaneh 8828a922f75Sshaneh UNUSED_PARAMETER(clientData); 8838a922f75Sshaneh 8848a922f75Sshaneh /* Process arguments */ 8858a922f75Sshaneh if( objc!=3 ){ 8868a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS"); 8878a922f75Sshaneh return TCL_ERROR; 8888a922f75Sshaneh } 8898a922f75Sshaneh if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR; 8908a922f75Sshaneh if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR; 8918a922f75Sshaneh 8928a922f75Sshaneh /* Invoke sqlite3_multiplex_set() */ 8938a922f75Sshaneh rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks); 8948a922f75Sshaneh 8958a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 8968a922f75Sshaneh return TCL_OK; 8978a922f75Sshaneh } 8988a922f75Sshaneh 8998a922f75Sshaneh /* 9008a922f75Sshaneh ** tclcmd: sqlite3_multiplex_dump 9018a922f75Sshaneh */ 9028a922f75Sshaneh static int test_multiplex_dump( 9038a922f75Sshaneh void * clientData, 9048a922f75Sshaneh Tcl_Interp *interp, 9058a922f75Sshaneh int objc, 9068a922f75Sshaneh Tcl_Obj *CONST objv[] 9078a922f75Sshaneh ){ 9088a922f75Sshaneh Tcl_Obj *pResult; 9098a922f75Sshaneh Tcl_Obj *pGroupTerm; 9108a922f75Sshaneh multiplexGroup *pGroup; 9118a922f75Sshaneh int i; 9128a922f75Sshaneh int nChunks = 0; 9138a922f75Sshaneh 9148a922f75Sshaneh UNUSED_PARAMETER(clientData); 9158a922f75Sshaneh UNUSED_PARAMETER(objc); 9168a922f75Sshaneh UNUSED_PARAMETER(objv); 9178a922f75Sshaneh 9188a922f75Sshaneh pResult = Tcl_NewObj(); 9198a922f75Sshaneh multiplexEnter(); 9208a922f75Sshaneh for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){ 9218a922f75Sshaneh pGroupTerm = Tcl_NewObj(); 9228a922f75Sshaneh 9238a922f75Sshaneh pGroup->zName[pGroup->nName] = '\0'; 9248a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9258a922f75Sshaneh Tcl_NewStringObj(pGroup->zName, -1)); 9268a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9278a922f75Sshaneh Tcl_NewIntObj(pGroup->nName)); 9288a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9298a922f75Sshaneh Tcl_NewIntObj(pGroup->flags)); 9308a922f75Sshaneh 9318a922f75Sshaneh /* count number of chunks with open handles */ 9328a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 9338a922f75Sshaneh if( pGroup->bOpen[i] ) nChunks++; 9348a922f75Sshaneh } 9358a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9368a922f75Sshaneh Tcl_NewIntObj(nChunks)); 9378a922f75Sshaneh 9388a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9398a922f75Sshaneh Tcl_NewIntObj(gMultiplex.nChunkSize)); 9408a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 9418a922f75Sshaneh Tcl_NewIntObj(gMultiplex.nMaxChunks)); 9428a922f75Sshaneh 9438a922f75Sshaneh Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); 9448a922f75Sshaneh } 9458a922f75Sshaneh multiplexLeave(); 9468a922f75Sshaneh Tcl_SetObjResult(interp, pResult); 9478a922f75Sshaneh return TCL_OK; 9488a922f75Sshaneh } 9498a922f75Sshaneh 9508a922f75Sshaneh /* 9518a922f75Sshaneh ** This routine registers the custom TCL commands defined in this 9528a922f75Sshaneh ** module. This should be the only procedure visible from outside 9538a922f75Sshaneh ** of this module. 9548a922f75Sshaneh */ 9558a922f75Sshaneh int Sqlitemultiplex_Init(Tcl_Interp *interp){ 9568a922f75Sshaneh static struct { 9578a922f75Sshaneh char *zName; 9588a922f75Sshaneh Tcl_ObjCmdProc *xProc; 9598a922f75Sshaneh } aCmd[] = { 9608a922f75Sshaneh { "sqlite3_multiplex_initialize", test_multiplex_initialize }, 9618a922f75Sshaneh { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, 9628a922f75Sshaneh { "sqlite3_multiplex_set", test_multiplex_set }, 9638a922f75Sshaneh { "sqlite3_multiplex_dump", test_multiplex_dump }, 9648a922f75Sshaneh }; 9658a922f75Sshaneh int i; 9668a922f75Sshaneh 9678a922f75Sshaneh for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 9688a922f75Sshaneh Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 9698a922f75Sshaneh } 9708a922f75Sshaneh 9718a922f75Sshaneh return TCL_OK; 9728a922f75Sshaneh } 9738a922f75Sshaneh #endif 974