xref: /sqlite-3.40.0/src/test_multiplex.c (revision bb201344)
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 
27*bb201344Sshaneh /*
28*bb201344Sshaneh ** For a build without mutexes, no-op the mutex calls.
29*bb201344Sshaneh */
30*bb201344Sshaneh #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
31*bb201344Sshaneh #define sqlite3_mutex_alloc(X)    ((sqlite3_mutex*)8)
32*bb201344Sshaneh #define sqlite3_mutex_free(X)
33*bb201344Sshaneh #define sqlite3_mutex_enter(X)
34*bb201344Sshaneh #define sqlite3_mutex_try(X)      SQLITE_OK
35*bb201344Sshaneh #define sqlite3_mutex_leave(X)
36*bb201344Sshaneh #define sqlite3_mutex_held(X)     ((void)(X),1)
37*bb201344Sshaneh #define sqlite3_mutex_notheld(X)  ((void)(X),1)
38*bb201344Sshaneh #endif /* SQLITE_THREADSAFE==0 */
39*bb201344Sshaneh 
40*bb201344Sshaneh 
418a922f75Sshaneh /************************ Shim Definitions ******************************/
428a922f75Sshaneh 
43050d09abSshaneh /* This is the limit on the chunk size.  It may be changed by calling
44050d09abSshaneh ** the sqlite3_multiplex_set() interface.
45050d09abSshaneh */
46b5830294Sshaneh #define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
47050d09abSshaneh /* Default limit on number of chunks.  Care should be taken
48050d09abSshaneh ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
49050d09abSshaneh ** format specifier. It may be changed by calling
50050d09abSshaneh ** the sqlite3_multiplex_set() interface.
51050d09abSshaneh */
528a922f75Sshaneh #define SQLITE_MULTIPLEX_MAX_CHUNKS 32
53050d09abSshaneh 
54050d09abSshaneh /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the
55050d09abSshaneh ** last SQLITE_MULTIPLEX_EXT_SZ characters of the
56050d09abSshaneh ** filename will be overwritten, otherwise, the
57050d09abSshaneh ** multiplex extension is simply appended to the filename.
58050d09abSshaneh ** Ex.  (undefined) test.db -> test.db01
59050d09abSshaneh **      (defined)   test.db -> test.01
60050d09abSshaneh ** Chunk 0 does not have a modified extension.
61050d09abSshaneh */
62050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_FMT    "%02d"
63050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_SZ     2
648a922f75Sshaneh 
658a922f75Sshaneh /************************ Object Definitions ******************************/
668a922f75Sshaneh 
678a922f75Sshaneh /* Forward declaration of all object types */
688a922f75Sshaneh typedef struct multiplexGroup multiplexGroup;
698a922f75Sshaneh typedef struct multiplexConn multiplexConn;
708a922f75Sshaneh 
718a922f75Sshaneh /*
728a922f75Sshaneh ** A "multiplex group" is a collection of files that collectively
738a922f75Sshaneh ** makeup a single SQLite DB file.  This allows the size of the DB
748a922f75Sshaneh ** to exceed the limits imposed by the file system.
758a922f75Sshaneh **
768a922f75Sshaneh ** There is an instance of the following object for each defined multiplex
778a922f75Sshaneh ** group.
788a922f75Sshaneh */
798a922f75Sshaneh struct multiplexGroup {
80050d09abSshaneh   sqlite3_file **pReal;            /* Handles to each chunk */
81*bb201344Sshaneh   char *bOpen;                     /* array of bools - 0 if chunk not opened */
828a922f75Sshaneh   char *zName;                     /* Base filename of this group */
838a922f75Sshaneh   int nName;                       /* Length of base filename */
848a922f75Sshaneh   int flags;                       /* Flags used for original opening */
858a922f75Sshaneh   multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
868a922f75Sshaneh };
878a922f75Sshaneh 
888a922f75Sshaneh /*
898a922f75Sshaneh ** An instance of the following object represents each open connection
908a922f75Sshaneh ** to a file that is multiplex'ed.  This object is a
918a922f75Sshaneh ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
928a922f75Sshaneh ** VFS is appended to this structure.
938a922f75Sshaneh */
948a922f75Sshaneh struct multiplexConn {
958a922f75Sshaneh   sqlite3_file base;              /* Base class - must be first */
968a922f75Sshaneh   multiplexGroup *pGroup;         /* The underlying group of files */
978a922f75Sshaneh };
988a922f75Sshaneh 
998a922f75Sshaneh /************************* Global Variables **********************************/
1008a922f75Sshaneh /*
1018a922f75Sshaneh ** All global variables used by this file are containing within the following
1028a922f75Sshaneh ** gMultiplex structure.
1038a922f75Sshaneh */
1048a922f75Sshaneh static struct {
1058a922f75Sshaneh   /* The pOrigVfs is the real, original underlying VFS implementation.
1068a922f75Sshaneh   ** Most operations pass-through to the real VFS.  This value is read-only
1078a922f75Sshaneh   ** during operation.  It is only modified at start-time and thus does not
1088a922f75Sshaneh   ** require a mutex.
1098a922f75Sshaneh   */
1108a922f75Sshaneh   sqlite3_vfs *pOrigVfs;
1118a922f75Sshaneh 
1128a922f75Sshaneh   /* The sThisVfs is the VFS structure used by this shim.  It is initialized
1138a922f75Sshaneh   ** at start-time and thus does not require a mutex
1148a922f75Sshaneh   */
1158a922f75Sshaneh   sqlite3_vfs sThisVfs;
1168a922f75Sshaneh 
1178a922f75Sshaneh   /* The sIoMethods defines the methods used by sqlite3_file objects
1188a922f75Sshaneh   ** associated with this shim.  It is initialized at start-time and does
1198a922f75Sshaneh   ** not require a mutex.
1208a922f75Sshaneh   **
1218a922f75Sshaneh   ** When the underlying VFS is called to open a file, it might return
1228a922f75Sshaneh   ** either a version 1 or a version 2 sqlite3_file object.  This shim
1238a922f75Sshaneh   ** has to create a wrapper sqlite3_file of the same version.  Hence
1248a922f75Sshaneh   ** there are two I/O method structures, one for version 1 and the other
1258a922f75Sshaneh   ** for version 2.
1268a922f75Sshaneh   */
1278a922f75Sshaneh   sqlite3_io_methods sIoMethodsV1;
1288a922f75Sshaneh   sqlite3_io_methods sIoMethodsV2;
1298a922f75Sshaneh 
130fd1552f2Sshaneh   /* True when this shim has been initialized.
1318a922f75Sshaneh   */
1328a922f75Sshaneh   int isInitialized;
1338a922f75Sshaneh 
1348a922f75Sshaneh   /* For run-time access any of the other global data structures in this
1358a922f75Sshaneh   ** shim, the following mutex must be held.
1368a922f75Sshaneh   */
1378a922f75Sshaneh   sqlite3_mutex *pMutex;
1388a922f75Sshaneh 
1398a922f75Sshaneh   /* List of multiplexGroup objects.
1408a922f75Sshaneh   */
1418a922f75Sshaneh   multiplexGroup *pGroups;
1428a922f75Sshaneh 
143fd1552f2Sshaneh   /* Chunk params.
1448a922f75Sshaneh   */
1458a922f75Sshaneh   int nChunkSize;
1468a922f75Sshaneh   int nMaxChunks;
1478a922f75Sshaneh 
148b5830294Sshaneh   /* Storage for temp file names.  Allocated during
149b5830294Sshaneh   ** initialization to the max pathname of the underlying VFS.
150b5830294Sshaneh   */
151b5830294Sshaneh   char *zName;
152b5830294Sshaneh 
1538a922f75Sshaneh } gMultiplex;
1548a922f75Sshaneh 
1558a922f75Sshaneh /************************* Utility Routines *********************************/
1568a922f75Sshaneh /*
1578a922f75Sshaneh ** Acquire and release the mutex used to serialize access to the
1588a922f75Sshaneh ** list of multiplexGroups.
1598a922f75Sshaneh */
1608a922f75Sshaneh static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
1618a922f75Sshaneh static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
1628a922f75Sshaneh 
1638a922f75Sshaneh /* Translate an sqlite3_file* that is really a multiplexGroup* into
1648a922f75Sshaneh ** the sqlite3_file* for the underlying original VFS.
1658a922f75Sshaneh */
1668a922f75Sshaneh static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
1678a922f75Sshaneh   multiplexGroup *pGroup = pConn->pGroup;
1688a922f75Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
1698a922f75Sshaneh   if( iChunk<gMultiplex.nMaxChunks ){
1708a922f75Sshaneh     sqlite3_file *pSubOpen = pGroup->pReal[iChunk];    /* Real file descriptor */
1718a922f75Sshaneh     if( !pGroup->bOpen[iChunk] ){
172050d09abSshaneh       memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
173050d09abSshaneh       if( iChunk ){
174050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR
175050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
176050d09abSshaneh #else
177050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
178050d09abSshaneh #endif
179050d09abSshaneh       }
180050d09abSshaneh       *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags);
1818a922f75Sshaneh       if( *rc==SQLITE_OK ){
1828a922f75Sshaneh         pGroup->bOpen[iChunk] = -1;
1838a922f75Sshaneh         return pSubOpen;
1848a922f75Sshaneh       }
1858a922f75Sshaneh       return NULL;
1868a922f75Sshaneh     }
1878a922f75Sshaneh     *rc = SQLITE_OK;
1888a922f75Sshaneh     return pSubOpen;
1898a922f75Sshaneh   }
190b5830294Sshaneh   *rc = SQLITE_FULL;
1918a922f75Sshaneh   return NULL;
1928a922f75Sshaneh }
1938a922f75Sshaneh 
1948a922f75Sshaneh /************************* VFS Method Wrappers *****************************/
195b5830294Sshaneh 
1968a922f75Sshaneh /*
1978a922f75Sshaneh ** This is the xOpen method used for the "multiplex" VFS.
1988a922f75Sshaneh **
1998a922f75Sshaneh ** Most of the work is done by the underlying original VFS.  This method
2008a922f75Sshaneh ** simply links the new file into the appropriate multiplex group if it is a
2018a922f75Sshaneh ** file that needs to be tracked.
2028a922f75Sshaneh */
2038a922f75Sshaneh static int multiplexOpen(
2048a922f75Sshaneh   sqlite3_vfs *pVfs,         /* The multiplex VFS */
2058a922f75Sshaneh   const char *zName,         /* Name of file to be opened */
2068a922f75Sshaneh   sqlite3_file *pConn,       /* Fill in this file descriptor */
2078a922f75Sshaneh   int flags,                 /* Flags to control the opening */
2088a922f75Sshaneh   int *pOutFlags             /* Flags showing results of opening */
2098a922f75Sshaneh ){
2108a922f75Sshaneh   int rc;                                        /* Result code */
2118a922f75Sshaneh   multiplexConn *pMultiplexOpen;                 /* The new multiplex file descriptor */
2128a922f75Sshaneh   multiplexGroup *pGroup;                        /* Corresponding multiplexGroup object */
2138a922f75Sshaneh   sqlite3_file *pSubOpen;                        /* Real file descriptor */
2148a922f75Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
2158a922f75Sshaneh   int nName = sqlite3Strlen30(zName);
2168a922f75Sshaneh   int i;
217050d09abSshaneh   int sz;
2188a922f75Sshaneh 
2198a922f75Sshaneh   UNUSED_PARAMETER(pVfs);
2208a922f75Sshaneh 
2218a922f75Sshaneh   /* We need to create a group structure and manage
2228a922f75Sshaneh   ** access to this group of files.
2238a922f75Sshaneh   */
2248a922f75Sshaneh   multiplexEnter();
2258a922f75Sshaneh   pMultiplexOpen = (multiplexConn*)pConn;
226050d09abSshaneh   /* allocate space for group */
227050d09abSshaneh   sz = sizeof(multiplexGroup)                         /* multiplexGroup */
228050d09abSshaneh      + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
229050d09abSshaneh      + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks)     /* *pReal */
230050d09abSshaneh      + gMultiplex.nMaxChunks                          /* bOpen[] */
231050d09abSshaneh      + nName + 1;                                     /* zName */
232050d09abSshaneh #ifndef SQLITE_MULTIPLEX_EXT_OVWR
233050d09abSshaneh   sz += SQLITE_MULTIPLEX_EXT_SZ;
234050d09abSshaneh   assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
235050d09abSshaneh #else
236050d09abSshaneh   assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
237050d09abSshaneh   assert(nName < pOrigVfs->mxPathname);
238050d09abSshaneh #endif
239050d09abSshaneh   pGroup = sqlite3_malloc( sz );
2408a922f75Sshaneh   if( pGroup==0 ){
2418a922f75Sshaneh     rc=SQLITE_NOMEM;
2428a922f75Sshaneh   }else{
243050d09abSshaneh     /* assign pointers to extra space allocated */
244050d09abSshaneh     char *p = (char *)&pGroup[1];
2458a922f75Sshaneh     pMultiplexOpen->pGroup = pGroup;
246050d09abSshaneh     memset(pGroup, 0, sz);
247050d09abSshaneh     pGroup->pReal = (sqlite3_file **)p;
248050d09abSshaneh     p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
2498a922f75Sshaneh     for(i=0; i<gMultiplex.nMaxChunks; i++){
250050d09abSshaneh       pGroup->pReal[i] = (sqlite3_file *)p;
251050d09abSshaneh       p += pOrigVfs->szOsFile;
2528a922f75Sshaneh     }
253050d09abSshaneh     pGroup->bOpen = p;
254050d09abSshaneh     p += gMultiplex.nMaxChunks;
255050d09abSshaneh     pGroup->zName = p;
256050d09abSshaneh     /* save off base filename, name length, and original open flags  */
2578a922f75Sshaneh     memcpy(pGroup->zName, zName, nName+1);
2588a922f75Sshaneh     pGroup->nName = nName;
2598a922f75Sshaneh     pGroup->flags = flags;
2608a922f75Sshaneh     pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
2618a922f75Sshaneh     if( pSubOpen ){
2628a922f75Sshaneh       if( pSubOpen->pMethods->iVersion==1 ){
2638a922f75Sshaneh         pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
2648a922f75Sshaneh       }else{
2658a922f75Sshaneh         pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
2668a922f75Sshaneh       }
2678a922f75Sshaneh       /* place this group at the head of our list */
2688a922f75Sshaneh       pGroup->pNext = gMultiplex.pGroups;
2698a922f75Sshaneh       if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
2708a922f75Sshaneh       gMultiplex.pGroups = pGroup;
2718a922f75Sshaneh     }else{
2728a922f75Sshaneh       sqlite3_free(pGroup);
2738a922f75Sshaneh     }
2748a922f75Sshaneh   }
2758a922f75Sshaneh   multiplexLeave();
2768a922f75Sshaneh   return rc;
2778a922f75Sshaneh }
2788a922f75Sshaneh 
279b5830294Sshaneh /*
280b5830294Sshaneh ** This is the xDelete method used for the "multiplex" VFS.
281b5830294Sshaneh ** It attempts to delete the filename specified, as well
282050d09abSshaneh ** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension.
283b5830294Sshaneh */
284b5830294Sshaneh static int multiplexDelete(
285b5830294Sshaneh   sqlite3_vfs *pVfs,         /* The multiplex VFS */
286b5830294Sshaneh   const char *zName,         /* Name of file to delete */
287b5830294Sshaneh   int syncDir
288b5830294Sshaneh ){
289b5830294Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
290b5830294Sshaneh   int rc = SQLITE_OK;
291b5830294Sshaneh   int nName = sqlite3Strlen30(zName);
292b5830294Sshaneh   int i;
293b5830294Sshaneh 
294b5830294Sshaneh   UNUSED_PARAMETER(pVfs);
295b5830294Sshaneh 
296b5830294Sshaneh   multiplexEnter();
297b5830294Sshaneh   memcpy(gMultiplex.zName, zName, nName+1);
298b5830294Sshaneh   for(i=0; i<gMultiplex.nMaxChunks; i++){
299b5830294Sshaneh     int rc2;
300b5830294Sshaneh     int exists = 0;
301050d09abSshaneh     if( i ){
302050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR
303050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
304050d09abSshaneh #else
305050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
306050d09abSshaneh #endif
307050d09abSshaneh     }
308b5830294Sshaneh     rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
309b5830294Sshaneh     if( rc2==SQLITE_OK && exists){
310b5830294Sshaneh       /* if it exists, delete it */
311b5830294Sshaneh       rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
312b5830294Sshaneh       if( rc2!=SQLITE_OK ) rc = rc2;
313b5830294Sshaneh     }else{
314b5830294Sshaneh       /* stop at first "gap" */
315b5830294Sshaneh       break;
316b5830294Sshaneh     }
317b5830294Sshaneh   }
318b5830294Sshaneh   multiplexLeave();
319b5830294Sshaneh   return rc;
320b5830294Sshaneh }
321b5830294Sshaneh 
322fffadc52Sdan static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
323fffadc52Sdan   return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
324fffadc52Sdan }
325fffadc52Sdan static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
326fffadc52Sdan   return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
327fffadc52Sdan }
328fffadc52Sdan static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
329fffadc52Sdan   return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
330fffadc52Sdan }
331fffadc52Sdan static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
332fffadc52Sdan   gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
333fffadc52Sdan }
334fffadc52Sdan static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
335fffadc52Sdan   return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
336fffadc52Sdan }
337fffadc52Sdan static void multiplexDlClose(sqlite3_vfs *a, void *b){
338fffadc52Sdan   gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
339fffadc52Sdan }
340fffadc52Sdan static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
341fffadc52Sdan   return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
342fffadc52Sdan }
343fffadc52Sdan static int multiplexSleep(sqlite3_vfs *a, int b){
344fffadc52Sdan   return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
345fffadc52Sdan }
346fffadc52Sdan static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
347fffadc52Sdan   return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
348fffadc52Sdan }
349fffadc52Sdan static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
350fffadc52Sdan   return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
351fffadc52Sdan }
352fffadc52Sdan static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
353fffadc52Sdan   return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
354fffadc52Sdan }
355fffadc52Sdan 
3568a922f75Sshaneh /************************ I/O Method Wrappers *******************************/
3578a922f75Sshaneh 
3588a922f75Sshaneh /* xClose requests get passed through to the original VFS.
3598a922f75Sshaneh ** We loop over all open chunk handles and close them.
3608a922f75Sshaneh ** The group structure for this file is unlinked from
3618a922f75Sshaneh ** our list of groups and freed.
3628a922f75Sshaneh */
3638a922f75Sshaneh static int multiplexClose(sqlite3_file *pConn){
3648a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
3658a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
3668a922f75Sshaneh   int rc = SQLITE_OK;
3678a922f75Sshaneh   int i;
3688a922f75Sshaneh   multiplexEnter();
369b5830294Sshaneh   /* close any open handles */
3708a922f75Sshaneh   for(i=0; i<gMultiplex.nMaxChunks; i++){
3718a922f75Sshaneh     if( pGroup->bOpen[i] ){
3728a922f75Sshaneh       sqlite3_file *pSubOpen = pGroup->pReal[i];
3738a922f75Sshaneh       int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
3748a922f75Sshaneh       if( rc2!=SQLITE_OK ) rc = rc2;
3758a922f75Sshaneh       pGroup->bOpen[i] = 0;
3768a922f75Sshaneh     }
3778a922f75Sshaneh   }
378b5830294Sshaneh   /* remove from linked list */
3798a922f75Sshaneh   if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
3808a922f75Sshaneh   if( pGroup->pPrev ){
3818a922f75Sshaneh     pGroup->pPrev->pNext = pGroup->pNext;
3828a922f75Sshaneh   }else{
3838a922f75Sshaneh     gMultiplex.pGroups = pGroup->pNext;
3848a922f75Sshaneh   }
3858a922f75Sshaneh   sqlite3_free(pGroup);
3868a922f75Sshaneh   multiplexLeave();
3878a922f75Sshaneh   return rc;
3888a922f75Sshaneh }
3898a922f75Sshaneh 
3908a922f75Sshaneh /* Pass xRead requests thru to the original VFS after
3918a922f75Sshaneh ** determining the correct chunk to operate on.
392fd1552f2Sshaneh ** Break up reads across chunk boundaries.
3938a922f75Sshaneh */
3948a922f75Sshaneh static int multiplexRead(
3958a922f75Sshaneh   sqlite3_file *pConn,
3968a922f75Sshaneh   void *pBuf,
3978a922f75Sshaneh   int iAmt,
3988a922f75Sshaneh   sqlite3_int64 iOfst
3998a922f75Sshaneh ){
4008a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
4018a922f75Sshaneh   int rc = SQLITE_OK;
4028a922f75Sshaneh   multiplexEnter();
4038a922f75Sshaneh   while( iAmt > 0 ){
4048a922f75Sshaneh     int i = (int)(iOfst/gMultiplex.nChunkSize);
4058a922f75Sshaneh     sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
4068a922f75Sshaneh     if( pSubOpen ){
4078a922f75Sshaneh       int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
4088a922f75Sshaneh       if( extra<0 ) extra = 0;
4098a922f75Sshaneh       iAmt -= extra;
4108a922f75Sshaneh       rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
4118a922f75Sshaneh       if( rc!=SQLITE_OK ) break;
4128a922f75Sshaneh       pBuf = (char *)pBuf + iAmt;
4138a922f75Sshaneh       iOfst += iAmt;
4148a922f75Sshaneh       iAmt = extra;
4158a922f75Sshaneh     }else{
4168a922f75Sshaneh       rc = SQLITE_IOERR_READ;
4178a922f75Sshaneh       break;
4188a922f75Sshaneh     }
4198a922f75Sshaneh   }
4208a922f75Sshaneh   multiplexLeave();
4218a922f75Sshaneh   return rc;
4228a922f75Sshaneh }
4238a922f75Sshaneh 
4248a922f75Sshaneh /* Pass xWrite requests thru to the original VFS after
4258a922f75Sshaneh ** determining the correct chunk to operate on.
426fd1552f2Sshaneh ** Break up writes across chunk boundaries.
4278a922f75Sshaneh */
4288a922f75Sshaneh static int multiplexWrite(
4298a922f75Sshaneh   sqlite3_file *pConn,
4308a922f75Sshaneh   const void *pBuf,
4318a922f75Sshaneh   int iAmt,
4328a922f75Sshaneh   sqlite3_int64 iOfst
4338a922f75Sshaneh ){
4348a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
4358a922f75Sshaneh   int rc = SQLITE_OK;
4368a922f75Sshaneh   multiplexEnter();
4378a922f75Sshaneh   while( iAmt > 0 ){
4388a922f75Sshaneh     int i = (int)(iOfst/gMultiplex.nChunkSize);
4398a922f75Sshaneh     sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
4408a922f75Sshaneh     if( pSubOpen ){
4418a922f75Sshaneh       int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
4428a922f75Sshaneh       if( extra<0 ) extra = 0;
4438a922f75Sshaneh       iAmt -= extra;
4448a922f75Sshaneh       rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
4458a922f75Sshaneh       if( rc!=SQLITE_OK ) break;
4468a922f75Sshaneh       pBuf = (char *)pBuf + iAmt;
4478a922f75Sshaneh       iOfst += iAmt;
4488a922f75Sshaneh       iAmt = extra;
4498a922f75Sshaneh     }else{
4508a922f75Sshaneh       rc = SQLITE_IOERR_WRITE;
4518a922f75Sshaneh       break;
4528a922f75Sshaneh     }
4538a922f75Sshaneh   }
4548a922f75Sshaneh   multiplexLeave();
4558a922f75Sshaneh   return rc;
4568a922f75Sshaneh }
4578a922f75Sshaneh 
4588a922f75Sshaneh /* Pass xTruncate requests thru to the original VFS after
4598a922f75Sshaneh ** determining the correct chunk to operate on.  Delete any
4608a922f75Sshaneh ** chunks above the truncate mark.
4618a922f75Sshaneh */
4628a922f75Sshaneh static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
4638a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
4648a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
4658a922f75Sshaneh   int rc = SQLITE_OK;
4668a922f75Sshaneh   int rc2;
4678a922f75Sshaneh   int i;
4688a922f75Sshaneh   sqlite3_file *pSubOpen;
4698a922f75Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
4708a922f75Sshaneh   multiplexEnter();
471050d09abSshaneh   memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
4728a922f75Sshaneh   /* delete the chunks above the truncate limit */
4738a922f75Sshaneh   for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
474fd1552f2Sshaneh     /* close any open chunks before deleting them */
4758a922f75Sshaneh     if( pGroup->bOpen[i] ){
4768a922f75Sshaneh       pSubOpen = pGroup->pReal[i];
4778a922f75Sshaneh       rc2 = pSubOpen->pMethods->xClose(pSubOpen);
4788a922f75Sshaneh       if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
4790596beecSshaneh       pGroup->bOpen[i] = 0;
4808a922f75Sshaneh     }
481050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR
482050d09abSshaneh     sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
483050d09abSshaneh #else
484050d09abSshaneh     sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
485050d09abSshaneh #endif
486050d09abSshaneh     rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
4878a922f75Sshaneh     if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
4888a922f75Sshaneh   }
4898a922f75Sshaneh   pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
4908a922f75Sshaneh   if( pSubOpen ){
4918a922f75Sshaneh     rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
4928a922f75Sshaneh     if( rc2!=SQLITE_OK ) rc = rc2;
4938a922f75Sshaneh   }else{
4948a922f75Sshaneh     rc = SQLITE_IOERR_TRUNCATE;
4958a922f75Sshaneh   }
4968a922f75Sshaneh   multiplexLeave();
4978a922f75Sshaneh   return rc;
4988a922f75Sshaneh }
4998a922f75Sshaneh 
5008a922f75Sshaneh /* Pass xSync requests through to the original VFS without change
5018a922f75Sshaneh */
5028a922f75Sshaneh static int multiplexSync(sqlite3_file *pConn, int flags){
5038a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
5048a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
5058a922f75Sshaneh   int rc = SQLITE_OK;
5068a922f75Sshaneh   int i;
5078a922f75Sshaneh   multiplexEnter();
5088a922f75Sshaneh   for(i=0; i<gMultiplex.nMaxChunks; i++){
5098a922f75Sshaneh     /* if we don't have it open, we don't need to sync it */
5108a922f75Sshaneh     if( pGroup->bOpen[i] ){
5118a922f75Sshaneh       sqlite3_file *pSubOpen = pGroup->pReal[i];
5128a922f75Sshaneh       int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
5138a922f75Sshaneh       if( rc2!=SQLITE_OK ) rc = rc2;
5148a922f75Sshaneh     }
5158a922f75Sshaneh   }
5168a922f75Sshaneh   multiplexLeave();
5178a922f75Sshaneh   return rc;
5188a922f75Sshaneh }
5198a922f75Sshaneh 
5208a922f75Sshaneh /* Pass xFileSize requests through to the original VFS.
5218a922f75Sshaneh ** Aggregate the size of all the chunks before returning.
5228a922f75Sshaneh */
5238a922f75Sshaneh static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
5248a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
5258a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
5268a922f75Sshaneh   int rc = SQLITE_OK;
5278a922f75Sshaneh   int rc2;
5288a922f75Sshaneh   int i;
5298a922f75Sshaneh   multiplexEnter();
5308a922f75Sshaneh   *pSize = 0;
5318a922f75Sshaneh   for(i=0; i<gMultiplex.nMaxChunks; i++){
5328a922f75Sshaneh     sqlite3_file *pSubOpen = NULL;
5338a922f75Sshaneh     /* if not opened already, check to see if the chunk exists */
5348a922f75Sshaneh     if( pGroup->bOpen[i] ){
5358a922f75Sshaneh       pSubOpen = pGroup->pReal[i];
5368a922f75Sshaneh     }else{
5378a922f75Sshaneh       sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
5388a922f75Sshaneh       int exists = 0;
539050d09abSshaneh       memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
540050d09abSshaneh       if( i ){
541050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR
542050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
543050d09abSshaneh #else
544050d09abSshaneh         sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
545050d09abSshaneh #endif
546050d09abSshaneh       }
547050d09abSshaneh       rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
5488a922f75Sshaneh       if( rc2==SQLITE_OK && exists){
5498a922f75Sshaneh         /* if it exists, open it */
5508a922f75Sshaneh         pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
5518a922f75Sshaneh       }else{
5528a922f75Sshaneh         /* stop at first "gap" */
5538a922f75Sshaneh         break;
5548a922f75Sshaneh       }
5558a922f75Sshaneh     }
5568a922f75Sshaneh     if( pSubOpen ){
557fd1552f2Sshaneh       sqlite3_int64 sz;
5588a922f75Sshaneh       rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
5598a922f75Sshaneh       if( rc2!=SQLITE_OK ){
5608a922f75Sshaneh         rc = rc2;
5618a922f75Sshaneh       }else{
5628fcfc344Sshaneh         if( sz>gMultiplex.nChunkSize ){
5638fcfc344Sshaneh           rc = SQLITE_IOERR_FSTAT;
5648fcfc344Sshaneh         }
5658a922f75Sshaneh         *pSize += sz;
5668a922f75Sshaneh       }
5678a922f75Sshaneh     }else{
5688a922f75Sshaneh       break;
5698a922f75Sshaneh     }
5708a922f75Sshaneh   }
5718a922f75Sshaneh   multiplexLeave();
5728a922f75Sshaneh   return rc;
5738a922f75Sshaneh }
5748a922f75Sshaneh 
5758a922f75Sshaneh /* Pass xLock requests through to the original VFS unchanged.
5768a922f75Sshaneh */
5778a922f75Sshaneh static int multiplexLock(sqlite3_file *pConn, int lock){
5788a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
5798a922f75Sshaneh   int rc;
5808a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
5818a922f75Sshaneh   if( pSubOpen ){
5828a922f75Sshaneh     return pSubOpen->pMethods->xLock(pSubOpen, lock);
5838a922f75Sshaneh   }
5848a922f75Sshaneh   return SQLITE_BUSY;
5858a922f75Sshaneh }
5868a922f75Sshaneh 
5878a922f75Sshaneh /* Pass xUnlock requests through to the original VFS unchanged.
5888a922f75Sshaneh */
5898a922f75Sshaneh static int multiplexUnlock(sqlite3_file *pConn, int lock){
5908a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
5918a922f75Sshaneh   int rc;
5928a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
5938a922f75Sshaneh   if( pSubOpen ){
5948a922f75Sshaneh     return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
5958a922f75Sshaneh   }
5968a922f75Sshaneh   return SQLITE_IOERR_UNLOCK;
5978a922f75Sshaneh }
5988a922f75Sshaneh 
5998a922f75Sshaneh /* Pass xCheckReservedLock requests through to the original VFS unchanged.
6008a922f75Sshaneh */
6018a922f75Sshaneh static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
6028a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6038a922f75Sshaneh   int rc;
6048a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6058a922f75Sshaneh   if( pSubOpen ){
6068a922f75Sshaneh     return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
6078a922f75Sshaneh   }
6088a922f75Sshaneh   return SQLITE_IOERR_CHECKRESERVEDLOCK;
6098a922f75Sshaneh }
6108a922f75Sshaneh 
6118a922f75Sshaneh /* Pass xFileControl requests through to the original VFS unchanged.
6128a922f75Sshaneh */
6138a922f75Sshaneh static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
6148a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6158a922f75Sshaneh   int rc;
6168a922f75Sshaneh   sqlite3_file *pSubOpen;
6178a922f75Sshaneh   if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
6188a922f75Sshaneh   pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6198a922f75Sshaneh   if( pSubOpen ){
6208a922f75Sshaneh     return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
6218a922f75Sshaneh   }
6228a922f75Sshaneh   return SQLITE_ERROR;
6238a922f75Sshaneh }
6248a922f75Sshaneh 
6258a922f75Sshaneh /* Pass xSectorSize requests through to the original VFS unchanged.
6268a922f75Sshaneh */
6278a922f75Sshaneh static int multiplexSectorSize(sqlite3_file *pConn){
6288a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6298a922f75Sshaneh   int rc;
6308a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6318a922f75Sshaneh   if( pSubOpen ){
6328a922f75Sshaneh     return pSubOpen->pMethods->xSectorSize(pSubOpen);
6338a922f75Sshaneh   }
6348a922f75Sshaneh   return SQLITE_DEFAULT_SECTOR_SIZE;
6358a922f75Sshaneh }
6368a922f75Sshaneh 
6378a922f75Sshaneh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
6388a922f75Sshaneh */
6398a922f75Sshaneh static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
6408a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6418a922f75Sshaneh   int rc;
6428a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6438a922f75Sshaneh   if( pSubOpen ){
6448a922f75Sshaneh     return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
6458a922f75Sshaneh   }
6468a922f75Sshaneh   return 0;
6478a922f75Sshaneh }
6488a922f75Sshaneh 
6498a922f75Sshaneh /* Pass xShmMap requests through to the original VFS unchanged.
6508a922f75Sshaneh */
6518a922f75Sshaneh static int multiplexShmMap(
6528a922f75Sshaneh   sqlite3_file *pConn,            /* Handle open on database file */
6538a922f75Sshaneh   int iRegion,                    /* Region to retrieve */
6548a922f75Sshaneh   int szRegion,                   /* Size of regions */
6558a922f75Sshaneh   int bExtend,                    /* True to extend file if necessary */
6568a922f75Sshaneh   void volatile **pp              /* OUT: Mapped memory */
6578a922f75Sshaneh ){
6588a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6598a922f75Sshaneh   int rc;
6608a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6618a922f75Sshaneh   if( pSubOpen ){
6628a922f75Sshaneh     return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
6638a922f75Sshaneh   }
6648a922f75Sshaneh   return SQLITE_IOERR;
6658a922f75Sshaneh }
6668a922f75Sshaneh 
6678a922f75Sshaneh /* Pass xShmLock requests through to the original VFS unchanged.
6688a922f75Sshaneh */
6698a922f75Sshaneh static int multiplexShmLock(
6708a922f75Sshaneh   sqlite3_file *pConn,       /* Database file holding the shared memory */
6718a922f75Sshaneh   int ofst,                  /* First lock to acquire or release */
6728a922f75Sshaneh   int n,                     /* Number of locks to acquire or release */
6738a922f75Sshaneh   int flags                  /* What to do with the lock */
6748a922f75Sshaneh ){
6758a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6768a922f75Sshaneh   int rc;
6778a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6788a922f75Sshaneh   if( pSubOpen ){
6798a922f75Sshaneh     return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
6808a922f75Sshaneh   }
6818a922f75Sshaneh   return SQLITE_BUSY;
6828a922f75Sshaneh }
6838a922f75Sshaneh 
6848a922f75Sshaneh /* Pass xShmBarrier requests through to the original VFS unchanged.
6858a922f75Sshaneh */
6868a922f75Sshaneh static void multiplexShmBarrier(sqlite3_file *pConn){
6878a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6888a922f75Sshaneh   int rc;
6898a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
6908a922f75Sshaneh   if( pSubOpen ){
6918a922f75Sshaneh     pSubOpen->pMethods->xShmBarrier(pSubOpen);
6928a922f75Sshaneh   }
6938a922f75Sshaneh }
6948a922f75Sshaneh 
6958a922f75Sshaneh /* Pass xShmUnmap requests through to the original VFS unchanged.
6968a922f75Sshaneh */
6978a922f75Sshaneh static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
6988a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
6998a922f75Sshaneh   int rc;
7008a922f75Sshaneh   sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
7018a922f75Sshaneh   if( pSubOpen ){
7028a922f75Sshaneh     return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
7038a922f75Sshaneh   }
7048a922f75Sshaneh   return SQLITE_OK;
7058a922f75Sshaneh }
7068a922f75Sshaneh 
7078a922f75Sshaneh /************************** Public Interfaces *****************************/
7088a922f75Sshaneh /*
7098a922f75Sshaneh ** Initialize the multiplex VFS shim.  Use the VFS named zOrigVfsName
7108a922f75Sshaneh ** as the VFS that does the actual work.  Use the default if
7118a922f75Sshaneh ** zOrigVfsName==NULL.
7128a922f75Sshaneh **
7138a922f75Sshaneh ** The multiplex VFS shim is named "multiplex".  It will become the default
7148a922f75Sshaneh ** VFS if makeDefault is non-zero.
7158a922f75Sshaneh **
7168a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
7178a922f75Sshaneh ** during start-up.
7188a922f75Sshaneh */
7198a922f75Sshaneh int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
7208a922f75Sshaneh   sqlite3_vfs *pOrigVfs;
7218a922f75Sshaneh   if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
7228a922f75Sshaneh   pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
7238a922f75Sshaneh   if( pOrigVfs==0 ) return SQLITE_ERROR;
7248a922f75Sshaneh   assert( pOrigVfs!=&gMultiplex.sThisVfs );
7258a922f75Sshaneh   gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
7268a922f75Sshaneh   if( !gMultiplex.pMutex ){
7278a922f75Sshaneh     return SQLITE_NOMEM;
7288a922f75Sshaneh   }
729b5830294Sshaneh   gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname);
730b5830294Sshaneh   if( !gMultiplex.zName ){
731b5830294Sshaneh     sqlite3_mutex_free(gMultiplex.pMutex);
732b5830294Sshaneh     return SQLITE_NOMEM;
733b5830294Sshaneh   }
7348a922f75Sshaneh   gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
7358a922f75Sshaneh   gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
7368a922f75Sshaneh   gMultiplex.pGroups = NULL;
7378a922f75Sshaneh   gMultiplex.isInitialized = 1;
7388a922f75Sshaneh   gMultiplex.pOrigVfs = pOrigVfs;
7398a922f75Sshaneh   gMultiplex.sThisVfs = *pOrigVfs;
7408a922f75Sshaneh   gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
7418a922f75Sshaneh   gMultiplex.sThisVfs.zName = "multiplex";
7428a922f75Sshaneh   gMultiplex.sThisVfs.xOpen = multiplexOpen;
743b5830294Sshaneh   gMultiplex.sThisVfs.xDelete = multiplexDelete;
744fffadc52Sdan   gMultiplex.sThisVfs.xAccess = multiplexAccess;
745fffadc52Sdan   gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
746fffadc52Sdan   gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
747fffadc52Sdan   gMultiplex.sThisVfs.xDlError = multiplexDlError;
748fffadc52Sdan   gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
749fffadc52Sdan   gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
750fffadc52Sdan   gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
751fffadc52Sdan   gMultiplex.sThisVfs.xSleep = multiplexSleep;
752fffadc52Sdan   gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
753fffadc52Sdan   gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
754fffadc52Sdan   gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
755fffadc52Sdan 
7568a922f75Sshaneh   gMultiplex.sIoMethodsV1.iVersion = 1;
7578a922f75Sshaneh   gMultiplex.sIoMethodsV1.xClose = multiplexClose;
7588a922f75Sshaneh   gMultiplex.sIoMethodsV1.xRead = multiplexRead;
7598a922f75Sshaneh   gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
7608a922f75Sshaneh   gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
7618a922f75Sshaneh   gMultiplex.sIoMethodsV1.xSync = multiplexSync;
7628a922f75Sshaneh   gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
7638a922f75Sshaneh   gMultiplex.sIoMethodsV1.xLock = multiplexLock;
7648a922f75Sshaneh   gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
7658a922f75Sshaneh   gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
7668a922f75Sshaneh   gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
7678a922f75Sshaneh   gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
7688a922f75Sshaneh   gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
7698a922f75Sshaneh   gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
7708a922f75Sshaneh   gMultiplex.sIoMethodsV2.iVersion = 2;
7718a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
7728a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
7738a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
7748a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
7758a922f75Sshaneh   sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
7768a922f75Sshaneh   return SQLITE_OK;
7778a922f75Sshaneh }
7788a922f75Sshaneh 
7798a922f75Sshaneh /*
7808a922f75Sshaneh ** Shutdown the multiplex system.
7818a922f75Sshaneh **
7828a922f75Sshaneh ** All SQLite database connections must be closed before calling this
7838a922f75Sshaneh ** routine.
7848a922f75Sshaneh **
7858a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
7868a922f75Sshaneh ** shutting down in order to free all remaining multiplex groups.
7878a922f75Sshaneh */
7888a922f75Sshaneh int sqlite3_multiplex_shutdown(void){
7898a922f75Sshaneh   if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
7908a922f75Sshaneh   if( gMultiplex.pGroups ) return SQLITE_MISUSE;
7918a922f75Sshaneh   gMultiplex.isInitialized = 0;
792b5830294Sshaneh   sqlite3_free(gMultiplex.zName);
7938a922f75Sshaneh   sqlite3_mutex_free(gMultiplex.pMutex);
7948a922f75Sshaneh   sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
7958a922f75Sshaneh   memset(&gMultiplex, 0, sizeof(gMultiplex));
7968a922f75Sshaneh   return SQLITE_OK;
7978a922f75Sshaneh }
7988a922f75Sshaneh 
7998a922f75Sshaneh /*
8008a922f75Sshaneh ** Adjust chunking params.  VFS should be initialized first.
8018a922f75Sshaneh ** No files should be open.  Re-intializing will reset these
8028a922f75Sshaneh ** to the default.
8038a922f75Sshaneh */
8048a922f75Sshaneh int sqlite3_multiplex_set(
8058a922f75Sshaneh   int nChunkSize,                 /* Max chunk size */
8068a922f75Sshaneh   int nMaxChunks                  /* Max number of chunks */
8078a922f75Sshaneh ){
8088a922f75Sshaneh   if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
8098a922f75Sshaneh   if( gMultiplex.pGroups ) return SQLITE_MISUSE;
810e5a6ad6dSshaneh   if( nChunkSize<32 ) return SQLITE_MISUSE;
811e5a6ad6dSshaneh   if( nMaxChunks<1 ) return SQLITE_MISUSE;
812050d09abSshaneh   if( nMaxChunks>99 ) return SQLITE_MISUSE;
8138a922f75Sshaneh   multiplexEnter();
8148a922f75Sshaneh   gMultiplex.nChunkSize = nChunkSize;
8158a922f75Sshaneh   gMultiplex.nMaxChunks = nMaxChunks;
8168a922f75Sshaneh   multiplexLeave();
8178a922f75Sshaneh   return SQLITE_OK;
8188a922f75Sshaneh }
8198a922f75Sshaneh 
8208a922f75Sshaneh /***************************** Test Code ***********************************/
8218a922f75Sshaneh #ifdef SQLITE_TEST
8228a922f75Sshaneh #include <tcl.h>
8238a922f75Sshaneh 
8248a922f75Sshaneh extern const char *sqlite3TestErrorName(int);
8258a922f75Sshaneh 
8268a922f75Sshaneh 
8278a922f75Sshaneh /*
8288a922f75Sshaneh ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
8298a922f75Sshaneh */
8308a922f75Sshaneh static int test_multiplex_initialize(
8318a922f75Sshaneh   void * clientData,
8328a922f75Sshaneh   Tcl_Interp *interp,
8338a922f75Sshaneh   int objc,
8348a922f75Sshaneh   Tcl_Obj *CONST objv[]
8358a922f75Sshaneh ){
8368a922f75Sshaneh   const char *zName;              /* Name of new multiplex VFS */
8378a922f75Sshaneh   int makeDefault;                /* True to make the new VFS the default */
8388a922f75Sshaneh   int rc;                         /* Value returned by multiplex_initialize() */
8398a922f75Sshaneh 
8408a922f75Sshaneh   UNUSED_PARAMETER(clientData);
8418a922f75Sshaneh 
8428a922f75Sshaneh   /* Process arguments */
8438a922f75Sshaneh   if( objc!=3 ){
8448a922f75Sshaneh     Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
8458a922f75Sshaneh     return TCL_ERROR;
8468a922f75Sshaneh   }
8478a922f75Sshaneh   zName = Tcl_GetString(objv[1]);
8488a922f75Sshaneh   if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
8498a922f75Sshaneh   if( zName[0]=='\0' ) zName = 0;
8508a922f75Sshaneh 
8518a922f75Sshaneh   /* Call sqlite3_multiplex_initialize() */
8528a922f75Sshaneh   rc = sqlite3_multiplex_initialize(zName, makeDefault);
8538a922f75Sshaneh   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
8548a922f75Sshaneh 
8558a922f75Sshaneh   return TCL_OK;
8568a922f75Sshaneh }
8578a922f75Sshaneh 
8588a922f75Sshaneh /*
8598a922f75Sshaneh ** tclcmd: sqlite3_multiplex_shutdown
8608a922f75Sshaneh */
8618a922f75Sshaneh static int test_multiplex_shutdown(
8628a922f75Sshaneh   void * clientData,
8638a922f75Sshaneh   Tcl_Interp *interp,
8648a922f75Sshaneh   int objc,
8658a922f75Sshaneh   Tcl_Obj *CONST objv[]
8668a922f75Sshaneh ){
8678a922f75Sshaneh   int rc;                         /* Value returned by multiplex_shutdown() */
8688a922f75Sshaneh 
8698a922f75Sshaneh   UNUSED_PARAMETER(clientData);
8708a922f75Sshaneh 
8718a922f75Sshaneh   if( objc!=1 ){
8728a922f75Sshaneh     Tcl_WrongNumArgs(interp, 1, objv, "");
8738a922f75Sshaneh     return TCL_ERROR;
8748a922f75Sshaneh   }
8758a922f75Sshaneh 
8768a922f75Sshaneh   /* Call sqlite3_multiplex_shutdown() */
8778a922f75Sshaneh   rc = sqlite3_multiplex_shutdown();
8788a922f75Sshaneh   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
8798a922f75Sshaneh 
8808a922f75Sshaneh   return TCL_OK;
8818a922f75Sshaneh }
8828a922f75Sshaneh 
8838a922f75Sshaneh /*
8848a922f75Sshaneh ** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
8858a922f75Sshaneh */
8868a922f75Sshaneh static int test_multiplex_set(
8878a922f75Sshaneh   void * clientData,
8888a922f75Sshaneh   Tcl_Interp *interp,
8898a922f75Sshaneh   int objc,
8908a922f75Sshaneh   Tcl_Obj *CONST objv[]
8918a922f75Sshaneh ){
8928a922f75Sshaneh   int nChunkSize;                 /* Max chunk size */
8938a922f75Sshaneh   int nMaxChunks;                 /* Max number of chunks */
8948a922f75Sshaneh   int rc;                         /* Value returned by sqlite3_multiplex_set() */
8958a922f75Sshaneh 
8968a922f75Sshaneh   UNUSED_PARAMETER(clientData);
8978a922f75Sshaneh 
8988a922f75Sshaneh   /* Process arguments */
8998a922f75Sshaneh   if( objc!=3 ){
9008a922f75Sshaneh     Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
9018a922f75Sshaneh     return TCL_ERROR;
9028a922f75Sshaneh   }
9038a922f75Sshaneh   if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
9048a922f75Sshaneh   if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
9058a922f75Sshaneh 
9068a922f75Sshaneh   /* Invoke sqlite3_multiplex_set() */
9078a922f75Sshaneh   rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
9088a922f75Sshaneh 
9098a922f75Sshaneh   Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
9108a922f75Sshaneh   return TCL_OK;
9118a922f75Sshaneh }
9128a922f75Sshaneh 
9138a922f75Sshaneh /*
9148a922f75Sshaneh ** tclcmd:  sqlite3_multiplex_dump
9158a922f75Sshaneh */
9168a922f75Sshaneh static int test_multiplex_dump(
9178a922f75Sshaneh   void * clientData,
9188a922f75Sshaneh   Tcl_Interp *interp,
9198a922f75Sshaneh   int objc,
9208a922f75Sshaneh   Tcl_Obj *CONST objv[]
9218a922f75Sshaneh ){
9228a922f75Sshaneh   Tcl_Obj *pResult;
9238a922f75Sshaneh   Tcl_Obj *pGroupTerm;
9248a922f75Sshaneh   multiplexGroup *pGroup;
9258a922f75Sshaneh   int i;
9268a922f75Sshaneh   int nChunks = 0;
9278a922f75Sshaneh 
9288a922f75Sshaneh   UNUSED_PARAMETER(clientData);
9298a922f75Sshaneh   UNUSED_PARAMETER(objc);
9308a922f75Sshaneh   UNUSED_PARAMETER(objv);
9318a922f75Sshaneh 
9328a922f75Sshaneh   pResult = Tcl_NewObj();
9338a922f75Sshaneh   multiplexEnter();
9348a922f75Sshaneh   for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
9358a922f75Sshaneh     pGroupTerm = Tcl_NewObj();
9368a922f75Sshaneh 
9378a922f75Sshaneh     pGroup->zName[pGroup->nName] = '\0';
9388a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9398a922f75Sshaneh           Tcl_NewStringObj(pGroup->zName, -1));
9408a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9418a922f75Sshaneh           Tcl_NewIntObj(pGroup->nName));
9428a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9438a922f75Sshaneh           Tcl_NewIntObj(pGroup->flags));
9448a922f75Sshaneh 
9458a922f75Sshaneh     /* count number of chunks with open handles */
9468a922f75Sshaneh     for(i=0; i<gMultiplex.nMaxChunks; i++){
9478a922f75Sshaneh       if( pGroup->bOpen[i] ) nChunks++;
9488a922f75Sshaneh     }
9498a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9508a922f75Sshaneh           Tcl_NewIntObj(nChunks));
9518a922f75Sshaneh 
9528a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9538a922f75Sshaneh           Tcl_NewIntObj(gMultiplex.nChunkSize));
9548a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
9558a922f75Sshaneh           Tcl_NewIntObj(gMultiplex.nMaxChunks));
9568a922f75Sshaneh 
9578a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
9588a922f75Sshaneh   }
9598a922f75Sshaneh   multiplexLeave();
9608a922f75Sshaneh   Tcl_SetObjResult(interp, pResult);
9618a922f75Sshaneh   return TCL_OK;
9628a922f75Sshaneh }
9638a922f75Sshaneh 
9648a922f75Sshaneh /*
9658a922f75Sshaneh ** This routine registers the custom TCL commands defined in this
9668a922f75Sshaneh ** module.  This should be the only procedure visible from outside
9678a922f75Sshaneh ** of this module.
9688a922f75Sshaneh */
9698a922f75Sshaneh int Sqlitemultiplex_Init(Tcl_Interp *interp){
9708a922f75Sshaneh   static struct {
9718a922f75Sshaneh      char *zName;
9728a922f75Sshaneh      Tcl_ObjCmdProc *xProc;
9738a922f75Sshaneh   } aCmd[] = {
9748a922f75Sshaneh     { "sqlite3_multiplex_initialize", test_multiplex_initialize },
9758a922f75Sshaneh     { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
9768a922f75Sshaneh     { "sqlite3_multiplex_set", test_multiplex_set },
9778a922f75Sshaneh     { "sqlite3_multiplex_dump", test_multiplex_dump },
9788a922f75Sshaneh   };
9798a922f75Sshaneh   int i;
9808a922f75Sshaneh 
9818a922f75Sshaneh   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
9828a922f75Sshaneh     Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
9838a922f75Sshaneh   }
9848a922f75Sshaneh 
9858a922f75Sshaneh   return TCL_OK;
9868a922f75Sshaneh }
9878a922f75Sshaneh #endif
988