xref: /sqlite-3.40.0/src/test_multiplex.c (revision 8fcfc344)
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