]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Additional tests and commenting for the multiplex VFS.
authorshaneh <shaneh@noemail.net>
Fri, 5 Nov 2010 03:43:54 +0000 (03:43 +0000)
committershaneh <shaneh@noemail.net>
Fri, 5 Nov 2010 03:43:54 +0000 (03:43 +0000)
FossilOrigin-Name: 8ed944ba74566c6241d655a39801ad192c61219a

manifest
manifest.uuid
src/test_multiplex.c
test/multiplex.test

index 2313dc3516ca0ce3402bf0344d847b7e3aad0bd3..36d8c95a95afa28fbd06c236304837c16c8b2b4a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,8 +1,5 @@
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA1
-
-C Update\sthe\sconfigure\sscript\sfor\sversion\s3.7.4.
-D 2010-11-05T00:04:58
+C Additional\stests\sand\scommenting\sfor\sthe\smultiplex\sVFS.
+D 2010-11-05T03:43:55
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -210,7 +207,7 @@ F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
 F src/test_journal.c 785edd54f963aefb3c1628124170a56697c68c70
 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
 F src/test_malloc.c 09a88f0c111201dc4f8c20470aa1b5f611d59200
-F src/test_multiplex.c be9c1445dca81ab9c77278b85b827cd446c656a8
+F src/test_multiplex.c 8103a0ae1b36cdaf63ef620233e62b786c585d08
 F src/test_mutex.c ce06b59aca168cd8c520b77159a24352a7469bd3
 F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec
 F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c
@@ -562,7 +559,7 @@ F test/misc5.test 45b2e3ed5f79af2b4f38ae362eaf4c49674575bd
 F test/misc6.test 953cc693924d88e6117aeba16f46f0bf5abede91
 F test/misc7.test c5f4e6a82e04e71820c0f9f64f6733f04c8ae0ae
 F test/misuse.test 30b3a458e5a70c31e74c291937b6c82204c59f33
-F test/multiplex.test 257af7decc32a50814adffbb6865e1a7eb9f42c3
+F test/multiplex.test 2f6830ad67e0a26d28658b4ed3ff07c909075eb8
 F test/mutex1.test 5b71777fc127509cd257910c8db799de557a02de
 F test/mutex2.test bfeaeac2e73095b2ac32285d2756e3a65e681660
 F test/nan.test a44e04df1486fcfb02d32468cbcd3c8e1e433723
@@ -888,14 +885,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 1635d927cd434052303e0e75f58a08a1e2fefd86
-R f7dd2f31340d63087de508ba8a0dfd99
-U drh
-Z e60feb74cf4f45ee711966b22bff87da
------BEGIN PGP SIGNATURE-----
-Version: GnuPG v1.4.6 (GNU/Linux)
-
-iD8DBQFM00otoxKgR168RlERAqgdAJwNcVzN46ivHhr0EG8GIx9OxWYy7wCfRo0u
-D2s9GCrWd2zJen6rIIewqYU=
-=ilDh
------END PGP SIGNATURE-----
+P 18a73795b427e3d04a8a437410a271a50bbe32e4
+R 7a583acd4959f9dd49542ab66e09f45d
+U shaneh
+Z 0c53acc363de7c9bf67ad1c52d50bdb6
index 1119bb53e955b02453851c105bd1d90c89161014..b14723cf9ef9c3dda0d43ef056151b0a4c01e067 100644 (file)
@@ -1 +1 @@
-18a73795b427e3d04a8a437410a271a50bbe32e4
\ No newline at end of file
+8ed944ba74566c6241d655a39801ad192c61219a
\ No newline at end of file
index d8c047300b1d68c1da757eeffbe8407a629f9812..e002c78bd6df355c5a3bc7bd8d935de6d22248ee 100644 (file)
-/*\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
 */
@@ -713,11 +718,11 @@ static int test_multiplex_set(
   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 ){
@@ -739,78 +744,78 @@ static int test_multiplex_set(
   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
index 92c1a9d649235abc3925ccd1ecf280ae453bfe0b..9495c6542192c045edb97e66eeac78cb37af351e 100644 (file)
@@ -26,6 +26,9 @@ proc multiplex_delete {name} {
 
 db close
 
+#-------------------------------------------------------------------------
+#   multiplex-1.1.*: Test initialize and shutdown.
+
 do_test multiplex-1.1 { sqlite3_multiplex_initialize nosuchvfs 1 } {SQLITE_ERROR}
 do_test multiplex-1.2 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
 do_test multiplex-1.3 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_MISUSE}
@@ -52,6 +55,7 @@ do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
 #                afterwards. Then close the database and successfully shut
 #                down the multiplex system.
 #
+#   multiplex-2.5.*: More reading/writing with small chunk size.
 
 sqlite3_multiplex_initialize "" 1
 sqlite3_multiplex_set 0x8000 16
@@ -96,6 +100,58 @@ do_test multiplex-2.4.99 {
   sqlite3_multiplex_shutdown
 } {SQLITE_OK}
 
+
+do_test multiplex-2.5.1 {
+  multiplex_delete test.db
+  sqlite3_multiplex_initialize "" 1
+  sqlite3_multiplex_set 0x1000 16
+} {SQLITE_OK}
+
+do_test multiplex-2.5.2 {
+  sqlite3 db test.db
+  execsql {
+    PRAGMA page_size = 1024;
+    PRAGMA journal_mode = delete;
+    PRAGMA auto_vacuum = off;
+    CREATE TABLE t1(a PRIMARY KEY, b);
+  }
+} {delete}
+
+do_test multiplex-2.5.3 { 
+  execsql { 
+    INSERT INTO t1 VALUES(1, 'one');
+    INSERT INTO t1 VALUES(2, randomblob(4000));
+    INSERT INTO t1 VALUES(3, 'three');
+    INSERT INTO t1 VALUES(4, randomblob(4000));
+    INSERT INTO t1 VALUES(5, 'five') 
+  }
+} {}
+
+do_test multiplex-2.5.4 {
+  db eval {SELECT * FROM t1 WHERE a=1}
+} {1 one}
+
+do_test multiplex-2.5.5 {
+  db eval {SELECT * FROM t1 WHERE a=3}
+} {3 three}
+
+do_test multiplex-2.5.6 {
+  db eval {SELECT * FROM t1 WHERE a=5}
+} {5 five}
+
+do_test multiplex-2.5.7 {
+  db eval {SELECT a,length(b) FROM t1 WHERE a=2}
+} {2 4000}
+
+do_test multiplex-2.5.8 {
+  db eval {SELECT a,length(b) FROM t1 WHERE a=4}
+} {4 4000}
+
+do_test multiplex-2.5.99 {
+  db close
+  sqlite3_multiplex_shutdown
+} {SQLITE_OK}
+
 #-------------------------------------------------------------------------
 # Try some tests with more than one connection to a database file. Still
 # in rollback mode.