/ Changes On Branch multiplex-enhancements
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch multiplex-enhancements Excluding Merge-Ins

This is equivalent to a diff from def98fd23e to 718f1ad7df

2011-04-01
18:39
Merge the multiplexer enhancements back into the trunk. (check-in: 2c125710cb user: drh tags: trunk)
14:22
Removed dependency on sqliteInt.h so that multiplex VFS shim can be compiled as loadable module. (Closed-Leaf check-in: 718f1ad7df user: shaneh tags: multiplex-enhancements)
2011-03-31
15:11
Enable/disable support. (check-in: b3c6d9aa9e user: shaneh tags: multiplex-enhancements)
2011-03-15
18:35
Fix an out-of-order variable declaration in shell.c. (check-in: 7257084650 user: drh tags: trunk)
04:45
Allow multiplex file names to be preceeded by prefix of the form ":multiplex:chunksize:maxchunks:" Still work to be done, though it compiles and prefixes are ignored. (check-in: cfa4a2f7ea user: shaneh tags: multiplex-enhancements)
02:55
Fix cut-and-paste typo in debugging print statement in winMutexTry(). (check-in: def98fd23e user: shaneh tags: trunk)
2011-03-14
13:54
Merge the unix-excl VFS into the trunk. This merge also adds the -vfs option to the command-line shell. (check-in: 3934b004e9 user: drh tags: trunk)

Changes to src/test_multiplex.c.

    18     18   ** "chunks" such that the total DB file size may exceed the maximum
    19     19   ** file size of the underlying file system.
    20     20   **
    21     21   */
    22     22   #include "sqlite3.h"
    23     23   #include <string.h>
    24     24   #include <assert.h>
    25         -#include "sqliteInt.h"
           25  +#include "test_multiplex.h"
           26  +
           27  +#ifndef SQLITE_CORE
           28  +  #define SQLITE_CORE 1  /* Disable the API redefinition in sqlite3ext.h */
           29  +#endif
           30  +#include "sqlite3ext.h"
           31  +
           32  +/* 
           33  +** These should be defined to be the same as the values in 
           34  +** sqliteInt.h.  They are defined seperately here so that
           35  +** the multiplex VFS shim can be built as a loadable 
           36  +** module.
           37  +*/
           38  +#define UNUSED_PARAMETER(x) (void)(x)
           39  +#define MAX_PAGE_SIZE       0x10000
           40  +#define DEFAULT_SECTOR_SIZE 0x1000
    26     41   
    27     42   /*
    28     43   ** For a build without mutexes, no-op the mutex calls.
    29     44   */
    30     45   #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
    31     46   #define sqlite3_mutex_alloc(X)    ((sqlite3_mutex*)8)
    32     47   #define sqlite3_mutex_free(X)
................................................................................
    35     50   #define sqlite3_mutex_leave(X)
    36     51   #define sqlite3_mutex_held(X)     ((void)(X),1)
    37     52   #define sqlite3_mutex_notheld(X)  ((void)(X),1)
    38     53   #endif /* SQLITE_THREADSAFE==0 */
    39     54   
    40     55   
    41     56   /************************ Shim Definitions ******************************/
           57  +
           58  +#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
    42     59   
    43     60   /* This is the limit on the chunk size.  It may be changed by calling
    44         -** the sqlite3_multiplex_set() interface.
           61  +** the xFileControl() interface.  It will be rounded up to a 
           62  +** multiple of MAX_PAGE_SIZE.  We default it here to 1GB.
    45     63   */
    46         -#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
           64  +#define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384)
           65  +
    47     66   /* Default limit on number of chunks.  Care should be taken
    48     67   ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
    49     68   ** format specifier. It may be changed by calling
    50         -** the sqlite3_multiplex_set() interface.
           69  +** the xFileControl() interface.
    51     70   */
    52     71   #define SQLITE_MULTIPLEX_MAX_CHUNKS 32
    53     72   
    54     73   /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the 
    55     74   ** last SQLITE_MULTIPLEX_EXT_SZ characters of the 
    56     75   ** filename will be overwritten, otherwise, the 
    57     76   ** multiplex extension is simply appended to the filename.
................................................................................
    78     97   */
    79     98   struct multiplexGroup {
    80     99     sqlite3_file **pReal;            /* Handles to each chunk */
    81    100     char *bOpen;                     /* array of bools - 0 if chunk not opened */
    82    101     char *zName;                     /* Base filename of this group */
    83    102     int nName;                       /* Length of base filename */
    84    103     int flags;                       /* Flags used for original opening */
          104  +  int nChunkSize;                  /* Chunk size used for this group */
          105  +  int nMaxChunks;                  /* Max number of chunks for this group */
          106  +  int bEnabled;                    /* TRUE to use Multiplex VFS for this file */
    85    107     multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
    86    108   };
    87    109   
    88    110   /*
    89    111   ** An instance of the following object represents each open connection
    90    112   ** to a file that is multiplex'ed.  This object is a 
    91    113   ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
................................................................................
   136    158     */
   137    159     sqlite3_mutex *pMutex;
   138    160   
   139    161     /* List of multiplexGroup objects.
   140    162     */
   141    163     multiplexGroup *pGroups;
   142    164   
   143         -  /* Chunk params.
   144         -  */
   145         -  int nChunkSize;
   146         -  int nMaxChunks;
   147         -
   148    165     /* Storage for temp file names.  Allocated during 
   149    166     ** initialization to the max pathname of the underlying VFS.
   150    167     */
   151    168     char *zName;
   152    169   
   153    170   } gMultiplex;
   154    171   
