-/*\r
-** 2010 October 28\r
-**\r
-** The author disclaims copyright to this source code. In place of\r
-** a legal notice, here is a blessing:\r
-**\r
-** May you do good and not evil.\r
-** May you find forgiveness for yourself and forgive others.\r
-** May you share freely, never taking more than you give.\r
-**\r
-*************************************************************************\r
-**\r
-** This file contains a VFS "shim" - a layer that sits in between the\r
-** pager and the real VFS.\r
-**\r
-** This particular shim enforces a multiplex system on DB files. \r
-** This shim shards/partitions a single DB file into smaller \r
-** "chunks" such that the total DB file size may exceed the maximum\r
-** file size of the underlying file system.\r
-**\r
-*/\r
-#include "sqlite3.h"\r
-#include <string.h>\r
-#include <assert.h>\r
-#include "sqliteInt.h"\r
-\r
-/************************ Shim Definitions ******************************/\r
-\r
-#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x80000000\r
-#define SQLITE_MULTIPLEX_MAX_CHUNKS 32\r
-\r
-/************************ Object Definitions ******************************/\r
-\r
-/* Forward declaration of all object types */\r
-typedef struct multiplexGroup multiplexGroup;\r
-typedef struct multiplexConn multiplexConn;\r
-\r
-/*\r
-** A "multiplex group" is a collection of files that collectively\r
-** makeup a single SQLite DB file. This allows the size of the DB\r
-** to exceed the limits imposed by the file system.\r
-**\r
-** There is an instance of the following object for each defined multiplex\r
-** group.\r
-*/\r
-struct multiplexGroup {\r
- sqlite3_file *pReal[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* Handles to each chunk */\r
- char bOpen[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* 0 if chunk not opened */\r
- char *zName; /* Base filename of this group */\r
- int nName; /* Length of base filename */\r
- int flags; /* Flags used for original opening */\r
- multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */\r
-};\r
-\r
-/*\r
-** An instance of the following object represents each open connection\r
-** to a file that is multiplex'ed. This object is a \r
-** subclass of sqlite3_file. The sqlite3_file object for the underlying\r
-** VFS is appended to this structure.\r
-*/\r
-struct multiplexConn {\r
- sqlite3_file base; /* Base class - must be first */\r
- multiplexGroup *pGroup; /* The underlying group of files */\r
-};\r
-\r
-/************************* Global Variables **********************************/\r
-/*\r
-** All global variables used by this file are containing within the following\r
-** gMultiplex structure.\r
-*/\r
-static struct {\r
- /* The pOrigVfs is the real, original underlying VFS implementation.\r
- ** Most operations pass-through to the real VFS. This value is read-only\r
- ** during operation. It is only modified at start-time and thus does not\r
- ** require a mutex.\r
- */\r
- sqlite3_vfs *pOrigVfs;\r
-\r
- /* The sThisVfs is the VFS structure used by this shim. It is initialized\r
- ** at start-time and thus does not require a mutex\r
- */\r
- sqlite3_vfs sThisVfs;\r
-\r
- /* The sIoMethods defines the methods used by sqlite3_file objects \r
- ** associated with this shim. It is initialized at start-time and does\r
- ** not require a mutex.\r
- **\r
- ** When the underlying VFS is called to open a file, it might return \r
- ** either a version 1 or a version 2 sqlite3_file object. This shim\r
- ** has to create a wrapper sqlite3_file of the same version. Hence\r
- ** there are two I/O method structures, one for version 1 and the other\r
- ** for version 2.\r
- */\r
- sqlite3_io_methods sIoMethodsV1;\r
- sqlite3_io_methods sIoMethodsV2;\r
-\r
- /* True when this shim as been initialized.\r
- */\r
- int isInitialized;\r
-\r
- /* For run-time access any of the other global data structures in this\r
- ** shim, the following mutex must be held.\r
- */\r
- sqlite3_mutex *pMutex;\r
-\r
- /* List of multiplexGroup objects.\r
- */\r
- multiplexGroup *pGroups;\r
-\r
- /* Chunk params\r
- */\r
- int nChunkSize;\r
- int nMaxChunks;\r
-\r
-} gMultiplex;\r
-\r
-/************************* Utility Routines *********************************/\r
-/*\r
-** Acquire and release the mutex used to serialize access to the\r
-** list of multiplexGroups.\r
-*/\r
-static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }\r
-static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }\r
-\r
-/* Translate an sqlite3_file* that is really a multiplexGroup* into\r
-** the sqlite3_file* for the underlying original VFS.\r
-*/\r
-static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){\r
- multiplexGroup *pGroup = pConn->pGroup;\r
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */\r
- if( iChunk<gMultiplex.nMaxChunks ){\r
- sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */\r
- if( !pGroup->bOpen[iChunk] ){\r
- pGroup->zName[pGroup->nName] = '\0';\r
- if( iChunk ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", iChunk);\r
- *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->zName, pSubOpen, pGroup->flags, pOutFlags);\r
- if( *rc==SQLITE_OK ){\r
- pGroup->bOpen[iChunk] = -1;\r
- return pSubOpen;\r
- }\r
- return NULL;\r
- }\r
- *rc = SQLITE_OK;\r
- return pSubOpen;\r
- }\r
- *rc = SQLITE_ERROR;\r
- return NULL;\r
-}\r
-\r
-/************************* VFS Method Wrappers *****************************/\r
-/*\r
-** This is the xOpen method used for the "multiplex" VFS.\r
-**\r
-** Most of the work is done by the underlying original VFS. This method\r
-** simply links the new file into the appropriate multiplex group if it is a\r
-** file that needs to be tracked.\r
-*/\r
-static int multiplexOpen(\r
- sqlite3_vfs *pVfs, /* The multiplex VFS */\r
- const char *zName, /* Name of file to be opened */\r
- sqlite3_file *pConn, /* Fill in this file descriptor */\r
- int flags, /* Flags to control the opening */\r
- int *pOutFlags /* Flags showing results of opening */\r
-){\r
- int rc; /* Result code */\r
- multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */\r
- multiplexGroup *pGroup; /* Corresponding multiplexGroup object */\r
- sqlite3_file *pSubOpen; /* Real file descriptor */\r
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */\r
- int nName = sqlite3Strlen30(zName);\r
- int i;\r
-\r
- UNUSED_PARAMETER(pVfs);\r
-\r
- /* If the file is not a main database file or a WAL, then use the\r
- ** normal xOpen method.\r
- */\r
- if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))==0 ){\r
- return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);\r
- }\r
-\r
- /* We need to create a group structure and manage\r
- ** access to this group of files.\r
- */\r
- multiplexEnter();\r
- pMultiplexOpen = (multiplexConn*)pConn;\r
- /* -0000\0 */\r
- pGroup = sqlite3_malloc( sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6 );\r
- if( pGroup==0 ){\r
- rc=SQLITE_NOMEM;\r
- }else{\r
- pMultiplexOpen->pGroup = pGroup;\r
- memset(pGroup, 0, sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6);\r
- for(i=0; i<gMultiplex.nMaxChunks; i++){\r
- pGroup->pReal[i] = (sqlite3_file *)((char *)&pGroup[1] + (pOrigVfs->szOsFile*i));\r
- }\r
- pGroup->zName = (char *)&pGroup[1] + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks);\r
- memcpy(pGroup->zName, zName, nName+1);\r
- pGroup->nName = nName;\r
- pGroup->flags = flags;\r
- pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);\r
- if( pSubOpen ){\r
- if( pSubOpen->pMethods->iVersion==1 ){\r
- pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;\r
- }else{\r
- pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;\r
- }\r
- /* place this group at the head of our list */\r
- pGroup->pNext = gMultiplex.pGroups;\r
- if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;\r
- gMultiplex.pGroups = pGroup;\r
- }else{\r
- sqlite3_free(pGroup);\r
- }\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/************************ I/O Method Wrappers *******************************/\r
-\r
-/* xClose requests get passed through to the original VFS.\r
-** We loop over all open chunk handles and close them.\r
-** The group structure for this file is unlinked from \r
-** our list of groups and freed.\r
-*/\r
-static int multiplexClose(sqlite3_file *pConn){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- multiplexGroup *pGroup = p->pGroup;\r
- int rc = SQLITE_OK;\r
- int i;\r
- multiplexEnter();\r
- for(i=0; i<gMultiplex.nMaxChunks; i++){\r
- if( pGroup->bOpen[i] ){\r
- sqlite3_file *pSubOpen = pGroup->pReal[i];\r
- int rc2 = pSubOpen->pMethods->xClose(pSubOpen);\r
- if( rc2!=SQLITE_OK ) rc = rc2;\r
- pGroup->bOpen[i] = 0;\r
- }\r
- }\r
- if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;\r
- if( pGroup->pPrev ){\r
- pGroup->pPrev->pNext = pGroup->pNext;\r
- }else{\r
- gMultiplex.pGroups = pGroup->pNext;\r
- }\r
- sqlite3_free(pGroup);\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xRead requests thru to the original VFS after\r
-** determining the correct chunk to operate on.\r
-*/\r
-static int multiplexRead(\r
- sqlite3_file *pConn,\r
- void *pBuf,\r
- int iAmt,\r
- sqlite3_int64 iOfst\r
-){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc = SQLITE_OK;\r
- multiplexEnter();\r
- while( iAmt > 0 ){\r
- int i = (int)(iOfst/gMultiplex.nChunkSize);\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);\r
- if( pSubOpen ){\r
- int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;\r
- if( extra<0 ) extra = 0;\r
- iAmt -= extra;\r
- rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);\r
- if( rc!=SQLITE_OK ) break;\r
- pBuf = (char *)pBuf + iAmt;\r
- iOfst += iAmt;\r
- iAmt = extra;\r
- }else{\r
- rc = SQLITE_IOERR_READ;\r
- break;\r
- }\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xWrite requests thru to the original VFS after\r
-** determining the correct chunk to operate on.\r
-*/\r
-static int multiplexWrite(\r
- sqlite3_file *pConn,\r
- const void *pBuf,\r
- int iAmt,\r
- sqlite3_int64 iOfst\r
-){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc = SQLITE_OK;\r
- multiplexEnter();\r
- while( iAmt > 0 ){\r
- int i = (int)(iOfst/gMultiplex.nChunkSize);\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);\r
- if( pSubOpen ){\r
- int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;\r
- if( extra<0 ) extra = 0;\r
- iAmt -= extra;\r
- rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);\r
- if( rc!=SQLITE_OK ) break;\r
- pBuf = (char *)pBuf + iAmt;\r
- iOfst += iAmt;\r
- iAmt = extra;\r
- }else{\r
- rc = SQLITE_IOERR_WRITE;\r
- break;\r
- }\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xTruncate requests thru to the original VFS after\r
-** determining the correct chunk to operate on. Delete any\r
-** chunks above the truncate mark.\r
-*/\r
-static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- multiplexGroup *pGroup = p->pGroup;\r
- int rc = SQLITE_OK;\r
- int rc2;\r
- int i;\r
- sqlite3_file *pSubOpen;\r
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */\r
- multiplexEnter();\r
- /* delete the chunks above the truncate limit */\r
- for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){\r
- if( pGroup->bOpen[i] ){\r
- pSubOpen = pGroup->pReal[i];\r
- rc2 = pSubOpen->pMethods->xClose(pSubOpen);\r
- if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;\r
- }\r
- pGroup->zName[pGroup->nName] = '\0';\r
- if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);\r
- rc2 = pOrigVfs->xDelete(pOrigVfs, pGroup->zName, 0);\r
- if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;\r
- }\r
- pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);\r
- if( pSubOpen ){\r
- rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);\r
- if( rc2!=SQLITE_OK ) rc = rc2;\r
- }else{\r
- rc = SQLITE_IOERR_TRUNCATE;\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xSync requests through to the original VFS without change\r
-*/\r
-static int multiplexSync(sqlite3_file *pConn, int flags){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- multiplexGroup *pGroup = p->pGroup;\r
- int rc = SQLITE_OK;\r
- int i;\r
- multiplexEnter();\r
- for(i=0; i<gMultiplex.nMaxChunks; i++){\r
- /* if we don't have it open, we don't need to sync it */\r
- if( pGroup->bOpen[i] ){\r
- sqlite3_file *pSubOpen = pGroup->pReal[i];\r
- int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);\r
- if( rc2!=SQLITE_OK ) rc = rc2;\r
- }\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xFileSize requests through to the original VFS.\r
-** Aggregate the size of all the chunks before returning.\r
-*/\r
-static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- multiplexGroup *pGroup = p->pGroup;\r
- int rc = SQLITE_OK;\r
- int rc2;\r
- int i;\r
- multiplexEnter();\r
- *pSize = 0;\r
- for(i=0; i<gMultiplex.nMaxChunks; i++){\r
- sqlite3_file *pSubOpen = NULL;\r
- sqlite3_int64 sz;\r
- /* if not opened already, check to see if the chunk exists */\r
- if( pGroup->bOpen[i] ){\r
- pSubOpen = pGroup->pReal[i];\r
- }else{\r
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */\r
- int exists = 0;\r
- pGroup->zName[pGroup->nName] = '\0';\r
- if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);\r
- rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->zName, SQLITE_ACCESS_EXISTS, &exists);\r
- if( rc2==SQLITE_OK && exists){\r
- /* if it exists, open it */\r
- pSubOpen = multiplexSubOpen(p, i, &rc, NULL);\r
- }else{\r
- /* stop at first "gap" */\r
- break;\r
- }\r
- }\r
- if( pSubOpen ){\r
- rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);\r
- if( rc2!=SQLITE_OK ){\r
- rc = rc2;\r
- }else{\r
- *pSize += sz;\r
- }\r
- }else{\r
- break;\r
- }\r
- }\r
- multiplexLeave();\r
- return rc;\r
-}\r
-\r
-/* Pass xLock requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexLock(sqlite3_file *pConn, int lock){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xLock(pSubOpen, lock);\r
- }\r
- return SQLITE_BUSY;\r
-}\r
-\r
-/* Pass xUnlock requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexUnlock(sqlite3_file *pConn, int lock){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xUnlock(pSubOpen, lock);\r
- }\r
- return SQLITE_IOERR_UNLOCK;\r
-}\r
-\r
-/* Pass xCheckReservedLock requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);\r
- }\r
- return SQLITE_IOERR_CHECKRESERVEDLOCK;\r
-}\r
-\r
-/* Pass xFileControl requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
+/*
+** 2010 October 28
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains a VFS "shim" - a layer that sits in between the
+** pager and the real VFS.
+**
+** This particular shim enforces a multiplex system on DB files.
+** This shim shards/partitions a single DB file into smaller
+** "chunks" such that the total DB file size may exceed the maximum
+** file size of the underlying file system.
+**
+*/
+#include "sqlite3.h"
+#include <string.h>
+#include <assert.h>
+#include "sqliteInt.h"
+
+/************************ Shim Definitions ******************************/
+
+#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x80000000
+#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
+
+/************************ Object Definitions ******************************/
+
+/* Forward declaration of all object types */
+typedef struct multiplexGroup multiplexGroup;
+typedef struct multiplexConn multiplexConn;
+
+/*
+** A "multiplex group" is a collection of files that collectively
+** makeup a single SQLite DB file. This allows the size of the DB
+** to exceed the limits imposed by the file system.
+**
+** There is an instance of the following object for each defined multiplex
+** group.
+*/
+struct multiplexGroup {
+ sqlite3_file *pReal[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* Handles to each chunk */
+ char bOpen[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* 0 if chunk not opened */
+ char *zName; /* Base filename of this group */
+ int nName; /* Length of base filename */
+ int flags; /* Flags used for original opening */
+ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
+};
+
+/*
+** An instance of the following object represents each open connection
+** to a file that is multiplex'ed. This object is a
+** subclass of sqlite3_file. The sqlite3_file object for the underlying
+** VFS is appended to this structure.
+*/
+struct multiplexConn {
+ sqlite3_file base; /* Base class - must be first */
+ multiplexGroup *pGroup; /* The underlying group of files */
+};
+
+/************************* Global Variables **********************************/
+/*
+** All global variables used by this file are containing within the following
+** gMultiplex structure.
+*/
+static struct {
+ /* The pOrigVfs is the real, original underlying VFS implementation.
+ ** Most operations pass-through to the real VFS. This value is read-only
+ ** during operation. It is only modified at start-time and thus does not
+ ** require a mutex.
+ */
+ sqlite3_vfs *pOrigVfs;
+
+ /* The sThisVfs is the VFS structure used by this shim. It is initialized
+ ** at start-time and thus does not require a mutex
+ */
+ sqlite3_vfs sThisVfs;
+
+ /* The sIoMethods defines the methods used by sqlite3_file objects
+ ** associated with this shim. It is initialized at start-time and does
+ ** not require a mutex.
+ **
+ ** When the underlying VFS is called to open a file, it might return
+ ** either a version 1 or a version 2 sqlite3_file object. This shim
+ ** has to create a wrapper sqlite3_file of the same version. Hence
+ ** there are two I/O method structures, one for version 1 and the other
+ ** for version 2.
+ */
+ sqlite3_io_methods sIoMethodsV1;
+ sqlite3_io_methods sIoMethodsV2;
+
+ /* True when this shim has been initialized.
+ */
+ int isInitialized;
+
+ /* For run-time access any of the other global data structures in this
+ ** shim, the following mutex must be held.
+ */
+ sqlite3_mutex *pMutex;
+
+ /* List of multiplexGroup objects.
+ */
+ multiplexGroup *pGroups;
+
+ /* Chunk params.
+ */
+ int nChunkSize;
+ int nMaxChunks;
+
+} gMultiplex;
+
+/************************* Utility Routines *********************************/
+/*
+** Acquire and release the mutex used to serialize access to the
+** list of multiplexGroups.
+*/
+static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
+static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
+
+/* Translate an sqlite3_file* that is really a multiplexGroup* into
+** the sqlite3_file* for the underlying original VFS.
+*/
+static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
+ multiplexGroup *pGroup = pConn->pGroup;
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ if( iChunk<gMultiplex.nMaxChunks ){
+ sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */
+ if( !pGroup->bOpen[iChunk] ){
+ pGroup->zName[pGroup->nName] = '\0';
+ if( iChunk ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", iChunk);
+ *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->zName, pSubOpen, pGroup->flags, pOutFlags);
+ if( *rc==SQLITE_OK ){
+ pGroup->bOpen[iChunk] = -1;
+ return pSubOpen;
+ }
+ return NULL;
+ }
+ *rc = SQLITE_OK;
+ return pSubOpen;
+ }
+ *rc = SQLITE_ERROR;
+ return NULL;
+}
+
+/************************* VFS Method Wrappers *****************************/
+/*
+** This is the xOpen method used for the "multiplex" VFS.
+**
+** Most of the work is done by the underlying original VFS. This method
+** simply links the new file into the appropriate multiplex group if it is a
+** file that needs to be tracked.
+*/
+static int multiplexOpen(
+ sqlite3_vfs *pVfs, /* The multiplex VFS */
+ const char *zName, /* Name of file to be opened */
+ sqlite3_file *pConn, /* Fill in this file descriptor */
+ int flags, /* Flags to control the opening */
+ int *pOutFlags /* Flags showing results of opening */
+){
+ int rc; /* Result code */
+ multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
+ multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
+ sqlite3_file *pSubOpen; /* Real file descriptor */
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ int nName = sqlite3Strlen30(zName);
+ int i;
+
+ UNUSED_PARAMETER(pVfs);
+
+ /* If the file is not a main database file or a WAL, then use the
+ ** normal xOpen method.
+ */
+ if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))==0 ){
+ return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
+ }
+
+ /* We need to create a group structure and manage
+ ** access to this group of files.
+ */
+ multiplexEnter();
+ pMultiplexOpen = (multiplexConn*)pConn;
+ /* allocate space for group, file handles,
+ ** and file name (+ extra for "-0000\0")
+ */
+ pGroup = sqlite3_malloc( sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6 );
+ if( pGroup==0 ){
+ rc=SQLITE_NOMEM;
+ }else{
+ pMultiplexOpen->pGroup = pGroup;
+ memset(pGroup, 0, sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6);
+ /* assign pointers to extra space for file handles */
+ for(i=0; i<gMultiplex.nMaxChunks; i++){
+ pGroup->pReal[i] = (sqlite3_file *)((char *)&pGroup[1] + (pOrigVfs->szOsFile*i));
+ }
+ pGroup->zName = (char *)&pGroup[1] + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks);
+ memcpy(pGroup->zName, zName, nName+1);
+ pGroup->nName = nName;
+ pGroup->flags = flags;
+ pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
+ if( pSubOpen ){
+ if( pSubOpen->pMethods->iVersion==1 ){
+ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
+ }else{
+ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
+ }
+ /* place this group at the head of our list */
+ pGroup->pNext = gMultiplex.pGroups;
+ if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
+ gMultiplex.pGroups = pGroup;
+ }else{
+ sqlite3_free(pGroup);
+ }
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/************************ I/O Method Wrappers *******************************/
+
+/* xClose requests get passed through to the original VFS.
+** We loop over all open chunk handles and close them.
+** The group structure for this file is unlinked from
+** our list of groups and freed.
+*/
+static int multiplexClose(sqlite3_file *pConn){
+ multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
+ int rc = SQLITE_OK;
+ int i;
+ multiplexEnter();
+ for(i=0; i<gMultiplex.nMaxChunks; i++){
+ if( pGroup->bOpen[i] ){
+ sqlite3_file *pSubOpen = pGroup->pReal[i];
+ int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
+ if( rc2!=SQLITE_OK ) rc = rc2;
+ pGroup->bOpen[i] = 0;
+ }
+ }
+ if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
+ if( pGroup->pPrev ){
+ pGroup->pPrev->pNext = pGroup->pNext;
+ }else{
+ gMultiplex.pGroups = pGroup->pNext;
+ }
+ sqlite3_free(pGroup);
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xRead requests thru to the original VFS after
+** determining the correct chunk to operate on.
+** Break up reads across chunk boundaries.
+*/
+static int multiplexRead(
+ sqlite3_file *pConn,
+ void *pBuf,
+ int iAmt,
+ sqlite3_int64 iOfst
+){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc = SQLITE_OK;
+ multiplexEnter();
+ while( iAmt > 0 ){
+ int i = (int)(iOfst/gMultiplex.nChunkSize);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_READ;
+ break;
+ }
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xWrite requests thru to the original VFS after
+** determining the correct chunk to operate on.
+** Break up writes across chunk boundaries.
+*/
+static int multiplexWrite(
+ sqlite3_file *pConn,
+ const void *pBuf,
+ int iAmt,
+ sqlite3_int64 iOfst
+){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc = SQLITE_OK;
+ multiplexEnter();
+ while( iAmt > 0 ){
+ int i = (int)(iOfst/gMultiplex.nChunkSize);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_WRITE;
+ break;
+ }
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xTruncate requests thru to the original VFS after
+** determining the correct chunk to operate on. Delete any
+** chunks above the truncate mark.
+*/
+static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
+ multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
+ int rc = SQLITE_OK;
+ int rc2;
+ int i;
+ sqlite3_file *pSubOpen;
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ multiplexEnter();
+ /* delete the chunks above the truncate limit */
+ for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
+ /* close any open chunks before deleting them */
+ if( pGroup->bOpen[i] ){
+ pSubOpen = pGroup->pReal[i];
+ rc2 = pSubOpen->pMethods->xClose(pSubOpen);
+ if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
+ }
+ if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);
+ rc2 = pOrigVfs->xDelete(pOrigVfs, pGroup->zName, 0);
+ if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
+ }
+ pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
+ if( pSubOpen ){
+ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
+ if( rc2!=SQLITE_OK ) rc = rc2;
+ }else{
+ rc = SQLITE_IOERR_TRUNCATE;
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xSync requests through to the original VFS without change
+*/
+static int multiplexSync(sqlite3_file *pConn, int flags){
+ multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
+ int rc = SQLITE_OK;
+ int i;
+ multiplexEnter();
+ for(i=0; i<gMultiplex.nMaxChunks; i++){
+ /* if we don't have it open, we don't need to sync it */
+ if( pGroup->bOpen[i] ){
+ sqlite3_file *pSubOpen = pGroup->pReal[i];
+ int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
+ if( rc2!=SQLITE_OK ) rc = rc2;
+ }
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xFileSize requests through to the original VFS.
+** Aggregate the size of all the chunks before returning.
+*/
+static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
+ multiplexConn *p = (multiplexConn*)pConn;
+ multiplexGroup *pGroup = p->pGroup;
+ int rc = SQLITE_OK;
+ int rc2;
+ int i;
+ multiplexEnter();
+ *pSize = 0;
+ for(i=0; i<gMultiplex.nMaxChunks; i++){
+ sqlite3_file *pSubOpen = NULL;
+ /* if not opened already, check to see if the chunk exists */
+ if( pGroup->bOpen[i] ){
+ pSubOpen = pGroup->pReal[i];
+ }else{
+ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
+ int exists = 0;
+ pGroup->zName[pGroup->nName] = '\0';
+ if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);
+ rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->zName, SQLITE_ACCESS_EXISTS, &exists);
+ if( rc2==SQLITE_OK && exists){
+ /* if it exists, open it */
+ pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
+ }else{
+ /* stop at first "gap" */
+ break;
+ }
+ }
+ if( pSubOpen ){
+ sqlite3_int64 sz;
+ rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( rc2!=SQLITE_OK ){
+ rc = rc2;
+ }else{
+ *pSize += sz;
+ }
+ }else{
+ break;
+ }
+ }
+ multiplexLeave();
+ return rc;
+}
+
+/* Pass xLock requests through to the original VFS unchanged.
+*/
+static int multiplexLock(sqlite3_file *pConn, int lock){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xLock(pSubOpen, lock);
+ }
+ return SQLITE_BUSY;
+}
+
+/* Pass xUnlock requests through to the original VFS unchanged.
+*/
+static int multiplexUnlock(sqlite3_file *pConn, int lock){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
+ }
+ return SQLITE_IOERR_UNLOCK;
+}
+
+/* Pass xCheckReservedLock requests through to the original VFS unchanged.
+*/
+static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
+ }
+ return SQLITE_IOERR_CHECKRESERVEDLOCK;
+}
+
+/* Pass xFileControl requests through to the original VFS unchanged.
+*/
+static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
sqlite3_file *pSubOpen;
if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
- pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);\r
- }\r
- return SQLITE_ERROR;\r
-}\r
-\r
-/* Pass xSectorSize requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexSectorSize(sqlite3_file *pConn){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xSectorSize(pSubOpen);\r
- }\r
- return SQLITE_DEFAULT_SECTOR_SIZE;\r
-}\r
-\r
-/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexDeviceCharacteristics(sqlite3_file *pConn){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);\r
- }\r
- return 0;\r
-}\r
-\r
-/* Pass xShmMap requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexShmMap(\r
- sqlite3_file *pConn, /* Handle open on database file */\r
- int iRegion, /* Region to retrieve */\r
- int szRegion, /* Size of regions */\r
- int bExtend, /* True to extend file if necessary */\r
- void volatile **pp /* OUT: Mapped memory */\r
-){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);\r
- }\r
- return SQLITE_IOERR;\r
-}\r
-\r
-/* Pass xShmLock requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexShmLock(\r
- sqlite3_file *pConn, /* Database file holding the shared memory */\r
- int ofst, /* First lock to acquire or release */\r
- int n, /* Number of locks to acquire or release */\r
- int flags /* What to do with the lock */\r
-){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);\r
- }\r
- return SQLITE_BUSY;\r
-}\r
-\r
-/* Pass xShmBarrier requests through to the original VFS unchanged.\r
-*/\r
-static void multiplexShmBarrier(sqlite3_file *pConn){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- pSubOpen->pMethods->xShmBarrier(pSubOpen);\r
- }\r
-}\r
-\r
-/* Pass xShmUnmap requests through to the original VFS unchanged.\r
-*/\r
-static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){\r
- multiplexConn *p = (multiplexConn*)pConn;\r
- int rc;\r
- sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);\r
- if( pSubOpen ){\r
- return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);\r
- }\r
- return SQLITE_OK;\r
-}\r
-\r
-/************************** Public Interfaces *****************************/\r
-/*\r
-** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName\r
-** as the VFS that does the actual work. Use the default if\r
-** zOrigVfsName==NULL. \r
-**\r
-** The multiplex VFS shim is named "multiplex". It will become the default\r
-** VFS if makeDefault is non-zero.\r
-**\r
-** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once\r
-** during start-up.\r
-*/\r
-int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){\r
- sqlite3_vfs *pOrigVfs;\r
- if( gMultiplex.isInitialized ) return SQLITE_MISUSE;\r
- pOrigVfs = sqlite3_vfs_find(zOrigVfsName);\r
- if( pOrigVfs==0 ) return SQLITE_ERROR;\r
- assert( pOrigVfs!=&gMultiplex.sThisVfs );\r
- gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);\r
- if( !gMultiplex.pMutex ){\r
- return SQLITE_NOMEM;\r
- }\r
- gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;\r
- gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;\r
- gMultiplex.pGroups = NULL;\r
- gMultiplex.isInitialized = 1;\r
- gMultiplex.pOrigVfs = pOrigVfs;\r
- gMultiplex.sThisVfs = *pOrigVfs;\r
+ pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
+ }
+ return SQLITE_ERROR;
+}
+
+/* Pass xSectorSize requests through to the original VFS unchanged.
+*/
+static int multiplexSectorSize(sqlite3_file *pConn){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xSectorSize(pSubOpen);
+ }
+ return SQLITE_DEFAULT_SECTOR_SIZE;
+}
+
+/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
+*/
+static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
+ }
+ return 0;
+}
+
+/* Pass xShmMap requests through to the original VFS unchanged.
+*/
+static int multiplexShmMap(
+ sqlite3_file *pConn, /* Handle open on database file */
+ int iRegion, /* Region to retrieve */
+ int szRegion, /* Size of regions */
+ int bExtend, /* True to extend file if necessary */
+ void volatile **pp /* OUT: Mapped memory */
+){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
+ }
+ return SQLITE_IOERR;
+}
+
+/* Pass xShmLock requests through to the original VFS unchanged.
+*/
+static int multiplexShmLock(
+ sqlite3_file *pConn, /* Database file holding the shared memory */
+ int ofst, /* First lock to acquire or release */
+ int n, /* Number of locks to acquire or release */
+ int flags /* What to do with the lock */
+){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
+ }
+ return SQLITE_BUSY;
+}
+
+/* Pass xShmBarrier requests through to the original VFS unchanged.
+*/
+static void multiplexShmBarrier(sqlite3_file *pConn){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ pSubOpen->pMethods->xShmBarrier(pSubOpen);
+ }
+}
+
+/* Pass xShmUnmap requests through to the original VFS unchanged.
+*/
+static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
+ multiplexConn *p = (multiplexConn*)pConn;
+ int rc;
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ if( pSubOpen ){
+ return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
+ }
+ return SQLITE_OK;
+}
+
+/************************** Public Interfaces *****************************/
+/*
+** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName
+** as the VFS that does the actual work. Use the default if
+** zOrigVfsName==NULL.
+**
+** The multiplex VFS shim is named "multiplex". It will become the default
+** VFS if makeDefault is non-zero.
+**
+** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
+** during start-up.
+*/
+int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
+ sqlite3_vfs *pOrigVfs;
+ if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
+ pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
+ if( pOrigVfs==0 ) return SQLITE_ERROR;
+ assert( pOrigVfs!=&gMultiplex.sThisVfs );
+ gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( !gMultiplex.pMutex ){
+ return SQLITE_NOMEM;
+ }
+ gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
+ gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
+ gMultiplex.pGroups = NULL;
+ gMultiplex.isInitialized = 1;
+ gMultiplex.pOrigVfs = pOrigVfs;
+ gMultiplex.sThisVfs = *pOrigVfs;
gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
- gMultiplex.sThisVfs.zName = "multiplex";\r
- gMultiplex.sThisVfs.xOpen = multiplexOpen;\r
- gMultiplex.sIoMethodsV1.iVersion = 1;\r
- gMultiplex.sIoMethodsV1.xClose = multiplexClose;\r
- gMultiplex.sIoMethodsV1.xRead = multiplexRead;\r
- gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;\r
- gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;\r
- gMultiplex.sIoMethodsV1.xSync = multiplexSync;\r
- gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;\r
- gMultiplex.sIoMethodsV1.xLock = multiplexLock;\r
- gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;\r
- gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;\r
- gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;\r
- gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;\r
- gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;\r
- gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;\r
- gMultiplex.sIoMethodsV2.iVersion = 2;\r
- gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;\r
- gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;\r
- gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;\r
- gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;\r
- sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);\r
- return SQLITE_OK;\r
-}\r
-\r
-/*\r
-** Shutdown the multiplex system.\r
-**\r
-** All SQLite database connections must be closed before calling this\r
-** routine.\r
-**\r
-** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while\r
-** shutting down in order to free all remaining multiplex groups.\r
-*/\r
-int sqlite3_multiplex_shutdown(void){\r
- if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;\r
- if( gMultiplex.pGroups ) return SQLITE_MISUSE;\r
- gMultiplex.isInitialized = 0;\r
- sqlite3_mutex_free(gMultiplex.pMutex);\r
- sqlite3_vfs_unregister(&gMultiplex.sThisVfs);\r
- memset(&gMultiplex, 0, sizeof(gMultiplex));\r
- return SQLITE_OK;\r
-}\r
-\r
-/*\r
-** Adjust chunking params. VFS should be initialized first.\r
-** No files should be open. Re-intializing will reset these\r
-** to the default.\r
+ gMultiplex.sThisVfs.zName = "multiplex";
+ gMultiplex.sThisVfs.xOpen = multiplexOpen;
+ gMultiplex.sIoMethodsV1.iVersion = 1;
+ gMultiplex.sIoMethodsV1.xClose = multiplexClose;
+ gMultiplex.sIoMethodsV1.xRead = multiplexRead;
+ gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
+ gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
+ gMultiplex.sIoMethodsV1.xSync = multiplexSync;
+ gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
+ gMultiplex.sIoMethodsV1.xLock = multiplexLock;
+ gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
+ gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
+ gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
+ gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
+ gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
+ gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
+ gMultiplex.sIoMethodsV2.iVersion = 2;
+ gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
+ gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
+ gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
+ gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
+ sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
+ return SQLITE_OK;
+}
+
+/*
+** Shutdown the multiplex system.
+**
+** All SQLite database connections must be closed before calling this
+** routine.
+**
+** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
+** shutting down in order to free all remaining multiplex groups.
+*/
+int sqlite3_multiplex_shutdown(void){
+ if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
+ if( gMultiplex.pGroups ) return SQLITE_MISUSE;
+ gMultiplex.isInitialized = 0;
+ sqlite3_mutex_free(gMultiplex.pMutex);
+ sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
+ memset(&gMultiplex, 0, sizeof(gMultiplex));
+ return SQLITE_OK;
+}
+
+/*
+** Adjust chunking params. VFS should be initialized first.
+** No files should be open. Re-intializing will reset these
+** to the default.
*/
int sqlite3_multiplex_set(
- int nChunkSize, /* Max chunk size */\r
- int nMaxChunks /* Max number of chunks */\r
+ int nChunkSize, /* Max chunk size */
+ int nMaxChunks /* Max number of chunks */
){
- if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;\r
- if( gMultiplex.pGroups ) return SQLITE_MISUSE;\r
- if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ) return SQLITE_MISUSE;\r
+ if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
+ if( gMultiplex.pGroups ) return SQLITE_MISUSE;
+ if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ) return SQLITE_MISUSE;
multiplexEnter();
- gMultiplex.nChunkSize = nChunkSize;\r
- gMultiplex.nMaxChunks = nMaxChunks;\r
+ gMultiplex.nChunkSize = nChunkSize;
+ gMultiplex.nMaxChunks = nMaxChunks;
multiplexLeave();
return SQLITE_OK;
}
-\r
-/***************************** Test Code ***********************************/\r
-#ifdef SQLITE_TEST\r
-#include <tcl.h>\r
-\r
-extern const char *sqlite3TestErrorName(int);\r
-\r
-\r
-/*\r
-** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT\r
-*/\r
-static int test_multiplex_initialize(\r
- void * clientData,\r
- Tcl_Interp *interp,\r
- int objc,\r
- Tcl_Obj *CONST objv[]\r
-){\r
- const char *zName; /* Name of new multiplex VFS */\r
- int makeDefault; /* True to make the new VFS the default */\r
- int rc; /* Value returned by multiplex_initialize() */\r
-\r
- UNUSED_PARAMETER(clientData);\r
-\r
- /* Process arguments */\r
- if( objc!=3 ){\r
- Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");\r
- return TCL_ERROR;\r
- }\r
- zName = Tcl_GetString(objv[1]);\r
- if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;\r
- if( zName[0]=='\0' ) zName = 0;\r
-\r
- /* Call sqlite3_multiplex_initialize() */\r
- rc = sqlite3_multiplex_initialize(zName, makeDefault);\r
- Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);\r
-\r
- return TCL_OK;\r
-}\r
-\r
-/*\r
-** tclcmd: sqlite3_multiplex_shutdown\r
-*/\r
-static int test_multiplex_shutdown(\r
- void * clientData,\r
- Tcl_Interp *interp,\r
- int objc,\r
- Tcl_Obj *CONST objv[]\r
-){\r
- int rc; /* Value returned by multiplex_shutdown() */\r
-\r
- UNUSED_PARAMETER(clientData);\r
-\r
- if( objc!=1 ){\r
- Tcl_WrongNumArgs(interp, 1, objv, "");\r
- return TCL_ERROR;\r
- }\r
-\r
- /* Call sqlite3_multiplex_shutdown() */\r
- rc = sqlite3_multiplex_shutdown();\r
- Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);\r
-\r
- return TCL_OK;\r
-}\r
-\r
+
+/***************************** Test Code ***********************************/
+#ifdef SQLITE_TEST
+#include <tcl.h>
+
+extern const char *sqlite3TestErrorName(int);
+
+
+/*
+** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
+*/
+static int test_multiplex_initialize(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zName; /* Name of new multiplex VFS */
+ int makeDefault; /* True to make the new VFS the default */
+ int rc; /* Value returned by multiplex_initialize() */
+
+ UNUSED_PARAMETER(clientData);
+
+ /* Process arguments */
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
+ return TCL_ERROR;
+ }
+ zName = Tcl_GetString(objv[1]);
+ if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
+ if( zName[0]=='\0' ) zName = 0;
+
+ /* Call sqlite3_multiplex_initialize() */
+ rc = sqlite3_multiplex_initialize(zName, makeDefault);
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
+
+ return TCL_OK;
+}
+
+/*
+** tclcmd: sqlite3_multiplex_shutdown
+*/
+static int test_multiplex_shutdown(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc; /* Value returned by multiplex_shutdown() */
+
+ UNUSED_PARAMETER(clientData);
+
+ if( objc!=1 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "");
+ return TCL_ERROR;
+ }
+
+ /* Call sqlite3_multiplex_shutdown() */
+ rc = sqlite3_multiplex_shutdown();
+ Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
+
+ return TCL_OK;
+}
+
/*
** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
*/
int objc,
Tcl_Obj *CONST objv[]
){
- int nChunkSize; /* Max chunk size */\r
- int nMaxChunks; /* Max number of chunks */\r
+ int nChunkSize; /* Max chunk size */
+ int nMaxChunks; /* Max number of chunks */
int rc; /* Value returned by sqlite3_multiplex_set() */
- UNUSED_PARAMETER(clientData);\r
+ UNUSED_PARAMETER(clientData);
/* Process arguments */
if( objc!=3 ){
return TCL_OK;
}
-/*\r
-** tclcmd: sqlite3_multiplex_dump\r
-*/\r
-static int test_multiplex_dump(\r
- void * clientData,\r
- Tcl_Interp *interp,\r
- int objc,\r
- Tcl_Obj *CONST objv[]\r
-){\r
- Tcl_Obj *pResult;\r
- Tcl_Obj *pGroupTerm;\r
- multiplexGroup *pGroup;\r
- int i;\r
- int nChunks = 0;\r
-\r
- UNUSED_PARAMETER(clientData);\r
- UNUSED_PARAMETER(objc);\r
- UNUSED_PARAMETER(objv);\r
-\r
- pResult = Tcl_NewObj();\r
- multiplexEnter();\r
- for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){\r
- pGroupTerm = Tcl_NewObj();\r
-\r
- pGroup->zName[pGroup->nName] = '\0';\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewStringObj(pGroup->zName, -1));\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewIntObj(pGroup->nName));\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewIntObj(pGroup->flags));\r
-\r
- /* count number of chunks with open handles */\r
- for(i=0; i<gMultiplex.nMaxChunks; i++){\r
- if( pGroup->bOpen[i] ) nChunks++;\r
- }\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewIntObj(nChunks));\r
-\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewIntObj(gMultiplex.nChunkSize));\r
- Tcl_ListObjAppendElement(interp, pGroupTerm,\r
- Tcl_NewIntObj(gMultiplex.nMaxChunks));\r
-\r
- Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);\r
- }\r
- multiplexLeave();\r
- Tcl_SetObjResult(interp, pResult);\r
- return TCL_OK;\r
-}\r
-\r
-/*\r
-** This routine registers the custom TCL commands defined in this\r
-** module. This should be the only procedure visible from outside\r
-** of this module.\r
-*/\r
-int Sqlitemultiplex_Init(Tcl_Interp *interp){\r
- static struct {\r
- char *zName;\r
- Tcl_ObjCmdProc *xProc;\r
- } aCmd[] = {\r
- { "sqlite3_multiplex_initialize", test_multiplex_initialize },\r
- { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },\r
+/*
+** tclcmd: sqlite3_multiplex_dump
+*/
+static int test_multiplex_dump(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ Tcl_Obj *pResult;
+ Tcl_Obj *pGroupTerm;
+ multiplexGroup *pGroup;
+ int i;
+ int nChunks = 0;
+
+ UNUSED_PARAMETER(clientData);
+ UNUSED_PARAMETER(objc);
+ UNUSED_PARAMETER(objv);
+
+ pResult = Tcl_NewObj();
+ multiplexEnter();
+ for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
+ pGroupTerm = Tcl_NewObj();
+
+ pGroup->zName[pGroup->nName] = '\0';
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewStringObj(pGroup->zName, -1));
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewIntObj(pGroup->nName));
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewIntObj(pGroup->flags));
+
+ /* count number of chunks with open handles */
+ for(i=0; i<gMultiplex.nMaxChunks; i++){
+ if( pGroup->bOpen[i] ) nChunks++;
+ }
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewIntObj(nChunks));
+
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewIntObj(gMultiplex.nChunkSize));
+ Tcl_ListObjAppendElement(interp, pGroupTerm,
+ Tcl_NewIntObj(gMultiplex.nMaxChunks));
+
+ Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
+ }
+ multiplexLeave();
+ Tcl_SetObjResult(interp, pResult);
+ return TCL_OK;
+}
+
+/*
+** This routine registers the custom TCL commands defined in this
+** module. This should be the only procedure visible from outside
+** of this module.
+*/
+int Sqlitemultiplex_Init(Tcl_Interp *interp){
+ static struct {
+ char *zName;
+ Tcl_ObjCmdProc *xProc;
+ } aCmd[] = {
+ { "sqlite3_multiplex_initialize", test_multiplex_initialize },
+ { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
{ "sqlite3_multiplex_set", test_multiplex_set },
- { "sqlite3_multiplex_dump", test_multiplex_dump },\r
- };\r
- int i;\r
-\r
- for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){\r
- Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);\r
- }\r
-\r
- return TCL_OK;\r
-}\r
-#endif\r
+ { "sqlite3_multiplex_dump", test_multiplex_dump },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
+ }
+
+ return TCL_OK;
+}
+#endif