xref: /sqlite-3.40.0/src/test_multiplex.c (revision f3cdcdcc)
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
14a12b6fa3Sdrh ** pager and the real VFS - that breaks up a very large database file
15a12b6fa3Sdrh ** into two or more smaller files on disk.  This is useful, for example,
16a12b6fa3Sdrh ** in order to support large, multi-gigabyte databases on older filesystems
17a12b6fa3Sdrh ** that limit the maximum file size to 2 GiB.
188a922f75Sshaneh **
19a12b6fa3Sdrh ** USAGE:
208a922f75Sshaneh **
21a12b6fa3Sdrh ** Compile this source file and link it with your application.  Then
22a12b6fa3Sdrh ** at start-time, invoke the following procedure:
23a12b6fa3Sdrh **
24a12b6fa3Sdrh **   int sqlite3_multiplex_initialize(
25a12b6fa3Sdrh **      const char *zOrigVfsName,    // The underlying real VFS
26a12b6fa3Sdrh **      int makeDefault              // True to make multiplex the default VFS
27a12b6fa3Sdrh **   );
28a12b6fa3Sdrh **
29a12b6fa3Sdrh ** The procedure call above will create and register a new VFS shim named
30a12b6fa3Sdrh ** "multiplex".  The multiplex VFS will use the VFS named by zOrigVfsName to
31a12b6fa3Sdrh ** do the actual disk I/O.  (The zOrigVfsName parameter may be NULL, in
32a12b6fa3Sdrh ** which case the default VFS at the moment sqlite3_multiplex_initialize()
33a12b6fa3Sdrh ** is called will be used as the underlying real VFS.)
34a12b6fa3Sdrh **
35a12b6fa3Sdrh ** If the makeDefault parameter is TRUE then multiplex becomes the new
36a12b6fa3Sdrh ** default VFS.  Otherwise, you can use the multiplex VFS by specifying
37a12b6fa3Sdrh ** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing
38a12b6fa3Sdrh ** URI filenames and adding "vfs=multiplex" as a parameter to the filename
39a12b6fa3Sdrh ** URI.
40a12b6fa3Sdrh **
41a12b6fa3Sdrh ** The multiplex VFS allows databases up to 32 GiB in size.  But it splits
42ad2148daSdrh ** the files up into smaller pieces, so that they will work even on
43ad2148daSdrh ** filesystems that do not support large files.  The default chunk size
44ad2148daSdrh ** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be
45ad2148daSdrh ** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE
46ad2148daSdrh ** macro.  Use the "chunksize=NNNN" query parameter with a URI filename
47ad2148daSdrh ** in order to select an alternative chunk size for individual connections
48ad2148daSdrh ** at run-time.
498a922f75Sshaneh */
508a922f75Sshaneh #include "sqlite3.h"
518a922f75Sshaneh #include <string.h>
528a922f75Sshaneh #include <assert.h>
536f25e89dSdrh #include <stdlib.h>
54d50deeebSshaneh #include "test_multiplex.h"
55d50deeebSshaneh 
5678c4de4cSshaneh #ifndef SQLITE_CORE
5778c4de4cSshaneh   #define SQLITE_CORE 1  /* Disable the API redefinition in sqlite3ext.h */
5878c4de4cSshaneh #endif
5978c4de4cSshaneh #include "sqlite3ext.h"
6078c4de4cSshaneh 
61bb201344Sshaneh /*
623801b65dSshaneh ** These should be defined to be the same as the values in
6348864df9Smistachkin ** sqliteInt.h.  They are defined separately here so that
643801b65dSshaneh ** the multiplex VFS shim can be built as a loadable
653801b65dSshaneh ** module.
663801b65dSshaneh */
673801b65dSshaneh #define UNUSED_PARAMETER(x) (void)(x)
683801b65dSshaneh #define MAX_PAGE_SIZE       0x10000
693801b65dSshaneh #define DEFAULT_SECTOR_SIZE 0x1000
703801b65dSshaneh 
713801b65dSshaneh /*
72bb201344Sshaneh ** For a build without mutexes, no-op the mutex calls.
73bb201344Sshaneh */
74bb201344Sshaneh #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
75bb201344Sshaneh #define sqlite3_mutex_alloc(X)    ((sqlite3_mutex*)8)
76bb201344Sshaneh #define sqlite3_mutex_free(X)
77bb201344Sshaneh #define sqlite3_mutex_enter(X)
78bb201344Sshaneh #define sqlite3_mutex_try(X)      SQLITE_OK
79bb201344Sshaneh #define sqlite3_mutex_leave(X)
80bb201344Sshaneh #define sqlite3_mutex_held(X)     ((void)(X),1)
81bb201344Sshaneh #define sqlite3_mutex_notheld(X)  ((void)(X),1)
82bb201344Sshaneh #endif /* SQLITE_THREADSAFE==0 */
83bb201344Sshaneh 
845fb96aafSdrh /* Maximum chunk number */
855fb96aafSdrh #define MX_CHUNK_NUMBER 299
865fb96aafSdrh 
8743a6d4bdSdrh /* First chunk for rollback journal files */
8827e69643Sdan #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
895fb96aafSdrh #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
9027e69643Sdan 
91bb201344Sshaneh 
928a922f75Sshaneh /************************ Shim Definitions ******************************/
938a922f75Sshaneh 
946f25e89dSdrh #ifndef SQLITE_MULTIPLEX_VFS_NAME
95d9523b97Sshaneh # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
966f25e89dSdrh #endif
97d9523b97Sshaneh 
98050d09abSshaneh /* This is the limit on the chunk size.  It may be changed by calling
9978c4de4cSshaneh ** the xFileControl() interface.  It will be rounded up to a
1006f25e89dSdrh ** multiple of MAX_PAGE_SIZE.  We default it here to 2GiB less 64KiB.
101050d09abSshaneh */
1026f25e89dSdrh #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
1036f25e89dSdrh # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
1046f25e89dSdrh #endif
105c27fa4b0Sshaneh 
10673795becSdrh /* This used to be the default limit on number of chunks, but
10773795becSdrh ** it is no longer enforced. There is currently no limit to the
10873795becSdrh ** number of chunks.
10973795becSdrh **
11073795becSdrh ** May be changed by calling the xFileControl() interface.
111050d09abSshaneh */
1126f25e89dSdrh #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
11373795becSdrh # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
1146f25e89dSdrh #endif
115050d09abSshaneh 
1168a922f75Sshaneh /************************ Object Definitions ******************************/
1178a922f75Sshaneh 
1188a922f75Sshaneh /* Forward declaration of all object types */
1198a922f75Sshaneh typedef struct multiplexGroup multiplexGroup;
1208a922f75Sshaneh typedef struct multiplexConn multiplexConn;
1218a922f75Sshaneh 
1228a922f75Sshaneh /*
1238a922f75Sshaneh ** A "multiplex group" is a collection of files that collectively
1248a922f75Sshaneh ** makeup a single SQLite DB file.  This allows the size of the DB
1258a922f75Sshaneh ** to exceed the limits imposed by the file system.
1268a922f75Sshaneh **
1278a922f75Sshaneh ** There is an instance of the following object for each defined multiplex
1288a922f75Sshaneh ** group.
1298a922f75Sshaneh */
1308a922f75Sshaneh struct multiplexGroup {
131f3717af4Sdrh   struct multiplexReal {           /* For each chunk */
132f3717af4Sdrh     sqlite3_file *p;                  /* Handle for the chunk */
133f3717af4Sdrh     char *z;                          /* Name of this chunk */
134f3717af4Sdrh   } *aReal;                        /* list of all chunks */
135f3717af4Sdrh   int nReal;                       /* Number of chunks */
1368a922f75Sshaneh   char *zName;                     /* Base filename of this group */
1378a922f75Sshaneh   int nName;                       /* Length of base filename */
1388a922f75Sshaneh   int flags;                       /* Flags used for original opening */
139e6deb204Sdrh   unsigned int szChunk;            /* Chunk size used for this group */
140e7d9f13dSdrh   unsigned char bEnabled;          /* TRUE to use Multiplex VFS for this file */
141e7d9f13dSdrh   unsigned char bTruncate;         /* TRUE to enable truncation of databases */
1428a922f75Sshaneh   multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
1438a922f75Sshaneh };
1448a922f75Sshaneh 
1458a922f75Sshaneh /*
1468a922f75Sshaneh ** An instance of the following object represents each open connection
1478a922f75Sshaneh ** to a file that is multiplex'ed.  This object is a
1488a922f75Sshaneh ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
1498a922f75Sshaneh ** VFS is appended to this structure.
1508a922f75Sshaneh */
1518a922f75Sshaneh struct multiplexConn {
1528a922f75Sshaneh   sqlite3_file base;              /* Base class - must be first */
1538a922f75Sshaneh   multiplexGroup *pGroup;         /* The underlying group of files */
1548a922f75Sshaneh };
1558a922f75Sshaneh 
1568a922f75Sshaneh /************************* Global Variables **********************************/
1578a922f75Sshaneh /*
1588a922f75Sshaneh ** All global variables used by this file are containing within the following
1598a922f75Sshaneh ** gMultiplex structure.
1608a922f75Sshaneh */
1618a922f75Sshaneh static struct {
1628a922f75Sshaneh   /* The pOrigVfs is the real, original underlying VFS implementation.
1638a922f75Sshaneh   ** Most operations pass-through to the real VFS.  This value is read-only
1648a922f75Sshaneh   ** during operation.  It is only modified at start-time and thus does not
1658a922f75Sshaneh   ** require a mutex.
1668a922f75Sshaneh   */
1678a922f75Sshaneh   sqlite3_vfs *pOrigVfs;
1688a922f75Sshaneh 
1698a922f75Sshaneh   /* The sThisVfs is the VFS structure used by this shim.  It is initialized
1708a922f75Sshaneh   ** at start-time and thus does not require a mutex
1718a922f75Sshaneh   */
1728a922f75Sshaneh   sqlite3_vfs sThisVfs;
1738a922f75Sshaneh 
1748a922f75Sshaneh   /* The sIoMethods defines the methods used by sqlite3_file objects
1758a922f75Sshaneh   ** associated with this shim.  It is initialized at start-time and does
1768a922f75Sshaneh   ** not require a mutex.
1778a922f75Sshaneh   **
1788a922f75Sshaneh   ** When the underlying VFS is called to open a file, it might return
1798a922f75Sshaneh   ** either a version 1 or a version 2 sqlite3_file object.  This shim
1808a922f75Sshaneh   ** has to create a wrapper sqlite3_file of the same version.  Hence
1818a922f75Sshaneh   ** there are two I/O method structures, one for version 1 and the other
1828a922f75Sshaneh   ** for version 2.
1838a922f75Sshaneh   */
1848a922f75Sshaneh   sqlite3_io_methods sIoMethodsV1;
1858a922f75Sshaneh   sqlite3_io_methods sIoMethodsV2;
1868a922f75Sshaneh 
187fd1552f2Sshaneh   /* True when this shim has been initialized.
1888a922f75Sshaneh   */
1898a922f75Sshaneh   int isInitialized;
1908a922f75Sshaneh 
1918a922f75Sshaneh   /* For run-time access any of the other global data structures in this
1928a922f75Sshaneh   ** shim, the following mutex must be held.
1938a922f75Sshaneh   */
1948a922f75Sshaneh   sqlite3_mutex *pMutex;
1958a922f75Sshaneh 
1968a922f75Sshaneh   /* List of multiplexGroup objects.
1978a922f75Sshaneh   */
1988a922f75Sshaneh   multiplexGroup *pGroups;
1998a922f75Sshaneh } gMultiplex;
2008a922f75Sshaneh 
2018a922f75Sshaneh /************************* Utility Routines *********************************/
2028a922f75Sshaneh /*
2038a922f75Sshaneh ** Acquire and release the mutex used to serialize access to the
2048a922f75Sshaneh ** list of multiplexGroups.
2058a922f75Sshaneh */
2068a922f75Sshaneh static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
2078a922f75Sshaneh static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
2088a922f75Sshaneh 
2093801b65dSshaneh /*
2103801b65dSshaneh ** Compute a string length that is limited to what can be stored in
2113801b65dSshaneh ** lower 30 bits of a 32-bit signed integer.
2123801b65dSshaneh **
2133801b65dSshaneh ** The value returned will never be negative.  Nor will it ever be greater
2143801b65dSshaneh ** than the actual length of the string.  For very long strings (greater
2153801b65dSshaneh ** than 1GiB) the value returned might be less than the true string length.
2163801b65dSshaneh */
217cc4e19beSshaneh static int multiplexStrlen30(const char *z){
2183801b65dSshaneh   const char *z2 = z;
2193801b65dSshaneh   if( z==0 ) return 0;
2203801b65dSshaneh   while( *z2 ){ z2++; }
2213801b65dSshaneh   return 0x3fffffff & (int)(z2 - z);
2223801b65dSshaneh }
2233801b65dSshaneh 
224e712b582Sdan /*
225e712b582Sdan ** Generate the file-name for chunk iChunk of the group with base name
226e712b582Sdan ** zBase. The file-name is written to buffer zOut before returning. Buffer
227730f85a3Sdan ** zOut must be allocated by the caller so that it is at least (nBase+5)
228e712b582Sdan ** bytes in size, where nBase is the length of zBase, not including the
229e712b582Sdan ** nul-terminator.
230730f85a3Sdan **
231730f85a3Sdan ** If iChunk is 0 (or 400 - the number for the first journal file chunk),
232730f85a3Sdan ** the output is a copy of the input string. Otherwise, if
233730f85a3Sdan ** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
234730f85a3Sdan ** a "." character, then the output is a copy of the input string with the
235730f85a3Sdan ** three-digit zero-padded decimal representation if iChunk appended to it.
236730f85a3Sdan ** For example:
237730f85a3Sdan **
238730f85a3Sdan **   zBase="test.db", iChunk=4  ->  zOut="test.db004"
239730f85a3Sdan **
240730f85a3Sdan ** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
241730f85a3Sdan ** a "." character, then everything after the "." is replaced by the
242730f85a3Sdan ** three-digit representation of iChunk.
243730f85a3Sdan **
244730f85a3Sdan **   zBase="test.db", iChunk=4  ->  zOut="test.004"
245730f85a3Sdan **
246730f85a3Sdan ** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
247730f85a3Sdan ** to pass to sqlite3_uri_parameter() and similar.
248e712b582Sdan */
249e712b582Sdan static void multiplexFilename(
250e712b582Sdan   const char *zBase,              /* Filename for chunk 0 */
251e712b582Sdan   int nBase,                      /* Size of zBase in bytes (without \0) */
252e712b582Sdan   int flags,                      /* Flags used to open file */
253e712b582Sdan   int iChunk,                     /* Chunk to generate filename for */
254e712b582Sdan   char *zOut                      /* Buffer to write generated name to */
255e712b582Sdan ){
256e712b582Sdan   int n = nBase;
257730f85a3Sdan   memcpy(zOut, zBase, n+1);
2585fb96aafSdrh   if( iChunk!=0 && iChunk<=MX_CHUNK_NUMBER ){
259e712b582Sdan #ifdef SQLITE_ENABLE_8_3_NAMES
260e712b582Sdan     int i;
261e712b582Sdan     for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
262e712b582Sdan     if( i>=n-4 ) n = i+1;
2635fb96aafSdrh     if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
264e712b582Sdan       /* The extensions on overflow files for main databases are 001, 002,
265e712b582Sdan       ** 003 and so forth.  To avoid name collisions, add 400 to the
266e712b582Sdan       ** extensions of journal files so that they are 401, 402, 403, ....
267e712b582Sdan       */
268e712b582Sdan       iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
2695fb96aafSdrh     }else if( flags & SQLITE_OPEN_WAL ){
2705fb96aafSdrh       /* To avoid name collisions, add 700 to the
2715fb96aafSdrh       ** extensions of WAL files so that they are 701, 702, 703, ....
2725fb96aafSdrh       */
2735fb96aafSdrh       iChunk += SQLITE_MULTIPLEX_WAL_8_3_OFFSET;
274e712b582Sdan     }
275e712b582Sdan #endif
276e712b582Sdan     sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
277730f85a3Sdan     n += 3;
278e712b582Sdan   }
279730f85a3Sdan 
280730f85a3Sdan   assert( zOut[n]=='\0' );
281730f85a3Sdan   zOut[n+1] = '\0';
282e712b582Sdan }
283e712b582Sdan 
284f3717af4Sdrh /* Compute the filename for the iChunk-th chunk
285f3717af4Sdrh */
286f3717af4Sdrh static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
287f3717af4Sdrh   if( iChunk>=pGroup->nReal ){
288f3717af4Sdrh     struct multiplexReal *p;
289*f3cdcdccSdrh     p = sqlite3_realloc64(pGroup->aReal, (iChunk+1)*sizeof(*p));
290f3717af4Sdrh     if( p==0 ){
291f3717af4Sdrh       return SQLITE_NOMEM;
292f3717af4Sdrh     }
293f3717af4Sdrh     memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
294f3717af4Sdrh     pGroup->aReal = p;
295f3717af4Sdrh     pGroup->nReal = iChunk+1;
296f3717af4Sdrh   }
2975b1626aaSdrh   if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
298f3717af4Sdrh     char *z;
299f3717af4Sdrh     int n = pGroup->nName;
300*f3cdcdccSdrh     pGroup->aReal[iChunk].z = z = sqlite3_malloc64( n+5 );
301f3717af4Sdrh     if( z==0 ){
302f3717af4Sdrh       return SQLITE_NOMEM;
303f3717af4Sdrh     }
304e712b582Sdan     multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
305f3717af4Sdrh   }
306f3717af4Sdrh   return SQLITE_OK;
307f3717af4Sdrh }
308f3717af4Sdrh 
3098a922f75Sshaneh /* Translate an sqlite3_file* that is really a multiplexGroup* into
3108a922f75Sshaneh ** the sqlite3_file* for the underlying original VFS.
311e7d9f13dSdrh **
312e7d9f13dSdrh ** For chunk 0, the pGroup->flags determines whether or not a new file
313e7d9f13dSdrh ** is created if it does not already exist.  For chunks 1 and higher, the
314e7d9f13dSdrh ** file is created only if createFlag is 1.
3158a922f75Sshaneh */
3168efdb732Sdrh static sqlite3_file *multiplexSubOpen(
317e7d9f13dSdrh   multiplexGroup *pGroup,    /* The multiplexor group */
318e7d9f13dSdrh   int iChunk,                /* Which chunk to open.  0==original file */
319e7d9f13dSdrh   int *rc,                   /* Result code in and out */
320e7d9f13dSdrh   int *pOutFlags,            /* Output flags */
321e7d9f13dSdrh   int createFlag             /* True to create if iChunk>0 */
3228efdb732Sdrh ){
323f3717af4Sdrh   sqlite3_file *pSubOpen = 0;
3248a922f75Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
32527e69643Sdan 
32627e69643Sdan #ifdef SQLITE_ENABLE_8_3_NAMES
32743a6d4bdSdrh   /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
32843a6d4bdSdrh   ** part of a database journal are named db.401, db.402, and so on. A
32943a6d4bdSdrh   ** database may therefore not grow to larger than 400 chunks. Attempting
33043a6d4bdSdrh   ** to open chunk 401 indicates the database is full. */
33127e69643Sdan   if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
332689453dbSdrh     sqlite3_log(SQLITE_FULL, "multiplexed chunk overflow: %s", pGroup->zName);
33327e69643Sdan     *rc = SQLITE_FULL;
33427e69643Sdan     return 0;
33527e69643Sdan   }
33627e69643Sdan #endif
33727e69643Sdan 
338f3717af4Sdrh   *rc = multiplexSubFilename(pGroup, iChunk);
339f3717af4Sdrh   if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
34006999667Sdrh     int flags, bExists;
341e7d9f13dSdrh     flags = pGroup->flags;
342e7d9f13dSdrh     if( createFlag ){
343e7d9f13dSdrh       flags |= SQLITE_OPEN_CREATE;
3448c24a369Sdrh     }else if( iChunk==0 ){
3458c24a369Sdrh       /* Fall through */
346e7d9f13dSdrh     }else if( pGroup->aReal[iChunk].z==0 ){
347e7d9f13dSdrh       return 0;
348e7d9f13dSdrh     }else{
349e7d9f13dSdrh       *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
350e7d9f13dSdrh                               SQLITE_ACCESS_EXISTS, &bExists);
3511779efd4Sdrh      if( *rc || !bExists ){
3521779efd4Sdrh         if( *rc ){
3531779efd4Sdrh           sqlite3_log(*rc, "multiplexor.xAccess failure on %s",
3541779efd4Sdrh                       pGroup->aReal[iChunk].z);
3551779efd4Sdrh         }
3561779efd4Sdrh         return 0;
3571779efd4Sdrh       }
358e7d9f13dSdrh       flags &= ~SQLITE_OPEN_CREATE;
359e7d9f13dSdrh     }
360*f3cdcdccSdrh     pSubOpen = sqlite3_malloc64( pOrigVfs->szOsFile );
361f3717af4Sdrh     if( pSubOpen==0 ){
3628c24a369Sdrh       *rc = SQLITE_IOERR_NOMEM;
363f3717af4Sdrh       return 0;
364050d09abSshaneh     }
365f3717af4Sdrh     pGroup->aReal[iChunk].p = pSubOpen;
36606999667Sdrh     *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
367e7d9f13dSdrh                           flags, pOutFlags);
36806999667Sdrh     if( (*rc)!=SQLITE_OK ){
3691779efd4Sdrh       sqlite3_log(*rc, "multiplexor.xOpen failure on %s",
3701779efd4Sdrh                   pGroup->aReal[iChunk].z);
371f3717af4Sdrh       sqlite3_free(pSubOpen);
372f3717af4Sdrh       pGroup->aReal[iChunk].p = 0;
373f3717af4Sdrh       return 0;
374f3717af4Sdrh     }
375f3717af4Sdrh   }
3768a922f75Sshaneh   return pSubOpen;
3778a922f75Sshaneh }
3788a922f75Sshaneh 
3793801b65dSshaneh /*
380e7d9f13dSdrh ** Return the size, in bytes, of chunk number iChunk.  If that chunk
381e7d9f13dSdrh ** does not exist, then return 0.  This function does not distingish between
382e7d9f13dSdrh ** non-existant files and zero-length files.
383e7d9f13dSdrh */
384e7d9f13dSdrh static sqlite3_int64 multiplexSubSize(
385e7d9f13dSdrh   multiplexGroup *pGroup,    /* The multiplexor group */
386e7d9f13dSdrh   int iChunk,                /* Which chunk to open.  0==original file */
387e7d9f13dSdrh   int *rc                    /* Result code in and out */
388e7d9f13dSdrh ){
389e7d9f13dSdrh   sqlite3_file *pSub;
390e7d9f13dSdrh   sqlite3_int64 sz = 0;
391e7d9f13dSdrh 
392b04f3c15Sdrh   if( *rc ) return 0;
393e7d9f13dSdrh   pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
394e7d9f13dSdrh   if( pSub==0 ) return 0;
395e7d9f13dSdrh   *rc = pSub->pMethods->xFileSize(pSub, &sz);
396e7d9f13dSdrh   return sz;
397e7d9f13dSdrh }
398e7d9f13dSdrh 
399e7d9f13dSdrh /*
4003801b65dSshaneh ** This is the implementation of the multiplex_control() SQL function.
4013801b65dSshaneh */
402d50deeebSshaneh static void multiplexControlFunc(
403d50deeebSshaneh   sqlite3_context *context,
404d50deeebSshaneh   int argc,
405d50deeebSshaneh   sqlite3_value **argv
406d50deeebSshaneh ){
40778c4de4cSshaneh   int rc = SQLITE_OK;
408d8ce22bbSshaneh   sqlite3 *db = sqlite3_context_db_handle(context);
4097bb22ac7Smistachkin   int op = 0;
410d8ce22bbSshaneh   int iVal;
411d8ce22bbSshaneh 
412d8ce22bbSshaneh   if( !db || argc!=2 ){
413d8ce22bbSshaneh     rc = SQLITE_ERROR;
414d8ce22bbSshaneh   }else{
415d8ce22bbSshaneh     /* extract params */
416d8ce22bbSshaneh     op = sqlite3_value_int(argv[0]);
417d8ce22bbSshaneh     iVal = sqlite3_value_int(argv[1]);
418d8ce22bbSshaneh     /* map function op to file_control op */
41978c4de4cSshaneh     switch( op ){
42078c4de4cSshaneh       case 1:
42178c4de4cSshaneh         op = MULTIPLEX_CTRL_ENABLE;
42278c4de4cSshaneh         break;
42378c4de4cSshaneh       case 2:
42478c4de4cSshaneh         op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
42578c4de4cSshaneh         break;
42678c4de4cSshaneh       case 3:
42778c4de4cSshaneh         op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
42878c4de4cSshaneh         break;
42978c4de4cSshaneh       default:
4303801b65dSshaneh         rc = SQLITE_NOTFOUND;
43178c4de4cSshaneh         break;
43278c4de4cSshaneh     }
433d8ce22bbSshaneh   }
43478c4de4cSshaneh   if( rc==SQLITE_OK ){
435d8ce22bbSshaneh     rc = sqlite3_file_control(db, 0, op, &iVal);
436d9523b97Sshaneh   }
4373801b65dSshaneh   sqlite3_result_error_code(context, rc);
438d9523b97Sshaneh }
439d9523b97Sshaneh 
440d9523b97Sshaneh /*
4413801b65dSshaneh ** This is the entry point to register the auto-extension for the
4423801b65dSshaneh ** multiplex_control() function.
443d9523b97Sshaneh */
444d50deeebSshaneh static int multiplexFuncInit(
44578c4de4cSshaneh   sqlite3 *db,
44678c4de4cSshaneh   char **pzErrMsg,
44778c4de4cSshaneh   const sqlite3_api_routines *pApi
448d50deeebSshaneh ){
449ac039688Sshaneh   int rc;
450ac039688Sshaneh   rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
451d8ce22bbSshaneh       0, multiplexControlFunc, 0, 0);
452ac039688Sshaneh   return rc;
453d9523b97Sshaneh }
454d9523b97Sshaneh 
455f3717af4Sdrh /*
456f3717af4Sdrh ** Close a single sub-file in the connection group.
457f3717af4Sdrh */
458f3717af4Sdrh static void multiplexSubClose(
459f3717af4Sdrh   multiplexGroup *pGroup,
460f3717af4Sdrh   int iChunk,
461f3717af4Sdrh   sqlite3_vfs *pOrigVfs
462f3717af4Sdrh ){
463f3717af4Sdrh   sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
464f3717af4Sdrh   if( pSubOpen ){
465f3717af4Sdrh     pSubOpen->pMethods->xClose(pSubOpen);
4662be25bffSdrh     if( pOrigVfs && pGroup->aReal[iChunk].z ){
4672be25bffSdrh       pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
4682be25bffSdrh     }
469f3717af4Sdrh     sqlite3_free(pGroup->aReal[iChunk].p);
470f3717af4Sdrh   }
471f3717af4Sdrh   sqlite3_free(pGroup->aReal[iChunk].z);
472f3717af4Sdrh   memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));
473f3717af4Sdrh }
474f3717af4Sdrh 
475f3717af4Sdrh /*
476f3717af4Sdrh ** Deallocate memory held by a multiplexGroup
477f3717af4Sdrh */
478f3717af4Sdrh static void multiplexFreeComponents(multiplexGroup *pGroup){
479f3717af4Sdrh   int i;
480f3717af4Sdrh   for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
481f3717af4Sdrh   sqlite3_free(pGroup->aReal);
482f3717af4Sdrh   pGroup->aReal = 0;
483f3717af4Sdrh   pGroup->nReal = 0;
484f3717af4Sdrh }
485f3717af4Sdrh 
486f3717af4Sdrh 
4878a922f75Sshaneh /************************* VFS Method Wrappers *****************************/
488b5830294Sshaneh 
4898a922f75Sshaneh /*
4908a922f75Sshaneh ** This is the xOpen method used for the "multiplex" VFS.
4918a922f75Sshaneh **
4928a922f75Sshaneh ** Most of the work is done by the underlying original VFS.  This method
4938a922f75Sshaneh ** simply links the new file into the appropriate multiplex group if it is a
4948a922f75Sshaneh ** file that needs to be tracked.
4958a922f75Sshaneh */
4968a922f75Sshaneh static int multiplexOpen(
4978a922f75Sshaneh   sqlite3_vfs *pVfs,         /* The multiplex VFS */
4988a922f75Sshaneh   const char *zName,         /* Name of file to be opened */
4998a922f75Sshaneh   sqlite3_file *pConn,       /* Fill in this file descriptor */
5008a922f75Sshaneh   int flags,                 /* Flags to control the opening */
5018a922f75Sshaneh   int *pOutFlags             /* Flags showing results of opening */
5028a922f75Sshaneh ){
503cc4e19beSshaneh   int rc = SQLITE_OK;                  /* Result code */
5048a922f75Sshaneh   multiplexConn *pMultiplexOpen;       /* The new multiplex file descriptor */
505fc30b042Sdrh   multiplexGroup *pGroup = 0;          /* Corresponding multiplexGroup object */
5060a0ca697Sdrh   sqlite3_file *pSubOpen = 0;                    /* Real file descriptor */
5078a922f75Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
508fc30b042Sdrh   int nName = 0;
509fc30b042Sdrh   int sz = 0;
510f3717af4Sdrh   char *zToFree = 0;
5118a922f75Sshaneh 
5128a922f75Sshaneh   UNUSED_PARAMETER(pVfs);
513f3717af4Sdrh   memset(pConn, 0, pVfs->szOsFile);
5145b1626aaSdrh   assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
5158a922f75Sshaneh 
5168a922f75Sshaneh   /* We need to create a group structure and manage
5178a922f75Sshaneh   ** access to this group of files.
5188a922f75Sshaneh   */
5198a922f75Sshaneh   multiplexEnter();
5208a922f75Sshaneh   pMultiplexOpen = (multiplexConn*)pConn;
521cc4e19beSshaneh 
522cc4e19beSshaneh   if( rc==SQLITE_OK ){
523050d09abSshaneh     /* allocate space for group */
5245b1626aaSdrh     nName = zName ? multiplexStrlen30(zName) : 0;
525050d09abSshaneh     sz = sizeof(multiplexGroup)                             /* multiplexGroup */
526050d09abSshaneh        + nName + 1;                                         /* zName */
527*f3cdcdccSdrh     pGroup = sqlite3_malloc64( sz );
5288a922f75Sshaneh     if( pGroup==0 ){
5298a922f75Sshaneh       rc = SQLITE_NOMEM;
530cc4e19beSshaneh     }
531cc4e19beSshaneh   }
532cc4e19beSshaneh 
533cc4e19beSshaneh   if( rc==SQLITE_OK ){
534c02a43afSdrh     const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0;
535050d09abSshaneh     /* assign pointers to extra space allocated */
536050d09abSshaneh     memset(pGroup, 0, sz);
5375b1626aaSdrh     pMultiplexOpen->pGroup = pGroup;
53827b2f053Smistachkin     pGroup->bEnabled = (unsigned char)-1;
539c02a43afSdrh     pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
54092913720Sdrh                                    (flags & SQLITE_OPEN_MAIN_DB)==0);
5417da5fcb0Sdrh     pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize",
54292913720Sdrh                                         SQLITE_MULTIPLEX_CHUNK_SIZE);
54392913720Sdrh     pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
5445b1626aaSdrh     if( zName ){
5455b1626aaSdrh       char *p = (char *)&pGroup[1];
546050d09abSshaneh       pGroup->zName = p;
5478a922f75Sshaneh       memcpy(pGroup->zName, zName, nName+1);
5488a922f75Sshaneh       pGroup->nName = nName;
5495b1626aaSdrh     }
55078c0eafbSdrh     if( pGroup->bEnabled ){
55192913720Sdrh       /* Make sure that the chunksize is such that the pending byte does not
55278c0eafbSdrh       ** falls at the end of a chunk.  A region of up to 64K following
55378c0eafbSdrh       ** the pending byte is never written, so if the pending byte occurs
55478c0eafbSdrh       ** near the end of a chunk, that chunk will be too small. */
5550420b74aSdan #ifndef SQLITE_OMIT_WSD
55678c0eafbSdrh       extern int sqlite3PendingByte;
5570420b74aSdan #else
5580420b74aSdan       int sqlite3PendingByte = 0x40000000;
5590420b74aSdan #endif
56078c0eafbSdrh       while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
56178c0eafbSdrh         pGroup->szChunk += 65536;
56278c0eafbSdrh       }
56378c0eafbSdrh     }
5648a922f75Sshaneh     pGroup->flags = flags;
5650a0ca697Sdrh     rc = multiplexSubFilename(pGroup, 1);
5660a0ca697Sdrh     if( rc==SQLITE_OK ){
567e7d9f13dSdrh       pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
568e7d9f13dSdrh       if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
5690a0ca697Sdrh     }
57031b21295Sdan     if( rc==SQLITE_OK ){
5718e18922fSmistachkin       sqlite3_int64 sz64;
5720a0ca697Sdrh 
5738e18922fSmistachkin       rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz64);
57431b21295Sdan       if( rc==SQLITE_OK && zName ){
575e712b582Sdan         int bExists;
57663c088e7Sdan         if( flags & SQLITE_OPEN_MASTER_JOURNAL ){
57763c088e7Sdan           pGroup->bEnabled = 0;
57863c088e7Sdan         }else
5798e18922fSmistachkin         if( sz64==0 ){
580e712b582Sdan           if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
581e712b582Sdan             /* If opening a main journal file and the first chunk is zero
582e712b582Sdan             ** bytes in size, delete any subsequent chunks from the
583e712b582Sdan             ** file-system. */
584e712b582Sdan             int iChunk = 1;
585e712b582Sdan             do {
586e712b582Sdan               rc = pOrigVfs->xAccess(pOrigVfs,
587e712b582Sdan                   pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists
588e712b582Sdan               );
589e712b582Sdan               if( rc==SQLITE_OK && bExists ){
590e712b582Sdan                 rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
591e712b582Sdan                 if( rc==SQLITE_OK ){
592e712b582Sdan                   rc = multiplexSubFilename(pGroup, ++iChunk);
593e712b582Sdan                 }
594e712b582Sdan               }
595e712b582Sdan             }while( rc==SQLITE_OK && bExists );
596e712b582Sdan           }
597e712b582Sdan         }else{
5980a0ca697Sdrh           /* If the first overflow file exists and if the size of the main file
5990a0ca697Sdrh           ** is different from the chunk size, that means the chunk size is set
6000a0ca697Sdrh           ** set incorrectly.  So fix it.
6010a0ca697Sdrh           **
6020a0ca697Sdrh           ** Or, if the first overflow file does not exist and the main file is
6030a0ca697Sdrh           ** larger than the chunk size, that means the chunk size is too small.
6040a0ca697Sdrh           ** But we have no way of determining the intended chunk size, so
6050a0ca697Sdrh           ** just disable the multiplexor all togethre.
6060a0ca697Sdrh           */
60731b21295Sdan           rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
608e712b582Sdan               SQLITE_ACCESS_EXISTS, &bExists);
609e7d9f13dSdrh           bExists = multiplexSubSize(pGroup, 1, &rc)>0;
6108e18922fSmistachkin           if( rc==SQLITE_OK && bExists && sz64==(sz64&0xffff0000) && sz64>0
6118e18922fSmistachkin               && sz64!=pGroup->szChunk ){
6128e18922fSmistachkin             pGroup->szChunk = (int)sz64;
6138e18922fSmistachkin           }else if( rc==SQLITE_OK && !bExists && sz64>pGroup->szChunk ){
614c27fa4b0Sshaneh             pGroup->bEnabled = 0;
615c27fa4b0Sshaneh           }
6160a0ca697Sdrh         }
61731b21295Sdan       }
618e712b582Sdan     }
6190a0ca697Sdrh 
62031b21295Sdan     if( rc==SQLITE_OK ){
6218a922f75Sshaneh       if( pSubOpen->pMethods->iVersion==1 ){
6228a922f75Sshaneh         pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
6238a922f75Sshaneh       }else{
6248a922f75Sshaneh         pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
6258a922f75Sshaneh       }
6268a922f75Sshaneh       /* place this group at the head of our list */
6278a922f75Sshaneh       pGroup->pNext = gMultiplex.pGroups;
6288a922f75Sshaneh       if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
6298a922f75Sshaneh       gMultiplex.pGroups = pGroup;
6308a922f75Sshaneh     }else{
631f3717af4Sdrh       multiplexFreeComponents(pGroup);
6328a922f75Sshaneh       sqlite3_free(pGroup);
6338a922f75Sshaneh     }
6348a922f75Sshaneh   }
6358a922f75Sshaneh   multiplexLeave();
636f3717af4Sdrh   sqlite3_free(zToFree);
6378a922f75Sshaneh   return rc;
6388a922f75Sshaneh }
6398a922f75Sshaneh 
640b5830294Sshaneh /*
641b5830294Sshaneh ** This is the xDelete method used for the "multiplex" VFS.
642f3717af4Sdrh ** It attempts to delete the filename specified.
643b5830294Sshaneh */
644b5830294Sshaneh static int multiplexDelete(
645b5830294Sshaneh   sqlite3_vfs *pVfs,         /* The multiplex VFS */
646b5830294Sshaneh   const char *zName,         /* Name of file to delete */
647b5830294Sshaneh   int syncDir
648b5830294Sshaneh ){
649e712b582Sdan   int rc;
650b5830294Sshaneh   sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
651e712b582Sdan   rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
652e712b582Sdan   if( rc==SQLITE_OK ){
653e712b582Sdan     /* If the main chunk was deleted successfully, also delete any subsequent
654e712b582Sdan     ** chunks - starting with the last (highest numbered).
655e712b582Sdan     */
65683cc1392Sdrh     int nName = (int)strlen(zName);
657e712b582Sdan     char *z;
658*f3cdcdccSdrh     z = sqlite3_malloc64(nName + 5);
659e712b582Sdan     if( z==0 ){
660e712b582Sdan       rc = SQLITE_IOERR_NOMEM;
661e712b582Sdan     }else{
662e712b582Sdan       int iChunk = 0;
663e712b582Sdan       int bExists;
664e712b582Sdan       do{
665e712b582Sdan         multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);
666e712b582Sdan         rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
667e712b582Sdan       }while( rc==SQLITE_OK && bExists );
668e712b582Sdan       while( rc==SQLITE_OK && iChunk>1 ){
669e712b582Sdan         multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
670e712b582Sdan         rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
671e712b582Sdan       }
6724aced72aSdan       if( rc==SQLITE_OK ){
673c611ee94Sdrh         iChunk = 0;
674c611ee94Sdrh         do{
675c611ee94Sdrh           multiplexFilename(zName, nName, SQLITE_OPEN_WAL, ++iChunk, z);
676c611ee94Sdrh           rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
677c611ee94Sdrh         }while( rc==SQLITE_OK && bExists );
678c611ee94Sdrh         while( rc==SQLITE_OK && iChunk>1 ){
679c611ee94Sdrh           multiplexFilename(zName, nName, SQLITE_OPEN_WAL, --iChunk, z);
680c611ee94Sdrh           rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
681c611ee94Sdrh         }
682e712b582Sdan       }
6834aced72aSdan     }
684e712b582Sdan     sqlite3_free(z);
685e712b582Sdan   }
686e712b582Sdan   return rc;
687b5830294Sshaneh }
688b5830294Sshaneh 
689d50deeebSshaneh static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
690d50deeebSshaneh   return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
691fffadc52Sdan }
692d50deeebSshaneh static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
693d50deeebSshaneh   return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
694fffadc52Sdan }
695fffadc52Sdan static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
696fffadc52Sdan   return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
697fffadc52Sdan }
698fffadc52Sdan static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
699fffadc52Sdan   gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
700fffadc52Sdan }
701fffadc52Sdan static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
702fffadc52Sdan   return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
703fffadc52Sdan }
704fffadc52Sdan static void multiplexDlClose(sqlite3_vfs *a, void *b){
705fffadc52Sdan   gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
706fffadc52Sdan }
707fffadc52Sdan static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
708fffadc52Sdan   return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
709fffadc52Sdan }
710fffadc52Sdan static int multiplexSleep(sqlite3_vfs *a, int b){
711fffadc52Sdan   return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
712fffadc52Sdan }
713fffadc52Sdan static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
714fffadc52Sdan   return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
715fffadc52Sdan }
716fffadc52Sdan static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
717fffadc52Sdan   return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
718fffadc52Sdan }
719fffadc52Sdan static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
720fffadc52Sdan   return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
721fffadc52Sdan }
722fffadc52Sdan 
7238a922f75Sshaneh /************************ I/O Method Wrappers *******************************/
7248a922f75Sshaneh 
7258a922f75Sshaneh /* xClose requests get passed through to the original VFS.
7268a922f75Sshaneh ** We loop over all open chunk handles and close them.
7278a922f75Sshaneh ** The group structure for this file is unlinked from
7288a922f75Sshaneh ** our list of groups and freed.
7298a922f75Sshaneh */
7308a922f75Sshaneh static int multiplexClose(sqlite3_file *pConn){
7318a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
7328a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
7338a922f75Sshaneh   int rc = SQLITE_OK;
7348a922f75Sshaneh   multiplexEnter();
735f3717af4Sdrh   multiplexFreeComponents(pGroup);
736b5830294Sshaneh   /* remove from linked list */
7378a922f75Sshaneh   if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
7388a922f75Sshaneh   if( pGroup->pPrev ){
7398a922f75Sshaneh     pGroup->pPrev->pNext = pGroup->pNext;
7408a922f75Sshaneh   }else{
7418a922f75Sshaneh     gMultiplex.pGroups = pGroup->pNext;
7428a922f75Sshaneh   }
7438a922f75Sshaneh   sqlite3_free(pGroup);
7448a922f75Sshaneh   multiplexLeave();
7458a922f75Sshaneh   return rc;
7468a922f75Sshaneh }
7478a922f75Sshaneh 
7488a922f75Sshaneh /* Pass xRead requests thru to the original VFS after
7498a922f75Sshaneh ** determining the correct chunk to operate on.
750fd1552f2Sshaneh ** Break up reads across chunk boundaries.
7518a922f75Sshaneh */
7528a922f75Sshaneh static int multiplexRead(
7538a922f75Sshaneh   sqlite3_file *pConn,
7548a922f75Sshaneh   void *pBuf,
7558a922f75Sshaneh   int iAmt,
7568a922f75Sshaneh   sqlite3_int64 iOfst
7578a922f75Sshaneh ){
7588a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
759d50deeebSshaneh   multiplexGroup *pGroup = p->pGroup;
7608a922f75Sshaneh   int rc = SQLITE_OK;
761ee68ccfbSdrh   int nMutex = 0;
762ee68ccfbSdrh   multiplexEnter(); nMutex++;
763c27fa4b0Sshaneh   if( !pGroup->bEnabled ){
764e7d9f13dSdrh     sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
765ee68ccfbSdrh     multiplexLeave(); nMutex--;
766f3717af4Sdrh     if( pSubOpen==0 ){
7678efdb732Sdrh       rc = SQLITE_IOERR_READ;
7688efdb732Sdrh     }else{
7698efdb732Sdrh       rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
7708efdb732Sdrh     }
771c27fa4b0Sshaneh   }else{
7728a922f75Sshaneh     while( iAmt > 0 ){
773e6deb204Sdrh       int i = (int)(iOfst / pGroup->szChunk);
77495a5bcbbSdrh       sqlite3_file *pSubOpen;
775ee68ccfbSdrh       if( nMutex==0 ){ multiplexEnter(); nMutex++; }
77695a5bcbbSdrh       pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
777ee68ccfbSdrh       multiplexLeave(); nMutex--;
7788a922f75Sshaneh       if( pSubOpen ){
779e6deb204Sdrh         int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
7808a922f75Sshaneh         if( extra<0 ) extra = 0;
7818a922f75Sshaneh         iAmt -= extra;
7828efdb732Sdrh         rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
783e6deb204Sdrh                                        iOfst % pGroup->szChunk);
7848a922f75Sshaneh         if( rc!=SQLITE_OK ) break;
7858a922f75Sshaneh         pBuf = (char *)pBuf + iAmt;
7868a922f75Sshaneh         iOfst += iAmt;
7878a922f75Sshaneh         iAmt = extra;
7888a922f75Sshaneh       }else{
7898a922f75Sshaneh         rc = SQLITE_IOERR_READ;
7908a922f75Sshaneh         break;
7918a922f75Sshaneh       }
7928a922f75Sshaneh     }
793c27fa4b0Sshaneh   }
794ee68ccfbSdrh   assert( nMutex==0 || nMutex==1 );
795ee68ccfbSdrh   if( nMutex ) multiplexLeave();
7968a922f75Sshaneh   return rc;
7978a922f75Sshaneh }
7988a922f75Sshaneh 
7998a922f75Sshaneh /* Pass xWrite requests thru to the original VFS after
8008a922f75Sshaneh ** determining the correct chunk to operate on.
801fd1552f2Sshaneh ** Break up writes across chunk boundaries.
8028a922f75Sshaneh */
8038a922f75Sshaneh static int multiplexWrite(
8048a922f75Sshaneh   sqlite3_file *pConn,
8058a922f75Sshaneh   const void *pBuf,
8068a922f75Sshaneh   int iAmt,
8078a922f75Sshaneh   sqlite3_int64 iOfst
8088a922f75Sshaneh ){
8098a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
810d50deeebSshaneh   multiplexGroup *pGroup = p->pGroup;
8118a922f75Sshaneh   int rc = SQLITE_OK;
8128a922f75Sshaneh   multiplexEnter();
813c27fa4b0Sshaneh   if( !pGroup->bEnabled ){
814e7d9f13dSdrh     sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
8158efdb732Sdrh     if( pSubOpen==0 ){
8168efdb732Sdrh       rc = SQLITE_IOERR_WRITE;
8178efdb732Sdrh     }else{
8188efdb732Sdrh       rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
8198efdb732Sdrh     }
820c27fa4b0Sshaneh   }else{
82127e69643Sdan     while( rc==SQLITE_OK && iAmt>0 ){
822e6deb204Sdrh       int i = (int)(iOfst / pGroup->szChunk);
823e7d9f13dSdrh       sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
8248a922f75Sshaneh       if( pSubOpen ){
825e6deb204Sdrh         int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
826e6deb204Sdrh                     pGroup->szChunk;
8278a922f75Sshaneh         if( extra<0 ) extra = 0;
8288a922f75Sshaneh         iAmt -= extra;
8298efdb732Sdrh         rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
830e6deb204Sdrh                                         iOfst % pGroup->szChunk);
8318a922f75Sshaneh         pBuf = (char *)pBuf + iAmt;
8328a922f75Sshaneh         iOfst += iAmt;
8338a922f75Sshaneh         iAmt = extra;
8348a922f75Sshaneh       }
8358a922f75Sshaneh     }
836c27fa4b0Sshaneh   }
8378a922f75Sshaneh   multiplexLeave();
8388a922f75Sshaneh   return rc;
8398a922f75Sshaneh }
8408a922f75Sshaneh 
8418a922f75Sshaneh /* Pass xTruncate requests thru to the original VFS after
8428a922f75Sshaneh ** determining the correct chunk to operate on.  Delete any
8438a922f75Sshaneh ** chunks above the truncate mark.
8448a922f75Sshaneh */
8458a922f75Sshaneh static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
8468a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
8478a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
8488a922f75Sshaneh   int rc = SQLITE_OK;
849c27fa4b0Sshaneh   multiplexEnter();
850c27fa4b0Sshaneh   if( !pGroup->bEnabled ){
851e7d9f13dSdrh     sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
8528efdb732Sdrh     if( pSubOpen==0 ){
8538efdb732Sdrh       rc = SQLITE_IOERR_TRUNCATE;
8548efdb732Sdrh     }else{
8558efdb732Sdrh       rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
8568efdb732Sdrh     }
857e7d9f13dSdrh   }else{
8588a922f75Sshaneh     int i;
859e7d9f13dSdrh     int iBaseGroup = (int)(size / pGroup->szChunk);
8608a922f75Sshaneh     sqlite3_file *pSubOpen;
8618a922f75Sshaneh     sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
8628a922f75Sshaneh     /* delete the chunks above the truncate limit */
863e7d9f13dSdrh     for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
864e7d9f13dSdrh       if( pGroup->bTruncate ){
865f3717af4Sdrh         multiplexSubClose(pGroup, i, pOrigVfs);
8668a922f75Sshaneh       }else{
867e7d9f13dSdrh         pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
868e7d9f13dSdrh         if( pSubOpen ){
869e7d9f13dSdrh           rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
8708a922f75Sshaneh         }
871c27fa4b0Sshaneh       }
872e7d9f13dSdrh     }
873e7d9f13dSdrh     if( rc==SQLITE_OK ){
874e7d9f13dSdrh       pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
875e7d9f13dSdrh       if( pSubOpen ){
876e7d9f13dSdrh         rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
877e7d9f13dSdrh       }
878e7d9f13dSdrh     }
879e7d9f13dSdrh     if( rc ) rc = SQLITE_IOERR_TRUNCATE;
880e7d9f13dSdrh   }
8818a922f75Sshaneh   multiplexLeave();
8828a922f75Sshaneh   return rc;
8838a922f75Sshaneh }
8848a922f75Sshaneh 
8858a922f75Sshaneh /* Pass xSync requests through to the original VFS without change
8868a922f75Sshaneh */
8878a922f75Sshaneh static int multiplexSync(sqlite3_file *pConn, int flags){
8888a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
8898a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
8908a922f75Sshaneh   int rc = SQLITE_OK;
8918a922f75Sshaneh   int i;
8928a922f75Sshaneh   multiplexEnter();
893f3717af4Sdrh   for(i=0; i<pGroup->nReal; i++){
894f3717af4Sdrh     sqlite3_file *pSubOpen = pGroup->aReal[i].p;
895f3717af4Sdrh     if( pSubOpen ){
8968a922f75Sshaneh       int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
8978a922f75Sshaneh       if( rc2!=SQLITE_OK ) rc = rc2;
8988a922f75Sshaneh     }
8998a922f75Sshaneh   }
9008a922f75Sshaneh   multiplexLeave();
9018a922f75Sshaneh   return rc;
9028a922f75Sshaneh }
9038a922f75Sshaneh 
9048a922f75Sshaneh /* Pass xFileSize requests through to the original VFS.
9058a922f75Sshaneh ** Aggregate the size of all the chunks before returning.
9068a922f75Sshaneh */
9078a922f75Sshaneh static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
9088a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
9098a922f75Sshaneh   multiplexGroup *pGroup = p->pGroup;
9108a922f75Sshaneh   int rc = SQLITE_OK;
9118a922f75Sshaneh   int i;
9128a922f75Sshaneh   multiplexEnter();
913c27fa4b0Sshaneh   if( !pGroup->bEnabled ){
914e7d9f13dSdrh     sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
9158efdb732Sdrh     if( pSubOpen==0 ){
9165b1626aaSdrh       rc = SQLITE_IOERR_FSTAT;
9178efdb732Sdrh     }else{
9188efdb732Sdrh       rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
9198efdb732Sdrh     }
920c27fa4b0Sshaneh   }else{
9218a922f75Sshaneh     *pSize = 0;
9225b1626aaSdrh     for(i=0; rc==SQLITE_OK; i++){
923e7d9f13dSdrh       sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
924e7d9f13dSdrh       if( sz==0 ) break;
9252be25bffSdrh       *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
9268a922f75Sshaneh     }
9278a922f75Sshaneh   }
9288a922f75Sshaneh   multiplexLeave();
9298a922f75Sshaneh   return rc;
9308a922f75Sshaneh }
9318a922f75Sshaneh 
9328a922f75Sshaneh /* Pass xLock requests through to the original VFS unchanged.
9338a922f75Sshaneh */
9348a922f75Sshaneh static int multiplexLock(sqlite3_file *pConn, int lock){
9358a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
9368a922f75Sshaneh   int rc;
937e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
9388a922f75Sshaneh   if( pSubOpen ){
9398a922f75Sshaneh     return pSubOpen->pMethods->xLock(pSubOpen, lock);
9408a922f75Sshaneh   }
9418a922f75Sshaneh   return SQLITE_BUSY;
9428a922f75Sshaneh }
9438a922f75Sshaneh 
9448a922f75Sshaneh /* Pass xUnlock requests through to the original VFS unchanged.
9458a922f75Sshaneh */
9468a922f75Sshaneh static int multiplexUnlock(sqlite3_file *pConn, int lock){
9478a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
9488a922f75Sshaneh   int rc;
949e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
9508a922f75Sshaneh   if( pSubOpen ){
9518a922f75Sshaneh     return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
9528a922f75Sshaneh   }
9538a922f75Sshaneh   return SQLITE_IOERR_UNLOCK;
9548a922f75Sshaneh }
9558a922f75Sshaneh 
9568a922f75Sshaneh /* Pass xCheckReservedLock requests through to the original VFS unchanged.
9578a922f75Sshaneh */
9588a922f75Sshaneh static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
9598a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
9608a922f75Sshaneh   int rc;
961e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
9628a922f75Sshaneh   if( pSubOpen ){
9638a922f75Sshaneh     return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
9648a922f75Sshaneh   }
9658a922f75Sshaneh   return SQLITE_IOERR_CHECKRESERVEDLOCK;
9668a922f75Sshaneh }
9678a922f75Sshaneh 
968c27fa4b0Sshaneh /* Pass xFileControl requests through to the original VFS unchanged,
969c27fa4b0Sshaneh ** except for any MULTIPLEX_CTRL_* requests here.
9708a922f75Sshaneh */
9718a922f75Sshaneh static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
9728a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
973d50deeebSshaneh   multiplexGroup *pGroup = p->pGroup;
974d50deeebSshaneh   int rc = SQLITE_ERROR;
9758a922f75Sshaneh   sqlite3_file *pSubOpen;
976d50deeebSshaneh 
977d50deeebSshaneh   if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
978d50deeebSshaneh   switch( op ){
979d50deeebSshaneh     case MULTIPLEX_CTRL_ENABLE:
980d50deeebSshaneh       if( pArg ) {
981d50deeebSshaneh         int bEnabled = *(int *)pArg;
982d50deeebSshaneh         pGroup->bEnabled = bEnabled;
983d50deeebSshaneh         rc = SQLITE_OK;
984d50deeebSshaneh       }
985d50deeebSshaneh       break;
986d50deeebSshaneh     case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
987d50deeebSshaneh       if( pArg ) {
988e6deb204Sdrh         unsigned int szChunk = *(unsigned*)pArg;
989e6deb204Sdrh         if( szChunk<1 ){
990d50deeebSshaneh           rc = SQLITE_MISUSE;
991d50deeebSshaneh         }else{
9923801b65dSshaneh           /* Round up to nearest multiple of MAX_PAGE_SIZE. */
993e6deb204Sdrh           szChunk = (szChunk + (MAX_PAGE_SIZE-1));
994e6deb204Sdrh           szChunk &= ~(MAX_PAGE_SIZE-1);
995e6deb204Sdrh           pGroup->szChunk = szChunk;
996d50deeebSshaneh           rc = SQLITE_OK;
997d50deeebSshaneh         }
998d50deeebSshaneh       }
999d50deeebSshaneh       break;
1000d50deeebSshaneh     case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
1001d50deeebSshaneh       rc = SQLITE_OK;
1002d50deeebSshaneh       break;
1003ac039688Sshaneh     case SQLITE_FCNTL_SIZE_HINT:
1004ac039688Sshaneh     case SQLITE_FCNTL_CHUNK_SIZE:
1005ac039688Sshaneh       /* no-op these */
1006ac039688Sshaneh       rc = SQLITE_OK;
1007ac039688Sshaneh       break;
10083b8fea9eSdrh     case SQLITE_FCNTL_PRAGMA: {
10093b8fea9eSdrh       char **aFcntl = (char**)pArg;
10108dd7a6a9Sdrh       /*
10118dd7a6a9Sdrh       ** EVIDENCE-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA
10128dd7a6a9Sdrh       ** file control is an array of pointers to strings (char**) in which the
10138dd7a6a9Sdrh       ** second element of the array is the name of the pragma and the third
10148dd7a6a9Sdrh       ** element is the argument to the pragma or NULL if the pragma has no
10158dd7a6a9Sdrh       ** argument.
10168dd7a6a9Sdrh       */
10173b8fea9eSdrh       if( aFcntl[1] && sqlite3_stricmp(aFcntl[1],"multiplex_truncate")==0 ){
10183b8fea9eSdrh         if( aFcntl[2] && aFcntl[2][0] ){
10193b8fea9eSdrh           if( sqlite3_stricmp(aFcntl[2], "on")==0
10203b8fea9eSdrh            || sqlite3_stricmp(aFcntl[2], "1")==0 ){
10213b8fea9eSdrh             pGroup->bTruncate = 1;
10223b8fea9eSdrh           }else
10233b8fea9eSdrh           if( sqlite3_stricmp(aFcntl[2], "off")==0
10243b8fea9eSdrh            || sqlite3_stricmp(aFcntl[2], "0")==0 ){
10253b8fea9eSdrh             pGroup->bTruncate = 0;
10263b8fea9eSdrh           }
10273b8fea9eSdrh         }
10288dd7a6a9Sdrh         /* EVIDENCE-OF: R-27806-26076 The handler for an SQLITE_FCNTL_PRAGMA
10298dd7a6a9Sdrh         ** file control can optionally make the first element of the char**
10308dd7a6a9Sdrh         ** argument point to a string obtained from sqlite3_mprintf() or the
10318dd7a6a9Sdrh         ** equivalent and that string will become the result of the pragma
10328dd7a6a9Sdrh         ** or the error message if the pragma fails.
10338dd7a6a9Sdrh         */
10343b8fea9eSdrh         aFcntl[0] = sqlite3_mprintf(pGroup->bTruncate ? "on" : "off");
10353b8fea9eSdrh         rc = SQLITE_OK;
10363b8fea9eSdrh         break;
10373b8fea9eSdrh       }
10383b8fea9eSdrh       /* If the multiplexor does not handle the pragma, pass it through
10393b8fea9eSdrh       ** into the default case. */
10403b8fea9eSdrh     }
1041d50deeebSshaneh     default:
1042e7d9f13dSdrh       pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
10438a922f75Sshaneh       if( pSubOpen ){
1044d50deeebSshaneh         rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
1045de60fc2dSdrh         if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
1046de60fc2dSdrh          *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
1047de60fc2dSdrh         }
10488a922f75Sshaneh       }
1049d50deeebSshaneh       break;
1050d50deeebSshaneh   }
1051d50deeebSshaneh   return rc;
10528a922f75Sshaneh }
10538a922f75Sshaneh 
10548a922f75Sshaneh /* Pass xSectorSize requests through to the original VFS unchanged.
10558a922f75Sshaneh */
10568a922f75Sshaneh static int multiplexSectorSize(sqlite3_file *pConn){
10578a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
10588a922f75Sshaneh   int rc;
1059e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1060c7f94629Sdrh   if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
10618a922f75Sshaneh     return pSubOpen->pMethods->xSectorSize(pSubOpen);
10628a922f75Sshaneh   }
10633801b65dSshaneh   return DEFAULT_SECTOR_SIZE;
10648a922f75Sshaneh }
10658a922f75Sshaneh 
10668a922f75Sshaneh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
10678a922f75Sshaneh */
10688a922f75Sshaneh static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
10698a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
10708a922f75Sshaneh   int rc;
1071e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
10728a922f75Sshaneh   if( pSubOpen ){
10738a922f75Sshaneh     return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
10748a922f75Sshaneh   }
10758a922f75Sshaneh   return 0;
10768a922f75Sshaneh }
10778a922f75Sshaneh 
10788a922f75Sshaneh /* Pass xShmMap requests through to the original VFS unchanged.
10798a922f75Sshaneh */
10808a922f75Sshaneh static int multiplexShmMap(
10818a922f75Sshaneh   sqlite3_file *pConn,            /* Handle open on database file */
10828a922f75Sshaneh   int iRegion,                    /* Region to retrieve */
10838a922f75Sshaneh   int szRegion,                   /* Size of regions */
10848a922f75Sshaneh   int bExtend,                    /* True to extend file if necessary */
10858a922f75Sshaneh   void volatile **pp              /* OUT: Mapped memory */
10868a922f75Sshaneh ){
10878a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
10888a922f75Sshaneh   int rc;
1089e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
10908a922f75Sshaneh   if( pSubOpen ){
10918a922f75Sshaneh     return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
10928a922f75Sshaneh   }
10938a922f75Sshaneh   return SQLITE_IOERR;
10948a922f75Sshaneh }
10958a922f75Sshaneh 
10968a922f75Sshaneh /* Pass xShmLock requests through to the original VFS unchanged.
10978a922f75Sshaneh */
10988a922f75Sshaneh static int multiplexShmLock(
10998a922f75Sshaneh   sqlite3_file *pConn,       /* Database file holding the shared memory */
11008a922f75Sshaneh   int ofst,                  /* First lock to acquire or release */
11018a922f75Sshaneh   int n,                     /* Number of locks to acquire or release */
11028a922f75Sshaneh   int flags                  /* What to do with the lock */
11038a922f75Sshaneh ){
11048a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
11058a922f75Sshaneh   int rc;
1106e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
11078a922f75Sshaneh   if( pSubOpen ){
11088a922f75Sshaneh     return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
11098a922f75Sshaneh   }
11108a922f75Sshaneh   return SQLITE_BUSY;
11118a922f75Sshaneh }
11128a922f75Sshaneh 
11138a922f75Sshaneh /* Pass xShmBarrier requests through to the original VFS unchanged.
11148a922f75Sshaneh */
11158a922f75Sshaneh static void multiplexShmBarrier(sqlite3_file *pConn){
11168a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
11178a922f75Sshaneh   int rc;
1118e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
11198a922f75Sshaneh   if( pSubOpen ){
11208a922f75Sshaneh     pSubOpen->pMethods->xShmBarrier(pSubOpen);
11218a922f75Sshaneh   }
11228a922f75Sshaneh }
11238a922f75Sshaneh 
11248a922f75Sshaneh /* Pass xShmUnmap requests through to the original VFS unchanged.
11258a922f75Sshaneh */
11268a922f75Sshaneh static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
11278a922f75Sshaneh   multiplexConn *p = (multiplexConn*)pConn;
11288a922f75Sshaneh   int rc;
1129e7d9f13dSdrh   sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
11308a922f75Sshaneh   if( pSubOpen ){
11318a922f75Sshaneh     return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
11328a922f75Sshaneh   }
11338a922f75Sshaneh   return SQLITE_OK;
11348a922f75Sshaneh }
11358a922f75Sshaneh 
11368a922f75Sshaneh /************************** Public Interfaces *****************************/
11378a922f75Sshaneh /*
113878c4de4cSshaneh ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
113978c4de4cSshaneh **
114078c4de4cSshaneh ** Use the VFS named zOrigVfsName as the VFS that does the actual work.
114178c4de4cSshaneh ** Use the default if zOrigVfsName==NULL.
11428a922f75Sshaneh **
11438a922f75Sshaneh ** The multiplex VFS shim is named "multiplex".  It will become the default
11448a922f75Sshaneh ** VFS if makeDefault is non-zero.
11458a922f75Sshaneh **
11468a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
11478a922f75Sshaneh ** during start-up.
11488a922f75Sshaneh */
11498a922f75Sshaneh int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
11508a922f75Sshaneh   sqlite3_vfs *pOrigVfs;
11518a922f75Sshaneh   if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
11528a922f75Sshaneh   pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
11538a922f75Sshaneh   if( pOrigVfs==0 ) return SQLITE_ERROR;
11548a922f75Sshaneh   assert( pOrigVfs!=&gMultiplex.sThisVfs );
11558a922f75Sshaneh   gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
11568a922f75Sshaneh   if( !gMultiplex.pMutex ){
11578a922f75Sshaneh     return SQLITE_NOMEM;
11588a922f75Sshaneh   }
11598a922f75Sshaneh   gMultiplex.pGroups = NULL;
11608a922f75Sshaneh   gMultiplex.isInitialized = 1;
11618a922f75Sshaneh   gMultiplex.pOrigVfs = pOrigVfs;
11628a922f75Sshaneh   gMultiplex.sThisVfs = *pOrigVfs;
11638a922f75Sshaneh   gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
1164d9523b97Sshaneh   gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
11658a922f75Sshaneh   gMultiplex.sThisVfs.xOpen = multiplexOpen;
1166b5830294Sshaneh   gMultiplex.sThisVfs.xDelete = multiplexDelete;
1167fffadc52Sdan   gMultiplex.sThisVfs.xAccess = multiplexAccess;
1168fffadc52Sdan   gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
1169fffadc52Sdan   gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
1170fffadc52Sdan   gMultiplex.sThisVfs.xDlError = multiplexDlError;
1171fffadc52Sdan   gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
1172fffadc52Sdan   gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
1173fffadc52Sdan   gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
1174fffadc52Sdan   gMultiplex.sThisVfs.xSleep = multiplexSleep;
1175fffadc52Sdan   gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
1176fffadc52Sdan   gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
1177fffadc52Sdan   gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
1178fffadc52Sdan 
11798a922f75Sshaneh   gMultiplex.sIoMethodsV1.iVersion = 1;
11808a922f75Sshaneh   gMultiplex.sIoMethodsV1.xClose = multiplexClose;
11818a922f75Sshaneh   gMultiplex.sIoMethodsV1.xRead = multiplexRead;
11828a922f75Sshaneh   gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
11838a922f75Sshaneh   gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
11848a922f75Sshaneh   gMultiplex.sIoMethodsV1.xSync = multiplexSync;
11858a922f75Sshaneh   gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
11868a922f75Sshaneh   gMultiplex.sIoMethodsV1.xLock = multiplexLock;
11878a922f75Sshaneh   gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
11888a922f75Sshaneh   gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
11898a922f75Sshaneh   gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
11908a922f75Sshaneh   gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
11918efdb732Sdrh   gMultiplex.sIoMethodsV1.xDeviceCharacteristics =
11928efdb732Sdrh                                             multiplexDeviceCharacteristics;
11938a922f75Sshaneh   gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
11948a922f75Sshaneh   gMultiplex.sIoMethodsV2.iVersion = 2;
11958a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
11968a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
11978a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
11988a922f75Sshaneh   gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
11998a922f75Sshaneh   sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
1200d50deeebSshaneh 
1201d50deeebSshaneh   sqlite3_auto_extension((void*)multiplexFuncInit);
1202d50deeebSshaneh 
12038a922f75Sshaneh   return SQLITE_OK;
12048a922f75Sshaneh }
12058a922f75Sshaneh 
12068a922f75Sshaneh /*
120778c4de4cSshaneh ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
12088a922f75Sshaneh **
12098a922f75Sshaneh ** All SQLite database connections must be closed before calling this
12108a922f75Sshaneh ** routine.
12118a922f75Sshaneh **
12128a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
12138a922f75Sshaneh ** shutting down in order to free all remaining multiplex groups.
12148a922f75Sshaneh */
1215a1a8298cSdrh int sqlite3_multiplex_shutdown(int eForce){
1216a1a8298cSdrh   int rc = SQLITE_OK;
12178a922f75Sshaneh   if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
1218a1a8298cSdrh   if( gMultiplex.pGroups ){
1219a1a8298cSdrh     sqlite3_log(SQLITE_MISUSE, "sqlite3_multiplex_shutdown() called "
1220a1a8298cSdrh                 "while database connections are still open");
1221a1a8298cSdrh     if( !eForce ) return SQLITE_MISUSE;
1222a1a8298cSdrh     rc = SQLITE_MISUSE;
1223a1a8298cSdrh   }
12248a922f75Sshaneh   gMultiplex.isInitialized = 0;
12258a922f75Sshaneh   sqlite3_mutex_free(gMultiplex.pMutex);
12268a922f75Sshaneh   sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
12278a922f75Sshaneh   memset(&gMultiplex, 0, sizeof(gMultiplex));
1228a1a8298cSdrh   return rc;
12298a922f75Sshaneh }
12308a922f75Sshaneh 
12318a922f75Sshaneh /***************************** Test Code ***********************************/
12328a922f75Sshaneh #ifdef SQLITE_TEST
12338a922f75Sshaneh #include <tcl.h>
1234e84d8d32Smistachkin extern const char *sqlite3ErrName(int);
12358a922f75Sshaneh 
12368a922f75Sshaneh 
12378a922f75Sshaneh /*
12388a922f75Sshaneh ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
12398a922f75Sshaneh */
12408a922f75Sshaneh static int test_multiplex_initialize(
12418a922f75Sshaneh   void * clientData,
12428a922f75Sshaneh   Tcl_Interp *interp,
12438a922f75Sshaneh   int objc,
12448a922f75Sshaneh   Tcl_Obj *CONST objv[]
12458a922f75Sshaneh ){
12468a922f75Sshaneh   const char *zName;              /* Name of new multiplex VFS */
12478a922f75Sshaneh   int makeDefault;                /* True to make the new VFS the default */
12488a922f75Sshaneh   int rc;                         /* Value returned by multiplex_initialize() */
12498a922f75Sshaneh 
12508a922f75Sshaneh   UNUSED_PARAMETER(clientData);
12518a922f75Sshaneh 
12528a922f75Sshaneh   /* Process arguments */
12538a922f75Sshaneh   if( objc!=3 ){
12548a922f75Sshaneh     Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
12558a922f75Sshaneh     return TCL_ERROR;
12568a922f75Sshaneh   }
12578a922f75Sshaneh   zName = Tcl_GetString(objv[1]);
12588a922f75Sshaneh   if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
12598a922f75Sshaneh   if( zName[0]=='\0' ) zName = 0;
12608a922f75Sshaneh 
12618a922f75Sshaneh   /* Call sqlite3_multiplex_initialize() */
12628a922f75Sshaneh   rc = sqlite3_multiplex_initialize(zName, makeDefault);
1263e84d8d32Smistachkin   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
12648a922f75Sshaneh 
12658a922f75Sshaneh   return TCL_OK;
12668a922f75Sshaneh }
12678a922f75Sshaneh 
12688a922f75Sshaneh /*
12698a922f75Sshaneh ** tclcmd: sqlite3_multiplex_shutdown
12708a922f75Sshaneh */
12718a922f75Sshaneh static int test_multiplex_shutdown(
12728a922f75Sshaneh   void * clientData,
12738a922f75Sshaneh   Tcl_Interp *interp,
12748a922f75Sshaneh   int objc,
12758a922f75Sshaneh   Tcl_Obj *CONST objv[]
12768a922f75Sshaneh ){
12778a922f75Sshaneh   int rc;                         /* Value returned by multiplex_shutdown() */
12788a922f75Sshaneh 
12798a922f75Sshaneh   UNUSED_PARAMETER(clientData);
12808a922f75Sshaneh 
1281a1a8298cSdrh   if( objc==2 && strcmp(Tcl_GetString(objv[1]),"-force")!=0 ){
1282a1a8298cSdrh     objc = 3;
1283a1a8298cSdrh   }
1284a1a8298cSdrh   if( (objc!=1 && objc!=2) ){
1285a1a8298cSdrh     Tcl_WrongNumArgs(interp, 1, objv, "?-force?");
12868a922f75Sshaneh     return TCL_ERROR;
12878a922f75Sshaneh   }
12888a922f75Sshaneh 
12898a922f75Sshaneh   /* Call sqlite3_multiplex_shutdown() */
1290a1a8298cSdrh   rc = sqlite3_multiplex_shutdown(objc==2);
1291e84d8d32Smistachkin   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
12928a922f75Sshaneh 
12938a922f75Sshaneh   return TCL_OK;
12948a922f75Sshaneh }
12958a922f75Sshaneh 
12968a922f75Sshaneh /*
12978a922f75Sshaneh ** tclcmd:  sqlite3_multiplex_dump
12988a922f75Sshaneh */
12998a922f75Sshaneh static int test_multiplex_dump(
13008a922f75Sshaneh   void * clientData,
13018a922f75Sshaneh   Tcl_Interp *interp,
13028a922f75Sshaneh   int objc,
13038a922f75Sshaneh   Tcl_Obj *CONST objv[]
13048a922f75Sshaneh ){
13058a922f75Sshaneh   Tcl_Obj *pResult;
13068a922f75Sshaneh   Tcl_Obj *pGroupTerm;
13078a922f75Sshaneh   multiplexGroup *pGroup;
13088a922f75Sshaneh   int i;
13098a922f75Sshaneh   int nChunks = 0;
13108a922f75Sshaneh 
13118a922f75Sshaneh   UNUSED_PARAMETER(clientData);
13128a922f75Sshaneh   UNUSED_PARAMETER(objc);
13138a922f75Sshaneh   UNUSED_PARAMETER(objv);
13148a922f75Sshaneh 
13158a922f75Sshaneh   pResult = Tcl_NewObj();
13168a922f75Sshaneh   multiplexEnter();
13178a922f75Sshaneh   for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
13188a922f75Sshaneh     pGroupTerm = Tcl_NewObj();
13198a922f75Sshaneh 
13205b1626aaSdrh     if( pGroup->zName ){
13218a922f75Sshaneh       pGroup->zName[pGroup->nName] = '\0';
13228a922f75Sshaneh       Tcl_ListObjAppendElement(interp, pGroupTerm,
13238a922f75Sshaneh           Tcl_NewStringObj(pGroup->zName, -1));
13245b1626aaSdrh     }else{
13255b1626aaSdrh       Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
13265b1626aaSdrh     }
13278a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
13288a922f75Sshaneh           Tcl_NewIntObj(pGroup->nName));
13298a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
13308a922f75Sshaneh           Tcl_NewIntObj(pGroup->flags));
13318a922f75Sshaneh 
13328a922f75Sshaneh     /* count number of chunks with open handles */
1333f3717af4Sdrh     for(i=0; i<pGroup->nReal; i++){
1334f3717af4Sdrh       if( pGroup->aReal[i].p!=0 ) nChunks++;
13358a922f75Sshaneh     }
13368a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
13378a922f75Sshaneh           Tcl_NewIntObj(nChunks));
13388a922f75Sshaneh 
13398a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
1340e6deb204Sdrh           Tcl_NewIntObj(pGroup->szChunk));
13418a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pGroupTerm,
1342f3717af4Sdrh           Tcl_NewIntObj(pGroup->nReal));
13438a922f75Sshaneh 
13448a922f75Sshaneh     Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
13458a922f75Sshaneh   }
13468a922f75Sshaneh   multiplexLeave();
13478a922f75Sshaneh   Tcl_SetObjResult(interp, pResult);
13488a922f75Sshaneh   return TCL_OK;
13498a922f75Sshaneh }
13508a922f75Sshaneh 
13518a922f75Sshaneh /*
1352d50deeebSshaneh ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
1353d50deeebSshaneh */
1354d50deeebSshaneh static int test_multiplex_control(
1355d50deeebSshaneh   ClientData cd,
1356d50deeebSshaneh   Tcl_Interp *interp,
1357d50deeebSshaneh   int objc,
1358d50deeebSshaneh   Tcl_Obj *CONST objv[]
1359d50deeebSshaneh ){
1360d50deeebSshaneh   int rc;                         /* Return code from file_control() */
1361d50deeebSshaneh   int idx;                        /* Index in aSub[] */
1362d50deeebSshaneh   Tcl_CmdInfo cmdInfo;            /* Command info structure for HANDLE */
1363d50deeebSshaneh   sqlite3 *db;                    /* Underlying db handle for HANDLE */
1364d50deeebSshaneh   int iValue = 0;
1365d50deeebSshaneh   void *pArg = 0;
1366d50deeebSshaneh 
1367d50deeebSshaneh   struct SubCommand {
1368d50deeebSshaneh     const char *zName;
1369d50deeebSshaneh     int op;
1370d50deeebSshaneh     int argtype;
1371d50deeebSshaneh   } aSub[] = {
1372d50deeebSshaneh     { "enable",       MULTIPLEX_CTRL_ENABLE,           1 },
1373d50deeebSshaneh     { "chunk_size",   MULTIPLEX_CTRL_SET_CHUNK_SIZE,   1 },
1374d50deeebSshaneh     { "max_chunks",   MULTIPLEX_CTRL_SET_MAX_CHUNKS,   1 },
1375d50deeebSshaneh     { 0, 0, 0 }
1376d50deeebSshaneh   };
1377d50deeebSshaneh 
1378c27fa4b0Sshaneh   if( objc!=5 ){
1379c27fa4b0Sshaneh     Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
1380d50deeebSshaneh     return TCL_ERROR;
1381d50deeebSshaneh   }
1382d50deeebSshaneh 
1383d50deeebSshaneh   if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
1384d50deeebSshaneh     Tcl_AppendResult(interp, "expected database handle, got \"", 0);
1385d50deeebSshaneh     Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
1386d50deeebSshaneh     return TCL_ERROR;
1387d50deeebSshaneh   }else{
1388d50deeebSshaneh     db = *(sqlite3 **)cmdInfo.objClientData;
1389d50deeebSshaneh   }
1390d50deeebSshaneh 
1391d50deeebSshaneh   rc = Tcl_GetIndexFromObjStruct(
1392d50deeebSshaneh       interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
1393d50deeebSshaneh   );
1394d50deeebSshaneh   if( rc!=TCL_OK ) return rc;
1395d50deeebSshaneh 
1396d50deeebSshaneh   switch( aSub[idx].argtype ){
1397d50deeebSshaneh     case 1:
1398d50deeebSshaneh       if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
1399d50deeebSshaneh         return TCL_ERROR;
1400d50deeebSshaneh       }
1401d50deeebSshaneh       pArg = (void *)&iValue;
1402d50deeebSshaneh       break;
1403d50deeebSshaneh     default:
1404d50deeebSshaneh       Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
1405d50deeebSshaneh       return TCL_ERROR;
1406d50deeebSshaneh   }
1407d50deeebSshaneh 
1408d50deeebSshaneh   rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
1409e84d8d32Smistachkin   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
1410d50deeebSshaneh   return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
1411d50deeebSshaneh }
1412d50deeebSshaneh 
1413d50deeebSshaneh /*
14148a922f75Sshaneh ** This routine registers the custom TCL commands defined in this
14158a922f75Sshaneh ** module.  This should be the only procedure visible from outside
14168a922f75Sshaneh ** of this module.
14178a922f75Sshaneh */
14188a922f75Sshaneh int Sqlitemultiplex_Init(Tcl_Interp *interp){
14198a922f75Sshaneh   static struct {
14208a922f75Sshaneh      char *zName;
14218a922f75Sshaneh      Tcl_ObjCmdProc *xProc;
14228a922f75Sshaneh   } aCmd[] = {
14238a922f75Sshaneh     { "sqlite3_multiplex_initialize", test_multiplex_initialize },
14248a922f75Sshaneh     { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
14258a922f75Sshaneh     { "sqlite3_multiplex_dump", test_multiplex_dump },
1426d50deeebSshaneh     { "sqlite3_multiplex_control", test_multiplex_control },
14278a922f75Sshaneh   };
14288a922f75Sshaneh   int i;
14298a922f75Sshaneh 
14308a922f75Sshaneh   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
14318a922f75Sshaneh     Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
14328a922f75Sshaneh   }
14338a922f75Sshaneh 
14348a922f75Sshaneh   return TCL_OK;
14358a922f75Sshaneh }
14368a922f75Sshaneh #endif
1437