................................................................................
   155    172   /************************* Utility Routines *********************************/
   156    173   /*
   157    174   ** Acquire and release the mutex used to serialize access to the
   158    175   ** list of multiplexGroups.
   159    176   */
   160    177   static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
   161    178   static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
          179  +
          180  +/*
          181  +** Compute a string length that is limited to what can be stored in
          182  +** lower 30 bits of a 32-bit signed integer.
          183  +**
          184  +** The value returned will never be negative.  Nor will it ever be greater
          185  +** than the actual length of the string.  For very long strings (greater
          186  +** than 1GiB) the value returned might be less than the true string length.
          187  +*/
          188  +int multiplexStrlen30(const char *z){
          189  +  const char *z2 = z;
          190  +  if( z==0 ) return 0;
          191  +  while( *z2 ){ z2++; }
          192  +  return 0x3fffffff & (int)(z2 - z);
          193  +}
   162    194   
   163    195   /* Translate an sqlite3_file* that is really a multiplexGroup* into
   164    196   ** the sqlite3_file* for the underlying original VFS.
   165    197   */
   166    198   static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
   167    199     multiplexGroup *pGroup = pConn->pGroup;
   168    200     sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
   169         -  if( iChunk<gMultiplex.nMaxChunks ){
          201  +  if( iChunk<pGroup->nMaxChunks ){
   170    202       sqlite3_file *pSubOpen = pGroup->pReal[iChunk];    /* Real file descriptor */
   171    203       if( !pGroup->bOpen[iChunk] ){
   172    204         memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
   173    205         if( iChunk ){
   174    206   #ifdef SQLITE_MULTIPLEX_EXT_OVWR
   175    207           sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
   176    208   #else
................................................................................
   186    218       }
   187    219       *rc = SQLITE_OK;
   188    220       return pSubOpen;
   189    221     }
   190    222     *rc = SQLITE_FULL;
   191    223     return NULL;
   192    224   }
          225  +
          226  +/*
          227  +** This is the implementation of the multiplex_control() SQL function.
          228  +*/
          229  +static void multiplexControlFunc(
          230  +  sqlite3_context *context,
          231  +  int argc,
          232  +  sqlite3_value **argv
          233  +){
          234  +  int rc = SQLITE_OK;
          235  +  sqlite3 *db = sqlite3_context_db_handle(context);
          236  +  int op;
          237  +  int iVal;
          238  +
          239  +  if( !db || argc!=2 ){ 
          240  +    rc = SQLITE_ERROR; 
          241  +  }else{
          242  +    /* extract params */
          243  +    op = sqlite3_value_int(argv[0]);
          244  +    iVal = sqlite3_value_int(argv[1]);
          245  +    /* map function op to file_control op */
          246  +    switch( op ){
          247  +      case 1: 
          248  +        op = MULTIPLEX_CTRL_ENABLE; 
          249  +        break;
          250  +      case 2: 
          251  +        op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; 
          252  +        break;
          253  +      case 3: 
          254  +        op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; 
          255  +        break;
          256  +      default:
          257  +        rc = SQLITE_NOTFOUND;
          258  +        break;
          259  +    }
          260  +  }
          261  +  if( rc==SQLITE_OK ){
          262  +    rc = sqlite3_file_control(db, 0, op, &iVal);
          263  +  }
          264  +  sqlite3_result_error_code(context, rc);
          265  +}
          266  +
          267  +/*
          268  +** This is the entry point to register the auto-extension for the 
          269  +** multiplex_control() function.
          270  +*/
          271  +static int multiplexFuncInit(
          272  +  sqlite3 *db, 
          273  +  char **pzErrMsg, 
          274  +  const sqlite3_api_routines *pApi
          275  +){
          276  +  int rc;
          277  +  rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 
          278  +      0, multiplexControlFunc, 0, 0);
          279  +  return rc;
          280  +}
   193    281   
   194    282   /************************* VFS Method Wrappers *****************************/
   195    283   
   196    284   /*
   197    285   ** This is the xOpen method used for the "multiplex" VFS.
   198    286   **
   199    287   ** Most of the work is done by the underlying original VFS.  This method
................................................................................
   208    296     int *pOutFlags             /* Flags showing results of opening */
   209    297   ){
   210    298     int rc;                                        /* Result code */
   211    299     multiplexConn *pMultiplexOpen;                 /* The new multiplex file descriptor */
   212    300     multiplexGroup *pGroup;                        /* Corresponding multiplexGroup object */
   213    301     sqlite3_file *pSubOpen;                        /* Real file descriptor */
   214    302     sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   215         -  int nName = sqlite3Strlen30(zName);
          303  +  int nName = multiplexStrlen30(zName);
   216    304     int i;
   217    305     int sz;
   218    306   
   219    307     UNUSED_PARAMETER(pVfs);
   220    308   
   221    309     /* We need to create a group structure and manage
   222    310     ** access to this group of files.
   223    311     */
   224    312     multiplexEnter();
   225    313     pMultiplexOpen = (multiplexConn*)pConn;
   226    314     /* allocate space for group */
   227         -  sz = sizeof(multiplexGroup)                         /* multiplexGroup */
   228         -     + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
   229         -     + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks)     /* *pReal */
   230         -     + gMultiplex.nMaxChunks                          /* bOpen[] */
   231         -     + nName + 1;                                     /* zName */
          315  +  sz = sizeof(multiplexGroup)                                /* multiplexGroup */
          316  +     + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS)  /* pReal[] */
          317  +     + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS)      /* *pReal */
          318  +     + SQLITE_MULTIPLEX_MAX_CHUNKS                           /* bOpen[] */
          319  +     + nName + 1;                                            /* zName */
   232    320   #ifndef SQLITE_MULTIPLEX_EXT_OVWR
   233    321     sz += SQLITE_MULTIPLEX_EXT_SZ;
   234    322     assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
   235    323   #else
   236    324     assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
   237    325     assert(nName < pOrigVfs->mxPathname);
   238    326   #endif
................................................................................
   240    328     if( pGroup==0 ){
   241    329       rc=SQLITE_NOMEM;
   242    330     }else{
   243    331       /* assign pointers to extra space allocated */
   244    332       char *p = (char *)&pGroup[1];
   245    333       pMultiplexOpen->pGroup = pGroup;
   246    334       memset(pGroup, 0, sz);
          335  +    pGroup->bEnabled = -1;
          336  +    pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
          337  +    pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
   247    338       pGroup->pReal = (sqlite3_file **)p;
   248         -    p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
   249         -    for(i=0; i<gMultiplex.nMaxChunks; i++){
          339  +    p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
          340  +    for(i=0; i<pGroup->nMaxChunks; i++){
   250    341         pGroup->pReal[i] = (sqlite3_file *)p;
   251    342         p += pOrigVfs->szOsFile;
   252    343       }
          344  +    /* bOpen[] vals should all be zero from memset above */
   253    345       pGroup->bOpen = p;
   254         -    p += gMultiplex.nMaxChunks;
          346  +    p += pGroup->nMaxChunks;
   255    347       pGroup->zName = p;
   256    348       /* save off base filename, name length, and original open flags  */
   257    349       memcpy(pGroup->zName, zName, nName+1);
   258    350       pGroup->nName = nName;
   259    351       pGroup->flags = flags;
   260    352       pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
   261    353       if( pSubOpen ){
          354  +      /* if this file is already larger than chunk size, disable 
          355  +      ** the multiplex feature.
          356  +      */
          357  +      sqlite3_int64 sz;
          358  +      int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
          359  +      if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
          360  +        pGroup->bEnabled = 0;
          361  +      }
   262    362         if( pSubOpen->pMethods->iVersion==1 ){
   263    363           pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
   264    364         }else{
   265    365           pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
   266    366         }
   267    367         /* place this group at the head of our list */
   268    368         pGroup->pNext = gMultiplex.pGroups;
................................................................................
   284    384   static int multiplexDelete(
   285    385     sqlite3_vfs *pVfs,         /* The multiplex VFS */
   286    386     const char *zName,         /* Name of file to delete */
   287    387     int syncDir
   288    388   ){
   289    389     sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   290    390     int rc = SQLITE_OK;
   291         -  int nName = sqlite3Strlen30(zName);
          391  +  int nName = multiplexStrlen30(zName);
   292    392     int i;
   293    393   
   294    394     UNUSED_PARAMETER(pVfs);
   295    395   
   296    396     multiplexEnter();
   297    397     memcpy(gMultiplex.zName, zName, nName+1);
   298         -  for(i=0; i<gMultiplex.nMaxChunks; i++){
          398  +  for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){
   299    399       int rc2;
   300    400       int exists = 0;
   301    401       if( i ){
   302    402   #ifdef SQLITE_MULTIPLEX_EXT_OVWR
   303         -        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
          403  +        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          404  +            gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, 
          405  +            SQLITE_MULTIPLEX_EXT_FMT, i);
   304    406   #else
   305         -        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
          407  +        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          408  +            gMultiplex.zName+nName, 
          409  +            SQLITE_MULTIPLEX_EXT_FMT, i);
   306    410   #endif
   307    411       }
   308         -    rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
          412  +    rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, 
          413  +        SQLITE_ACCESS_EXISTS, &exists);
   309    414       if( rc2==SQLITE_OK && exists){
   310    415         /* if it exists, delete it */
   311    416         rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
   312    417         if( rc2!=SQLITE_OK ) rc = rc2;
   313    418       }else{
   314    419         /* stop at first "gap" */
   315    420         break;
................................................................................
   363    468   static int multiplexClose(sqlite3_file *pConn){
   364    469     multiplexConn *p = (multiplexConn*)pConn;
   365    470     multiplexGroup *pGroup = p->pGroup;
   366    471     int rc = SQLITE_OK;
   367    472     int i;
   368    473     multiplexEnter();
   369    474     /* close any open handles */
   370         -  for(i=0; i<gMultiplex.nMaxChunks; i++){
          475  +  for(i=0; i<pGroup->nMaxChunks; i++){
   371    476       if( pGroup->bOpen[i] ){
   372    477         sqlite3_file *pSubOpen = pGroup->pReal[i];
   373    478         int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
   374    479         if( rc2!=SQLITE_OK ) rc = rc2;
   375    480         pGroup->bOpen[i] = 0;
   376    481       }
   377    482     }
................................................................................
   394    499   static int multiplexRead(
   395    500     sqlite3_file *pConn,
   396    501     void *pBuf,
   397    502     int iAmt,
   398    503     sqlite3_int64 iOfst
   399    504   ){
   400    505     multiplexConn *p = (multiplexConn*)pConn;
          506  +  multiplexGroup *pGroup = p->pGroup;
   401    507     int rc = SQLITE_OK;
   402    508     multiplexEnter();
   403         -  while( iAmt > 0 ){
   404         -    int i = (int)(iOfst/gMultiplex.nChunkSize);
   405         -    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
   406         -    if( pSubOpen ){
   407         -      int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
   408         -      if( extra<0 ) extra = 0;
   409         -      iAmt -= extra;
   410         -      rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
   411         -      if( rc!=SQLITE_OK ) break;
   412         -      pBuf = (char *)pBuf + iAmt;
   413         -      iOfst += iAmt;
   414         -      iAmt = extra;
   415         -    }else{
   416         -      rc = SQLITE_IOERR_READ;
   417         -      break;
          509  +  if( !pGroup->bEnabled ){
          510  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          511  +    rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
          512  +  }else{
          513  +    while( iAmt > 0 ){
          514  +      int i = (int)(iOfst / pGroup->nChunkSize);
          515  +      sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          516  +      if( pSubOpen ){
          517  +        int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
          518  +        if( extra<0 ) extra = 0;
          519  +        iAmt -= extra;
          520  +        rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
          521  +        if( rc!=SQLITE_OK ) break;
          522  +        pBuf = (char *)pBuf + iAmt;
          523  +        iOfst += iAmt;
          524  +        iAmt = extra;
          525  +      }else{
          526  +        rc = SQLITE_IOERR_READ;
          527  +        break;
          528  +      }
   418    529       }
   419    530     }
   420    531     multiplexLeave();
   421    532     return rc;
   422    533   }
   423    534   
   424    535   /* Pass xWrite requests thru to the original VFS after
................................................................................
   428    539   static int multiplexWrite(
   429    540     sqlite3_file *pConn,
   430    541     const void *pBuf,
   431    542     int iAmt,
   432    543     sqlite3_int64 iOfst
   433    544   ){
   434    545     multiplexConn *p = (multiplexConn*)pConn;
          546  +  multiplexGroup *pGroup = p->pGroup;
   435    547     int rc = SQLITE_OK;
   436    548     multiplexEnter();
   437         -  while( iAmt > 0 ){
   438         -    int i = (int)(iOfst/gMultiplex.nChunkSize);
   439         -    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
   440         -    if( pSubOpen ){
   441         -      int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
   442         -      if( extra<0 ) extra = 0;
   443         -      iAmt -= extra;
   444         -      rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
   445         -      if( rc!=SQLITE_OK ) break;
   446         -      pBuf = (char *)pBuf + iAmt;
   447         -      iOfst += iAmt;
   448         -      iAmt = extra;
   449         -    }else{
   450         -      rc = SQLITE_IOERR_WRITE;
   451         -      break;
          549  +  if( !pGroup->bEnabled ){
          550  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          551  +    rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
          552  +  }else{
          553  +    while( iAmt > 0 ){
          554  +      int i = (int)(iOfst / pGroup->nChunkSize);
          555  +      sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          556  +      if( pSubOpen ){
          557  +        int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
          558  +        if( extra<0 ) extra = 0;
          559  +        iAmt -= extra;
          560  +        rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
          561  +        if( rc!=SQLITE_OK ) break;
          562  +        pBuf = (char *)pBuf + iAmt;
          563  +        iOfst += iAmt;
          564  +        iAmt = extra;
          565  +      }else{
          566  +        rc = SQLITE_IOERR_WRITE;
          567  +        break;
          568  +      }
   452    569       }
   453    570     }
   454    571     multiplexLeave();
   455    572     return rc;
   456    573   }
   457    574   
   458    575   /* Pass xTruncate requests thru to the original VFS after
................................................................................
   459    576   ** determining the correct chunk to operate on.  Delete any
   460    577   ** chunks above the truncate mark.
   461    578   */
   462    579   static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
   463    580     multiplexConn *p = (multiplexConn*)pConn;
   464    581     multiplexGroup *pGroup = p->pGroup;
   465    582     int rc = SQLITE_OK;
   466         -  int rc2;
   467         -  int i;
   468         -  sqlite3_file *pSubOpen;
   469         -  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   470    583     multiplexEnter();
   471         -  memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
   472         -  /* delete the chunks above the truncate limit */
   473         -  for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
   474         -    /* close any open chunks before deleting them */
   475         -    if( pGroup->bOpen[i] ){
   476         -      pSubOpen = pGroup->pReal[i];
   477         -      rc2 = pSubOpen->pMethods->xClose(pSubOpen);
   478         -      if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
   479         -      pGroup->bOpen[i] = 0;
   480         -    }
          584  +  if( !pGroup->bEnabled ){
          585  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          586  +    rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
          587  +  }else{
          588  +    int rc2;
          589  +    int i;
          590  +    sqlite3_file *pSubOpen;
          591  +    sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
          592  +    memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
          593  +    /* delete the chunks above the truncate limit */
          594  +    for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){
          595  +      /* close any open chunks before deleting them */
          596  +      if( pGroup->bOpen[i] ){
          597  +        pSubOpen = pGroup->pReal[i];
          598  +        rc2 = pSubOpen->pMethods->xClose(pSubOpen);
          599  +        if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
          600  +        pGroup->bOpen[i] = 0;
          601  +      }
   481    602   #ifdef SQLITE_MULTIPLEX_EXT_OVWR
   482         -    sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
          603  +      sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          604  +          gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, 
          605  +          SQLITE_MULTIPLEX_EXT_FMT, i);
   483    606   #else
   484         -    sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
          607  +      sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          608  +          gMultiplex.zName+pGroup->nName, 
          609  +          SQLITE_MULTIPLEX_EXT_FMT, i);
   485    610   #endif
   486         -    rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
   487         -    if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
   488         -  }
   489         -  pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
   490         -  if( pSubOpen ){
   491         -    rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
   492         -    if( rc2!=SQLITE_OK ) rc = rc2;
   493         -  }else{
   494         -    rc = SQLITE_IOERR_TRUNCATE;
          611  +      rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
          612  +      if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
          613  +    }
          614  +    pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL);
          615  +    if( pSubOpen ){
          616  +      rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
          617  +      if( rc2!=SQLITE_OK ) rc = rc2;
          618  +    }else{
          619  +      rc = SQLITE_IOERR_TRUNCATE;
          620  +    }
   495    621     }
   496    622     multiplexLeave();
   497    623     return rc;
   498    624   }
   499    625   
   500    626   /* Pass xSync requests through to the original VFS without change
   501    627   */
   502    628   static int multiplexSync(sqlite3_file *pConn, int flags){
   503    629     multiplexConn *p = (multiplexConn*)pConn;
   504    630     multiplexGroup *pGroup = p->pGroup;
   505    631     int rc = SQLITE_OK;
   506    632     int i;
   507    633     multiplexEnter();
   508         -  for(i=0; i<gMultiplex.nMaxChunks; i++){
          634  +  for(i=0; i<pGroup->nMaxChunks; i++){
   509    635       /* if we don't have it open, we don't need to sync it */
   510    636       if( pGroup->bOpen[i] ){
   511    637         sqlite3_file *pSubOpen = pGroup->pReal[i];
   512    638         int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
   513    639         if( rc2!=SQLITE_OK ) rc = rc2;
   514    640       }
   515    641     }
................................................................................
   523    649   static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
   524    650     multiplexConn *p = (multiplexConn*)pConn;
   525    651     multiplexGroup *pGroup = p->pGroup;
   526    652     int rc = SQLITE_OK;
   527    653     int rc2;
   528    654     int i;
   529    655     multiplexEnter();
   530         -  *pSize = 0;
   531         -  for(i=0; i<gMultiplex.nMaxChunks; i++){
   532         -    sqlite3_file *pSubOpen = NULL;
   533         -    /* if not opened already, check to see if the chunk exists */
   534         -    if( pGroup->bOpen[i] ){
   535         -      pSubOpen = pGroup->pReal[i];
   536         -    }else{
   537         -      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   538         -      int exists = 0;
   539         -      memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
   540         -      if( i ){
          656  +  if( !pGroup->bEnabled ){
          657  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          658  +    rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
          659  +  }else{
          660  +    *pSize = 0;
          661  +    for(i=0; i<pGroup->nMaxChunks; i++){
          662  +      sqlite3_file *pSubOpen = NULL;
          663  +      /* if not opened already, check to see if the chunk exists */
          664  +      if( pGroup->bOpen[i] ){
          665  +        pSubOpen = pGroup->pReal[i];
          666  +      }else{
          667  +        sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
          668  +        int exists = 0;
          669  +        memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
          670  +        if( i ){
   541    671   #ifdef SQLITE_MULTIPLEX_EXT_OVWR
   542         -        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
          672  +          sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          673  +              gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, 
          674  +              SQLITE_MULTIPLEX_EXT_FMT, i);
   543    675   #else
   544         -        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
          676  +          sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 
          677  +              gMultiplex.zName+pGroup->nName, 
          678  +              SQLITE_MULTIPLEX_EXT_FMT, i);
   545    679   #endif
          680  +        }
          681  +        rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, 
          682  +            SQLITE_ACCESS_EXISTS, &exists);
          683  +        if( rc2==SQLITE_OK && exists){
          684  +          /* if it exists, open it */
          685  +          pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          686  +        }else{
          687  +          /* stop at first "gap" */
          688  +          break;
          689  +        }
   546    690         }
   547         -      rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
   548         -      if( rc2==SQLITE_OK && exists){
   549         -        /* if it exists, open it */
   550         -        pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          691  +      if( pSubOpen ){
          692  +        sqlite3_int64 sz;
          693  +        rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
          694  +        if( rc2!=SQLITE_OK ){
          695  +          rc = rc2;
          696  +        }else{
          697  +          if( sz>pGroup->nChunkSize ){
          698  +            rc = SQLITE_IOERR_FSTAT;
          699  +          }
          700  +          *pSize += sz;
          701  +        }
   551    702         }else{
   552         -        /* stop at first "gap" */
   553    703           break;
   554    704         }
   555    705       }
   556         -    if( pSubOpen ){
   557         -      sqlite3_int64 sz;
   558         -      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
   559         -      if( rc2!=SQLITE_OK ){
   560         -        rc = rc2;
   561         -      }else{
   562         -        if( sz>gMultiplex.nChunkSize ){
   563         -          rc = SQLITE_IOERR_FSTAT;
   564         -        }
   565         -        *pSize += sz;
   566         -      }
   567         -    }else{
   568         -      break;
   569         -    }
   570    706     }
   571    707     multiplexLeave();
   572    708     return rc;
   573    709   }
   574    710   
   575    711   /* Pass xLock requests through to the original VFS unchanged.
   576    712   */
................................................................................
   604    740     sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
   605    741     if( pSubOpen ){
   606    742       return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
   607    743     }
   608    744     return SQLITE_IOERR_CHECKRESERVEDLOCK;
   609    745   }
   610    746   
   611         -/* Pass xFileControl requests through to the original VFS unchanged.
          747  +/* Pass xFileControl requests through to the original VFS unchanged,
          748  +** except for any MULTIPLEX_CTRL_* requests here.
   612    749   */
   613    750   static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
   614    751     multiplexConn *p = (multiplexConn*)pConn;
   615         -  int rc;
          752  +  multiplexGroup *pGroup = p->pGroup;
          753  +  int rc = SQLITE_ERROR;
   616    754     sqlite3_file *pSubOpen;
   617         -  if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
   618         -  pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
   619         -  if( pSubOpen ){
   620         -    return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
          755  +
          756  +  if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
          757  +  switch( op ){
          758  +    case MULTIPLEX_CTRL_ENABLE:
          759  +      if( pArg ) {
          760  +        int bEnabled = *(int *)pArg;
          761  +        pGroup->bEnabled = bEnabled;
          762  +        rc = SQLITE_OK;
          763  +      }
          764  +      break;
          765  +    case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
          766  +      if( pArg ) {
          767  +        int nChunkSize = *(int *)pArg;
          768  +        if( nChunkSize<1 ){
          769  +          rc = SQLITE_MISUSE;
          770  +        }else{
          771  +          /* Round up to nearest multiple of MAX_PAGE_SIZE. */
          772  +          nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1));
          773  +          nChunkSize &= ~(MAX_PAGE_SIZE-1);
          774  +          pGroup->nChunkSize = nChunkSize;
          775  +          rc = SQLITE_OK;
          776  +        }
          777  +      }
          778  +      break;
          779  +    case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
          780  +      if( pArg ) {
          781  +        int nMaxChunks = *(int *)pArg;
          782  +        if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){
          783  +          rc = SQLITE_MISUSE;
          784  +        }else{
          785  +          pGroup->nMaxChunks = nMaxChunks;
          786  +          rc = SQLITE_OK;
          787  +        }
          788  +      }
          789  +      break;
          790  +    case SQLITE_FCNTL_SIZE_HINT:
          791  +    case SQLITE_FCNTL_CHUNK_SIZE:
          792  +      /* no-op these */
          793  +      rc = SQLITE_OK;
          794  +      break;
          795  +    default:
          796  +      pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          797  +      if( pSubOpen ){
          798  +        rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
          799  +      }
          800  +      break;
   621    801     }
   622         -  return SQLITE_ERROR;
          802  +  return rc;
   623    803   }
   624    804   
   625    805   /* Pass xSectorSize requests through to the original VFS unchanged.
   626    806   */
   627    807   static int multiplexSectorSize(sqlite3_file *pConn){
   628    808     multiplexConn *p = (multiplexConn*)pConn;
   629    809     int rc;
   630    810     sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
   631    811     if( pSubOpen ){
   632    812       return pSubOpen->pMethods->xSectorSize(pSubOpen);
   633    813     }
   634         -  return SQLITE_DEFAULT_SECTOR_SIZE;
          814  +  return DEFAULT_SECTOR_SIZE;
   635    815   }
   636    816   
   637    817   /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
   638    818   */
   639    819   static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
   640    820     multiplexConn *p = (multiplexConn*)pConn;
   641    821     int rc;
................................................................................
   702    882       return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
   703    883     }
   704    884     return SQLITE_OK;
   705    885   }
   706    886   
   707    887   /************************** Public Interfaces *****************************/
   708    888   /*
   709         -** Initialize the multiplex VFS shim.  Use the VFS named zOrigVfsName
   710         -** as the VFS that does the actual work.  Use the default if
   711         -** zOrigVfsName==NULL.  
          889  +** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
          890  +**
          891  +** Use the VFS named zOrigVfsName as the VFS that does the actual work.  
          892  +** Use the default if zOrigVfsName==NULL.  
   712    893   **
   713    894   ** The multiplex VFS shim is named "multiplex".  It will become the default
   714    895   ** VFS if makeDefault is non-zero.
   715    896   **
   716    897   ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
   717    898   ** during start-up.
   718    899   */
................................................................................
   727    908       return SQLITE_NOMEM;
   728    909     }
   729    910     gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname);
   730    911     if( !gMultiplex.zName ){
   731    912       sqlite3_mutex_free(gMultiplex.pMutex);
   732    913       return SQLITE_NOMEM;
   733    914     }
   734         -  gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
   735         -  gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
   736    915     gMultiplex.pGroups = NULL;
   737    916     gMultiplex.isInitialized = 1;
   738    917     gMultiplex.pOrigVfs = pOrigVfs;
   739    918     gMultiplex.sThisVfs = *pOrigVfs;
   740    919     gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
   741         -  gMultiplex.sThisVfs.zName = "multiplex";
          920  +  gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
   742    921     gMultiplex.sThisVfs.xOpen = multiplexOpen;
   743    922     gMultiplex.sThisVfs.xDelete = multiplexDelete;
   744    923     gMultiplex.sThisVfs.xAccess = multiplexAccess;
   745    924     gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
   746    925     gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
   747    926     gMultiplex.sThisVfs.xDlError = multiplexDlError;
   748    927     gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
................................................................................
   769    948     gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
   770    949     gMultiplex.sIoMethodsV2.iVersion = 2;
   771    950     gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
   772    951     gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
   773    952     gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
   774    953     gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
   775    954     sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
          955  +
          956  +  sqlite3_auto_extension((void*)multiplexFuncInit);
          957  +
   776    958     return SQLITE_OK;
   777    959   }
   778    960   
   779    961   /*
   780         -** Shutdown the multiplex system.
          962  +** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
   781    963   **
   782    964   ** All SQLite database connections must be closed before calling this
   783    965   ** routine.
   784    966   **
   785    967   ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
   786    968   ** shutting down in order to free all remaining multiplex groups.
   787    969   */
................................................................................
   792    974     sqlite3_free(gMultiplex.zName);
   793    975     sqlite3_mutex_free(gMultiplex.pMutex);
   794    976     sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
   795    977     memset(&gMultiplex, 0, sizeof(gMultiplex));
   796    978     return SQLITE_OK;
   797    979   }
   798    980   
   799         -/*
   800         -** Adjust chunking params.  VFS should be initialized first.
   801         -** No files should be open.  Re-intializing will reset these
   802         -** to the default.
   803         -*/
   804         -int sqlite3_multiplex_set(
   805         -  int nChunkSize,                 /* Max chunk size */
   806         -  int nMaxChunks                  /* Max number of chunks */
   807         -){
   808         -  if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
   809         -  if( gMultiplex.pGroups ) return SQLITE_MISUSE;
   810         -  if( nChunkSize<32 ) return SQLITE_MISUSE;
   811         -  if( nMaxChunks<1 ) return SQLITE_MISUSE;
   812         -  if( nMaxChunks>99 ) return SQLITE_MISUSE;
   813         -  multiplexEnter();
   814         -  gMultiplex.nChunkSize = nChunkSize;
   815         -  gMultiplex.nMaxChunks = nMaxChunks;
   816         -  multiplexLeave();
   817         -  return SQLITE_OK;
   818         -}
   819         -
   820    981   /***************************** Test Code ***********************************/
   821    982   #ifdef SQLITE_TEST
   822    983   #include <tcl.h>
   823         -
   824    984   extern const char *sqlite3TestErrorName(int);
   825    985   
   826    986   
   827    987   /*
   828    988   ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
   829    989   */
   830    990   static int test_multiplex_initialize(
................................................................................
   876   1036     /* Call sqlite3_multiplex_shutdown() */
   877   1037     rc = sqlite3_multiplex_shutdown();
   878   1038     Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
   879   1039   
   880   1040     return TCL_OK;
   881   1041   }
   882   1042   
   883         -/*
   884         -** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
   885         -*/
   886         -static int test_multiplex_set(
   887         -  void * clientData,
   888         -  Tcl_Interp *interp,
   889         -  int objc,
   890         -  Tcl_Obj *CONST objv[]
   891         -){
   892         -  int nChunkSize;                 /* Max chunk size */
   893         -  int nMaxChunks;                 /* Max number of chunks */
   894         -  int rc;                         /* Value returned by sqlite3_multiplex_set() */
   895         -
   896         -  UNUSED_PARAMETER(clientData);
   897         -
   898         -  /* Process arguments */
   899         -  if( objc!=3 ){
   900         -    Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
   901         -    return TCL_ERROR;
   902         -  }
   903         -  if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
   904         -  if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
   905         -
   906         -  /* Invoke sqlite3_multiplex_set() */
   907         -  rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
   908         -
   909         -  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
   910         -  return TCL_OK;
   911         -}
   912         -
   913   1043   /*
   914   1044   ** tclcmd:  sqlite3_multiplex_dump
   915   1045   */
   916   1046   static int test_multiplex_dump(
   917   1047     void * clientData,
   918   1048     Tcl_Interp *interp,
   919   1049     int objc,
................................................................................
   939   1069             Tcl_NewStringObj(pGroup->zName, -1));
   940   1070       Tcl_ListObjAppendElement(interp, pGroupTerm,
   941   1071             Tcl_NewIntObj(pGroup->nName));
   942   1072       Tcl_ListObjAppendElement(interp, pGroupTerm,
   943   1073             Tcl_NewIntObj(pGroup->flags));
   944   1074   
   945   1075       /* count number of chunks with open handles */
   946         -    for(i=0; i<gMultiplex.nMaxChunks; i++){
         1076  +    for(i=0; i<pGroup->nMaxChunks; i++){
   947   1077         if( pGroup->bOpen[i] ) nChunks++;
   948   1078       }
   949   1079       Tcl_ListObjAppendElement(interp, pGroupTerm,
   950   1080             Tcl_NewIntObj(nChunks));
   951   1081   
   952   1082       Tcl_ListObjAppendElement(interp, pGroupTerm,
   953         -          Tcl_NewIntObj(gMultiplex.nChunkSize));
         1083  +          Tcl_NewIntObj(pGroup->nChunkSize));
   954   1084       Tcl_ListObjAppendElement(interp, pGroupTerm,
   955         -          Tcl_NewIntObj(gMultiplex.nMaxChunks));
         1085  +          Tcl_NewIntObj(pGroup->nMaxChunks));
   956   1086   
   957   1087       Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
   958   1088     }
   959   1089     multiplexLeave();
   960   1090     Tcl_SetObjResult(interp, pResult);
   961   1091     return TCL_OK;
   962   1092   }
         1093  +
         1094  +/*
         1095  +** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
         1096  +*/
         1097  +static int test_multiplex_control(
         1098  +  ClientData cd,
         1099  +  Tcl_Interp *interp,
         1100  +  int objc,
         1101  +  Tcl_Obj *CONST objv[]
         1102  +){
         1103  +  int rc;                         /* Return code from file_control() */
         1104  +  int idx;                        /* Index in aSub[] */
         1105  +  Tcl_CmdInfo cmdInfo;            /* Command info structure for HANDLE */
         1106  +  sqlite3 *db;                    /* Underlying db handle for HANDLE */
         1107  +  int iValue = 0;
         1108  +  void *pArg = 0;
         1109  +
         1110  +  struct SubCommand {
         1111  +    const char *zName;
         1112  +    int op;
         1113  +    int argtype;
         1114  +  } aSub[] = {
         1115  +    { "enable",       MULTIPLEX_CTRL_ENABLE,           1 },
         1116  +    { "chunk_size",   MULTIPLEX_CTRL_SET_CHUNK_SIZE,   1 },
         1117  +    { "max_chunks",   MULTIPLEX_CTRL_SET_MAX_CHUNKS,   1 },
         1118  +    { 0, 0, 0 }
         1119  +  };
         1120  +
         1121  +  if( objc!=5 ){
         1122  +    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
         1123  +    return TCL_ERROR;
         1124  +  }
         1125  +
         1126  +  if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
         1127  +    Tcl_AppendResult(interp, "expected database handle, got \"", 0);
         1128  +    Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
         1129  +    return TCL_ERROR;
         1130  +  }else{
         1131  +    db = *(sqlite3 **)cmdInfo.objClientData;
         1132  +  }
         1133  +
         1134  +  rc = Tcl_GetIndexFromObjStruct(
         1135  +      interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
         1136  +  );
         1137  +  if( rc!=TCL_OK ) return rc;
         1138  +
         1139  +  switch( aSub[idx].argtype ){
         1140  +    case 1:
         1141  +      if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
         1142  +        return TCL_ERROR;
         1143  +      }
         1144  +      pArg = (void *)&iValue;
         1145  +      break;
         1146  +    default:
         1147  +      Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
         1148  +      return TCL_ERROR;
         1149  +  }
         1150  +
         1151  +  rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
         1152  +  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
         1153  +  return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
         1154  +}
   963   1155   
   964   1156   /*
   965   1157   ** This routine registers the custom TCL commands defined in this
   966   1158   ** module.  This should be the only procedure visible from outside
   967   1159   ** of this module.
   968   1160   */
   969   1161   int Sqlitemultiplex_Init(Tcl_Interp *interp){
   970   1162     static struct {
   971   1163        char *zName;
   972   1164        Tcl_ObjCmdProc *xProc;
   973   1165     } aCmd[] = {
   974   1166       { "sqlite3_multiplex_initialize", test_multiplex_initialize },
   975   1167       { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
   976         -    { "sqlite3_multiplex_set", test_multiplex_set },
   977   1168       { "sqlite3_multiplex_dump", test_multiplex_dump },
         1169  +    { "sqlite3_multiplex_control", test_multiplex_control },
   978   1170     };
   979   1171     int i;
   980   1172   
   981   1173     for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
   982   1174       Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
   983   1175     }
   984   1176   
   985   1177     return TCL_OK;
   986   1178   }
   987   1179   #endif

Added src/test_multiplex.h.

            1  +/*
            2  +** 2011 March 18
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +**
           13  +** This file contains a VFS "shim" - a layer that sits in between the
           14  +** pager and the real VFS.
           15  +**
           16  +** This particular shim enforces a multiplex system on DB files.  
           17  +** This shim shards/partitions a single DB file into smaller 
           18  +** "chunks" such that the total DB file size may exceed the maximum
           19  +** file size of the underlying file system.
           20  +**
           21  +*/
           22  +
           23  +#ifndef _TEST_MULTIPLEX_H
           24  +#define _TEST_MULTIPLEX_H
           25  +
           26  +/*
           27  +** CAPI: File-control Operations Supported by Multiplex VFS
           28  +**
           29  +** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle.
           30  +**
           31  +** MULTIPLEX_CTRL_ENABLE:
           32  +**   This file control is used to enable or disable the multiplex
           33  +**   shim.
           34  +**
           35  +** MULTIPLEX_CTRL_SET_CHUNK_SIZE:
           36  +**   This file control is used to set the maximum allowed chunk 
           37  +**   size for a multiplex file set.  The chunk size should be 
           38  +**   a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up
           39  +**   if not.
           40  +**
           41  +** MULTIPLEX_CTRL_SET_MAX_CHUNKS:
           42  +**   This file control is used to set the maximum number of chunks
           43  +**   allowed to be used for a mutliplex file set.
           44  +*/
           45  +#define MULTIPLEX_CTRL_ENABLE          214014
           46  +#define MULTIPLEX_CTRL_SET_CHUNK_SIZE  214015
           47  +#define MULTIPLEX_CTRL_SET_MAX_CHUNKS  214016
           48  +
           49  +/*
           50  +** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
           51  +**
           52  +** Use the VFS named zOrigVfsName as the VFS that does the actual work.  
           53  +** Use the default if zOrigVfsName==NULL.  
           54  +**
           55  +** The multiplex VFS shim is named "multiplex".  It will become the default
           56  +** VFS if makeDefault is non-zero.
           57  +**
           58  +** An auto-extension is registered which will make the function 
           59  +** multiplex_control() available to database connections.  This
           60  +** function gives access to the xFileControl interface of the 
           61  +** multiplex VFS shim.
           62  +**
           63  +** SELECT multiplex_control(<op>,<val>);
           64  +** 
           65  +**   <op>=1 MULTIPLEX_CTRL_ENABLE
           66  +**   <val>=0 disable
           67  +**   <val>=1 enable
           68  +** 
           69  +**   <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE
           70  +**   <val> int, chunk size
           71  +** 
           72  +**   <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS
           73  +**   <val> int, max chunks
           74  +**
           75  +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
           76  +** during start-up.
           77  +*/
           78  +extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault);
           79  +
           80  +/*
           81  +** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
           82  +**
           83  +** All SQLite database connections must be closed before calling this
           84  +** routine.
           85  +**
           86  +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
           87  +** shutting down in order to free all remaining multiplex groups.
           88  +*/
           89  +extern int sqlite3_multiplex_shutdown(void);
           90  +
           91  +#endif

Changes to test/multiplex.test.

    10     10   #***********************************************************************
    11     11   #
    12     12   
    13     13   set testdir [file dirname $argv0]
    14     14   source $testdir/tester.tcl
    15     15   source $testdir/malloc_common.tcl
    16     16   
    17         -set g_chunk_size 2147483648
           17  +set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
    18     18   set g_max_chunks 32
    19     19   
    20     20   # This handles appending the chunk number
    21     21   # to the end of the filename.  if 
    22     22   # SQLITE_MULTIPLEX_EXT_OVWR is defined, then
    23     23   # it overwrites the last 2 bytes of the 
    24     24   # file name with the chunk number.
................................................................................
    28     28     ifcapable {multiplex_ext_overwrite} {
    29     29       set name [string range $name 0 [expr [string length $name]-2-1]]
    30     30     }
    31     31     return $name$num
    32     32   }
    33     33   
    34     34   # This saves off the parameters and calls the 
    35         -# underlying sqlite3_multiplex_set() API.
    36         -proc multiplex_set {chunk_size max_chunks} {
           35  +# underlying sqlite3_multiplex_control() API.
           36  +proc multiplex_set {db name chunk_size max_chunks} {
    37     37     global g_chunk_size
    38     38     global g_max_chunks
    39         -  set g_chunk_size $chunk_size
           39  +  set g_chunk_size [ expr (($chunk_size+($::SQLITE_MAX_PAGE_SIZE-1)) & ~($::SQLITE_MAX_PAGE_SIZE-1)) ]
    40     40     set g_max_chunks $max_chunks
    41         -  sqlite3_multiplex_set $chunk_size $max_chunks
           41  +  set rc [catch {sqlite3_multiplex_control $db $name chunk_size $chunk_size} msg]
           42  +  if { $rc==0 } { 
           43  +    set rc [catch {sqlite3_multiplex_control $db $name max_chunks $max_chunks} msg]
           44  +  }
           45  +  list $msg
    42     46   }
    43     47   
    44     48   # This attempts to delete the base file and 
    45     49   # and files with the chunk extension.
    46     50   proc multiplex_delete {name} {
    47     51     global g_max_chunks
    48     52     for {set i 0} {$i<$g_max_chunks} {incr i} {
................................................................................
    66     70   do_test multiplex-1.4 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
    67     71   
    68     72   do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 }        {SQLITE_OK}
    69     73   do_test multiplex-1.6 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
    70     74   do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
    71     75   do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
    72     76   
    73         -do_test multiplex-1.9  { sqlite3_multiplex_initialize "" 1 }       {SQLITE_OK}
    74         -do_test multiplex-1.10.1 { multiplex_set 32768 16 }                {SQLITE_OK}
    75         -do_test multiplex-1.10.2 { multiplex_set 32768 -1 }                {SQLITE_MISUSE}
    76         -do_test multiplex-1.10.3 { multiplex_set -1 16 }                   {SQLITE_MISUSE}
    77         -do_test multiplex-1.10.4 { multiplex_set 31 16 }                   {SQLITE_MISUSE}
    78         -do_test multiplex-1.10.5 { multiplex_set 32768 100 }               {SQLITE_MISUSE}
    79         -do_test multiplex-1.11 { sqlite3_multiplex_shutdown }              {SQLITE_OK}
    80     77   
           78  +do_test multiplex-1.9.1  { sqlite3_multiplex_initialize "" 1 }     {SQLITE_OK}
           79  +do_test multiplex-1.9.2  { sqlite3 db test.db }                    {}
           80  +do_test multiplex-1.9.3  { multiplex_set db main 32768 16 }        {SQLITE_OK}
           81  +do_test multiplex-1.9.4  { multiplex_set db main 32768 -1 }        {SQLITE_MISUSE}
           82  +do_test multiplex-1.9.5  { multiplex_set db main -1 16 }           {SQLITE_MISUSE}
           83  +do_test multiplex-1.9.6  { multiplex_set db main 31 16 }           {SQLITE_OK}
           84  +do_test multiplex-1.9.7  { multiplex_set db main 32768 100 }       {SQLITE_MISUSE}
           85  +do_test multiplex-1.9.8  { multiplex_set db main 1073741824 1 }    {SQLITE_OK}
           86  +do_test multiplex-1.9.9  { db close }                              {}
           87  +do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown }            {SQLITE_OK}
           88  +
           89  +do_test multiplex-1.10.1  { sqlite3_multiplex_initialize "" 1 }                                  {SQLITE_OK}
           90  +do_test multiplex-1.10.2  { sqlite3 db test.db }                                                 {}
           91  +do_test multiplex-1.10.3  { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 }      {0}
           92  +do_test multiplex-1.10.4  { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 }         {1}
           93  +do_test multiplex-1.10.5  { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 }         {1}
           94  +do_test multiplex-1.10.6  { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 }         {0}
           95  +do_test multiplex-1.10.7  { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 }        {1}
           96  +do_test multiplex-1.10.8  { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0}
           97  +do_test multiplex-1.10.9  { db close }                                                           {}
           98  +do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown }                                         {SQLITE_OK}
           99  +
          100  +do_test multiplex-1.11.1  { sqlite3_multiplex_initialize "" 1 }               {SQLITE_OK}
          101  +do_test multiplex-1.11.2  { sqlite3 db test.db }                              {}
          102  +do_test multiplex-1.11.3  { sqlite3_multiplex_control db main enable 0  }     {SQLITE_OK}
          103  +do_test multiplex-1.11.4  { sqlite3_multiplex_control db main enable 1  }     {SQLITE_OK}
          104  +do_test multiplex-1.11.5  { sqlite3_multiplex_control db main enable -1 }     {SQLITE_OK}
          105  +do_test multiplex-1.11.6  { db close }                                        {}
          106  +do_test multiplex-1.11.7  { sqlite3_multiplex_shutdown }                      {SQLITE_OK}
          107  +
          108  +do_test multiplex-1.12.1  { sqlite3_multiplex_initialize "" 1 }                           {SQLITE_OK}
          109  +do_test multiplex-1.12.2  { sqlite3 db test.db }                                          {}
          110  +do_test multiplex-1.12.3  { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 }   {0}
          111  +do_test multiplex-1.12.4  { lindex [ catchsql { SELECT multiplex_control(1, 1); } ] 0 }   {0}
          112  +do_test multiplex-1.12.5  { lindex [ catchsql { SELECT multiplex_control(1, -1); } ] 0 }  {0}
          113  +do_test multiplex-1.12.6  { db close }                                                    {}
          114  +do_test multiplex-1.12.7  { sqlite3_multiplex_shutdown }                                  {SQLITE_OK}
          115  +
          116  +do_test multiplex-1.13.1  { sqlite3_multiplex_initialize "" 1 }                           {SQLITE_OK}
          117  +do_test multiplex-1.13.2  { sqlite3 db test.db }                                          {}
          118  +do_test multiplex-1.13.3  { lindex [ catchsql { SELECT multiplex_control(-1, 0); } ] 0 }  {1}
          119  +do_test multiplex-1.13.4  { lindex [ catchsql { SELECT multiplex_control(4, 1); } ] 0 }   {1}
          120  +do_test multiplex-1.13.6  { db close }                                                    {}
          121  +do_test multiplex-1.13.7  { sqlite3_multiplex_shutdown }                                  {SQLITE_OK}
    81    122   
    82    123   #-------------------------------------------------------------------------
    83    124   # Some simple warm-body tests with a single database file in rollback 
    84    125   # mode:
    85    126   #
    86    127   #   multiplex-2.1.*: Test simple writing to a multiplex file.
    87    128   #
................................................................................
    94    135   #                afterwards. Then close the database and successfully shut
    95    136   #                down the multiplex system.
    96    137   #
    97    138   #   multiplex-2.5.*: More reading/writing.
    98    139   #
    99    140   #   multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
   100    141   #                well as varying journal mode.
          142  +#
          143  +#   multiplex-2.7.*: Disable/enable tests.
          144  +#
   101    145   
   102    146   sqlite3_multiplex_initialize "" 1
   103         -multiplex_set 32768 16
          147  +multiplex_set db main 32768 16
   104    148   
   105    149   do_test multiplex-2.1.2 {
   106    150     sqlite3 db test.db
   107    151     execsql {
   108    152       PRAGMA page_size=1024;
   109    153       PRAGMA auto_vacuum=OFF;
   110    154       PRAGMA journal_mode=DELETE;
................................................................................
   125    169   } {}
   126    170   do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144}
   127    171   
   128    172   do_test multiplex-2.3.1 {
   129    173     sqlite3 db2 test2.db
   130    174     db2 close
   131    175   } {}
          176  +
   132    177   
   133    178   do_test multiplex-2.4.1 {
   134    179     sqlite3_multiplex_shutdown
   135    180   } {SQLITE_MISUSE}
   136    181   do_test multiplex-2.4.2 {
   137    182     execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
   138    183   } {}
................................................................................
   142    187     sqlite3_multiplex_shutdown
   143    188   } {SQLITE_OK}
   144    189   
   145    190   
   146    191   do_test multiplex-2.5.1 {
   147    192     multiplex_delete test.db
   148    193     sqlite3_multiplex_initialize "" 1
   149         -  multiplex_set 4096 16
          194  +  sqlite3 db test.db
          195  +  multiplex_set db main 4096 16
   150    196   } {SQLITE_OK}
   151    197   
   152    198   do_test multiplex-2.5.2 {
   153         -  sqlite3 db test.db
   154    199     execsql {
   155    200       PRAGMA page_size = 1024;
   156    201       PRAGMA journal_mode = delete;
   157    202       PRAGMA auto_vacuum = off;
   158    203       CREATE TABLE t1(a PRIMARY KEY, b);
   159    204     }
   160    205   } {delete}
................................................................................
   161    206   
   162    207   do_test multiplex-2.5.3 { 
   163    208     execsql { 
   164    209       INSERT INTO t1 VALUES(1, 'one');
   165    210       INSERT INTO t1 VALUES(2, randomblob(4000));
   166    211       INSERT INTO t1 VALUES(3, 'three');
   167    212       INSERT INTO t1 VALUES(4, randomblob(4000));
   168         -    INSERT INTO t1 VALUES(5, 'five') 
          213  +    INSERT INTO t1 VALUES(5, 'five');
          214  +    INSERT INTO t1 VALUES(6, randomblob($g_chunk_size));
          215  +    INSERT INTO t1 VALUES(7, randomblob($g_chunk_size));
   169    216     }
   170    217   } {}
   171    218   
   172    219   do_test multiplex-2.5.4 {
   173    220     db eval {SELECT * FROM t1 WHERE a=1}
   174    221   } {1 one}
   175    222   
................................................................................
   201    248   set all_journal_modes {delete persist truncate memory off}
   202    249   foreach jmode $all_journal_modes {
   203    250     for {set sz 151} {$sz<8000} {set sz [expr $sz+419]} {
   204    251   
   205    252       do_test multiplex-2.6.1.$sz.$jmode {
   206    253         multiplex_delete test.db
   207    254         sqlite3_multiplex_initialize "" 1
   208         -      multiplex_set $sz 32
          255  +      sqlite3 db test.db
          256  +      multiplex_set db main $sz 32
   209    257       } {SQLITE_OK}
   210    258   
   211    259       do_test multiplex-2.6.2.$sz.$jmode {
   212         -      sqlite3 db test.db
   213    260         db eval {
   214    261           PRAGMA page_size = 1024;
   215    262           PRAGMA auto_vacuum = off;
   216    263         }
   217    264         db eval "PRAGMA journal_mode = $jmode;"
   218    265       } $jmode
   219    266   
................................................................................
   239    286         db close
   240    287         sqlite3_multiplex_shutdown
   241    288       } {SQLITE_OK}
   242    289   
   243    290     }
   244    291   }
   245    292   
          293  +do_test multiplex-2.7.1  { multiplex_delete test.db }                                       {}
          294  +do_test multiplex-2.7.2  { sqlite3_multiplex_initialize "" 1 }                              {SQLITE_OK}
          295  +do_test multiplex-2.7.3  { sqlite3 db test.db }                                             {}
          296  +do_test multiplex-2.7.4  { lindex [ catchsql { SELECT multiplex_control(2, 65536); } ] 0 }  {0}
          297  +do_test multiplex-2.7.5  { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 }      {0}
          298  +do_test multiplex-2.7.6 { 
          299  +  execsql { 
          300  +    CREATE TABLE t1(a PRIMARY KEY, b);
          301  +    INSERT INTO t1 VALUES(1, randomblob(1000));
          302  +  }
          303  +} {}
          304  +# verify only one file, and file size is less than chunks size
          305  +do_test multiplex-2.7.7  { expr ([file size [multiplex_name test.db 0]] < 65536) } {1}
          306  +do_test multiplex-2.7.8  { file exists [multiplex_name test.db 1] }                {0}
          307  +do_test multiplex-2.7.9 { 
          308  +  execsql { 
          309  +    INSERT INTO t1 VALUES(2, randomblob(65536));
          310  +  }
          311  +} {}
          312  +# verify only one file, and file size exceeds chunks size
          313  +do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1}
          314  +do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] }                {0}
          315  +do_test multiplex-2.7.12 { db close }                                              {}
          316  +do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown }                            {SQLITE_OK}
          317  +
   246    318   #-------------------------------------------------------------------------
   247    319   # Try some tests with more than one connection to a database file. Still
   248    320   # in rollback mode.
   249    321   #
   250    322   #   multiplex-3.1.*: Two connections to a single database file.
   251    323   #
   252    324   #   multiplex-3.2.*: Two connections to each of several database files (that
   253    325   #                are in the same multiplex group).
   254    326   #
   255    327   do_test multiplex-3.1.1 {
   256    328     multiplex_delete test.db
   257    329     sqlite3_multiplex_initialize "" 1
   258         -  multiplex_set 32768 16
          330  +  sqlite3 db test.db
          331  +  multiplex_set db main 32768 16
   259    332   } {SQLITE_OK}
   260    333   do_test multiplex-3.1.2 {
   261         -  sqlite3 db test.db
   262    334     execsql {
   263    335       PRAGMA page_size = 1024;
   264    336       PRAGMA journal_mode = delete;
   265    337       PRAGMA auto_vacuum = off;
   266    338       CREATE TABLE t1(a PRIMARY KEY, b);
   267    339       INSERT INTO t1 VALUES(1, 'one');
   268    340     }
................................................................................
   337    409     foreach db {db1a db2a db2b db1b} { catch { $db close } }
   338    410   } {}
   339    411   
   340    412   #-------------------------------------------------------------------------
   341    413   #
   342    414   
   343    415   sqlite3_multiplex_initialize "" 1
   344         -multiplex_set 32768 16
          416  +multiplex_set db main 32768 16
   345    417   
   346    418   # Return a list of all currently defined multiplexs.
   347    419   proc multiplex_list {} {
   348    420     set allq {}
   349    421     foreach q [sqlite3_multiplex_dump] {
   350    422       lappend allq [lindex $q 0]
   351    423     }
................................................................................
   399    471   
   400    472   #-------------------------------------------------------------------------
   401    473   # The following tests test that the multiplex VFS handles malloc and IO 
   402    474   # errors.
   403    475   #
   404    476   
   405    477   sqlite3_multiplex_initialize "" 1
   406         -multiplex_set 32768 16
          478  +multiplex_set db main 32768 16
   407    479   
   408    480   do_faultsim_test multiplex-5.1 -prep {
   409    481     catch {db close}
   410    482   } -body {
   411    483     sqlite3 db test2.db
   412    484   }
   413    485   do_faultsim_test multiplex-5.2 -prep {
................................................................................
   444    516   } {1 {unable to open database file}}
   445    517   catch { file delete test.db }
   446    518   
   447    519   do_faultsim_test multiplex-5.5 -prep {
   448    520     catch { sqlite3_multiplex_shutdown }
   449    521   } -body {
   450    522     sqlite3_multiplex_initialize "" 1
   451         -  multiplex_set 32768 16
          523  +  multiplex_set db main 32768 16
   452    524   }
   453    525   
   454    526   # test that mismatch filesize is detected
   455    527   #
   456    528   # Do not run this test if $::G(perm:presql) is set. If it is set, then the
   457    529   # expected IO error will occur within the Tcl [sqlite3] wrapper, not within
   458    530   # the first SQL statement executed below. This breaks the test case.
................................................................................
   469    541           PRAGMA auto_vacuum = off;
   470    542         }
   471    543         db eval "PRAGMA journal_mode = $jmode;"
   472    544       } $jmode
   473    545       do_test multiplex-5.6.2.$jmode {
   474    546         execsql {
   475    547           CREATE TABLE t1(a, b);
   476         -        INSERT INTO t1 VALUES(1, randomblob(1100));
   477         -        INSERT INTO t1 VALUES(2, randomblob(1100));
   478         -        INSERT INTO t1 VALUES(3, randomblob(1100));
   479         -        INSERT INTO t1 VALUES(4, randomblob(1100));
   480         -        INSERT INTO t1 VALUES(5, randomblob(1100));
          548  +        INSERT INTO t1 VALUES(1, randomblob(15000));
          549  +        INSERT INTO t1 VALUES(2, randomblob(15000));
          550  +        INSERT INTO t1 VALUES(3, randomblob(15000));
          551  +        INSERT INTO t1 VALUES(4, randomblob(15000));
          552  +        INSERT INTO t1 VALUES(5, randomblob(15000));
   481    553         }
   482    554         db close
   483    555         sqlite3_multiplex_initialize "" 1
   484         -      multiplex_set 4096 16
   485    556         sqlite3 db test.db
   486         -    } {}
          557  +      multiplex_set db main 4096 16
          558  +    } {SQLITE_OK}
   487    559       do_test multiplex-5.6.3.$jmode {
   488    560         catchsql {
   489         -        INSERT INTO t1 VALUES(6, randomblob(1100));
          561  +        INSERT INTO t1 VALUES(6, randomblob(15000));
   490    562         }
   491    563       } {1 {disk I/O error}}
   492    564       do_test multiplex-5.6.4.$jmode {
   493    565         db close
   494    566       } {}
   495    567     }
   496    568   }
   497    569   
   498    570   catch { sqlite3_multiplex_shutdown }
   499    571   finish_test