/ Artifact Content
Login

Artifact 3fc368022c46fe44ec22c5e1ed727223a54a6a1d:


     1  /*
     2  ** 2010 October 28
     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 - that breaks up a very large database file
    15  ** into two or more smaller files on disk.  This is useful, for example,
    16  ** in order to support large, multi-gigabyte databases on older filesystems
    17  ** that limit the maximum file size to 2 GiB.
    18  **
    19  ** USAGE:
    20  **
    21  ** Compile this source file and link it with your application.  Then
    22  ** at start-time, invoke the following procedure:
    23  **
    24  **   int sqlite3_multiplex_initialize(
    25  **      const char *zOrigVfsName,    // The underlying real VFS
    26  **      int makeDefault              // True to make multiplex the default VFS
    27  **   );
    28  **
    29  ** The procedure call above will create and register a new VFS shim named
    30  ** "multiplex".  The multiplex VFS will use the VFS named by zOrigVfsName to
    31  ** do the actual disk I/O.  (The zOrigVfsName parameter may be NULL, in 
    32  ** which case the default VFS at the moment sqlite3_multiplex_initialize()
    33  ** is called will be used as the underlying real VFS.)  
    34  **
    35  ** If the makeDefault parameter is TRUE then multiplex becomes the new
    36  ** default VFS.  Otherwise, you can use the multiplex VFS by specifying
    37  ** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing
    38  ** URI filenames and adding "vfs=multiplex" as a parameter to the filename
    39  ** URI.
    40  **
    41  ** The multiplex VFS allows databases up to 32 GiB in size.  But it splits
    42  ** the files up into smaller pieces, so that they will work even on 
    43  ** filesystems that do not support large files.  The default chunk size
    44  ** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be
    45  ** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE
    46  ** macro.  Use the "chunksize=NNNN" query parameter with a URI filename
    47  ** in order to select an alternative chunk size for individual connections
    48  ** at run-time.
    49  */
    50  #include "sqlite3.h"
    51  #include <string.h>
    52  #include <assert.h>
    53  #include <stdlib.h>
    54  #include "test_multiplex.h"
    55  
    56  #ifndef SQLITE_CORE
    57    #define SQLITE_CORE 1  /* Disable the API redefinition in sqlite3ext.h */
    58  #endif
    59  #include "sqlite3ext.h"
    60  
    61  /* 
    62  ** These should be defined to be the same as the values in 
    63  ** sqliteInt.h.  They are defined seperately here so that
    64  ** the multiplex VFS shim can be built as a loadable 
    65  ** module.
    66  */
    67  #define UNUSED_PARAMETER(x) (void)(x)
    68  #define MAX_PAGE_SIZE       0x10000
    69  #define DEFAULT_SECTOR_SIZE 0x1000
    70  
    71  /*
    72  ** For a build without mutexes, no-op the mutex calls.
    73  */
    74  #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
    75  #define sqlite3_mutex_alloc(X)    ((sqlite3_mutex*)8)
    76  #define sqlite3_mutex_free(X)
    77  #define sqlite3_mutex_enter(X)
    78  #define sqlite3_mutex_try(X)      SQLITE_OK
    79  #define sqlite3_mutex_leave(X)
    80  #define sqlite3_mutex_held(X)     ((void)(X),1)
    81  #define sqlite3_mutex_notheld(X)  ((void)(X),1)
    82  #endif /* SQLITE_THREADSAFE==0 */
    83  
    84  
    85  /************************ Shim Definitions ******************************/
    86  
    87  #ifndef SQLITE_MULTIPLEX_VFS_NAME
    88  # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
    89  #endif
    90  
    91  /* This is the limit on the chunk size.  It may be changed by calling
    92  ** the xFileControl() interface.  It will be rounded up to a 
    93  ** multiple of MAX_PAGE_SIZE.  We default it here to 2GiB less 64KiB.
    94  */
    95  #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
    96  # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
    97  #endif
    98  
    99  /* Default limit on number of chunks.  Care should be taken
   100  ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
   101  ** format specifier. It may be changed by calling
   102  ** the xFileControl() interface.
   103  */
   104  #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
   105  # define SQLITE_MULTIPLEX_MAX_CHUNKS 32
   106  #endif
   107  
   108  /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the 
   109  ** last SQLITE_MULTIPLEX_EXT_SZ characters of the 
   110  ** filename will be overwritten, otherwise, the 
   111  ** multiplex extension is simply appended to the filename.
   112  ** Ex.  (undefined) test.db -> test.db01
   113  **      (defined)   test.db -> test.01
   114  ** Chunk 0 does not have a modified extension.
   115  */
   116  #define SQLITE_MULTIPLEX_EXT_FMT    "%02d"
   117  #define SQLITE_MULTIPLEX_EXT_SZ     2
   118  
   119  /************************ Object Definitions ******************************/
   120  
   121  /* Forward declaration of all object types */
   122  typedef struct multiplexGroup multiplexGroup;
   123  typedef struct multiplexConn multiplexConn;
   124  
   125  /*
   126  ** A "multiplex group" is a collection of files that collectively
   127  ** makeup a single SQLite DB file.  This allows the size of the DB
   128  ** to exceed the limits imposed by the file system.
   129  **
   130  ** There is an instance of the following object for each defined multiplex
   131  ** group.
   132  */
   133  struct multiplexGroup {
   134    struct multiplexReal {           /* For each chunk */
   135      sqlite3_file *p;                  /* Handle for the chunk */
   136      char *z;                          /* Name of this chunk */
   137    } *aReal;                        /* list of all chunks */
   138    int nReal;                       /* Number of chunks */
   139    char *zName;                     /* Base filename of this group */
   140    int nName;                       /* Length of base filename */
   141    int flags;                       /* Flags used for original opening */
   142    unsigned int szChunk;            /* Chunk size used for this group */
   143    int bEnabled;                    /* TRUE to use Multiplex VFS for this file */
   144    multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
   145  };
   146  
   147  /*
   148  ** An instance of the following object represents each open connection
   149  ** to a file that is multiplex'ed.  This object is a 
   150  ** subclass of sqlite3_file.  The sqlite3_file object for the underlying
   151  ** VFS is appended to this structure.
   152  */
   153  struct multiplexConn {
   154    sqlite3_file base;              /* Base class - must be first */
   155    multiplexGroup *pGroup;         /* The underlying group of files */
   156  };
   157  
   158  /************************* Global Variables **********************************/
   159  /*
   160  ** All global variables used by this file are containing within the following
   161  ** gMultiplex structure.
   162  */
   163  static struct {
   164    /* The pOrigVfs is the real, original underlying VFS implementation.
   165    ** Most operations pass-through to the real VFS.  This value is read-only
   166    ** during operation.  It is only modified at start-time and thus does not
   167    ** require a mutex.
   168    */
   169    sqlite3_vfs *pOrigVfs;
   170  
   171    /* The sThisVfs is the VFS structure used by this shim.  It is initialized
   172    ** at start-time and thus does not require a mutex
   173    */
   174    sqlite3_vfs sThisVfs;
   175  
   176    /* The sIoMethods defines the methods used by sqlite3_file objects 
   177    ** associated with this shim.  It is initialized at start-time and does
   178    ** not require a mutex.
   179    **
   180    ** When the underlying VFS is called to open a file, it might return 
   181    ** either a version 1 or a version 2 sqlite3_file object.  This shim
   182    ** has to create a wrapper sqlite3_file of the same version.  Hence
   183    ** there are two I/O method structures, one for version 1 and the other
   184    ** for version 2.
   185    */
   186    sqlite3_io_methods sIoMethodsV1;
   187    sqlite3_io_methods sIoMethodsV2;
   188  
   189    /* True when this shim has been initialized.
   190    */
   191    int isInitialized;
   192  
   193    /* For run-time access any of the other global data structures in this
   194    ** shim, the following mutex must be held.
   195    */
   196    sqlite3_mutex *pMutex;
   197  
   198    /* List of multiplexGroup objects.
   199    */
   200    multiplexGroup *pGroups;
   201  } gMultiplex;
   202  
   203  /************************* Utility Routines *********************************/
   204  /*
   205  ** Acquire and release the mutex used to serialize access to the
   206  ** list of multiplexGroups.
   207  */
   208  static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
   209  static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
   210  
   211  /*
   212  ** Compute a string length that is limited to what can be stored in
   213  ** lower 30 bits of a 32-bit signed integer.
   214  **
   215  ** The value returned will never be negative.  Nor will it ever be greater
   216  ** than the actual length of the string.  For very long strings (greater
   217  ** than 1GiB) the value returned might be less than the true string length.
   218  */
   219  static int multiplexStrlen30(const char *z){
   220    const char *z2 = z;
   221    if( z==0 ) return 0;
   222    while( *z2 ){ z2++; }
   223    return 0x3fffffff & (int)(z2 - z);
   224  }
   225  
   226  /*
   227  ** Create a temporary file name in zBuf.  zBuf must be big enough to
   228  ** hold at pOrigVfs->mxPathname characters.  This function departs
   229  ** from the traditional temporary name generation in the os_win
   230  ** and os_unix VFS in several ways, but is necessary so that 
   231  ** the file name is known for temporary files (like those used 
   232  ** during vacuum.)
   233  **
   234  ** N.B. This routine assumes your underlying VFS is ok with using
   235  ** "/" as a directory seperator.  This is the default for UNIXs
   236  ** and is allowed (even mixed) for most versions of Windows.
   237  */
   238  static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
   239    static char zChars[] =
   240      "abcdefghijklmnopqrstuvwxyz"
   241      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
   242      "0123456789";
   243    int i,j;
   244    int attempts = 0;
   245    int exists = 0;
   246    int rc = SQLITE_ERROR;
   247  
   248    /* Check that the output buffer is large enough for 
   249    ** pVfs->mxPathname characters.
   250    */
   251    if( pOrigVfs->mxPathname <= nBuf ){
   252      char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
   253      if( zTmp==0 ) return SQLITE_NOMEM;
   254  
   255      /* sqlite3_temp_directory should always be less than
   256      ** pVfs->mxPathname characters.
   257      */
   258      sqlite3_snprintf(pOrigVfs->mxPathname,
   259                       zTmp,
   260                       "%s/",
   261                       sqlite3_temp_directory ? sqlite3_temp_directory : ".");
   262      rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
   263      sqlite3_free(zTmp);
   264      if( rc ) return rc;
   265  
   266      /* Check that the output buffer is large enough for the temporary file 
   267      ** name.
   268      */
   269      j = multiplexStrlen30(zBuf);
   270      if( (j + 8 + 1 + 3 + 1) <= nBuf ){
   271        /* Make 3 attempts to generate a unique name. */
   272        do {
   273          attempts++;
   274          sqlite3_randomness(8, &zBuf[j]);
   275          for(i=0; i<8; i++){
   276            unsigned char uc = (unsigned char)zBuf[j+i];
   277            zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
   278          }
   279          memcpy(&zBuf[j+i], ".tmp", 5);
   280          rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
   281        } while ( (rc==SQLITE_OK) && exists && (attempts<3) );
   282        if( rc==SQLITE_OK && exists ){
   283          rc = SQLITE_ERROR;
   284        }
   285      }
   286    }
   287  
   288    return rc;
   289  }
   290  
   291  /* Compute the filename for the iChunk-th chunk
   292  */
   293  static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
   294    if( iChunk>=pGroup->nReal ){
   295      struct multiplexReal *p;
   296      p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
   297      if( p==0 ){
   298        return SQLITE_NOMEM;
   299      }
   300      memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
   301      pGroup->aReal = p;
   302      pGroup->nReal = iChunk+1;
   303    }
   304    if( pGroup->aReal[iChunk].z==0 ){
   305      char *z;
   306      int n = pGroup->nName;
   307      pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+3 );
   308      if( z==0 ){
   309        return SQLITE_NOMEM;
   310      }
   311      memcpy(z, pGroup->zName, n+1);
   312      if( iChunk>0 ){
   313  #ifdef SQLITE_ENABLE_8_3_NAMES
   314        if( n>3 && z[n-3]=='.' ){
   315          n--;
   316        }else if( n>4 && z[n-4]=='.' ){
   317          n -= 2;
   318        }
   319  #endif
   320        sqlite3_snprintf(3,&z[n],"%02d",iChunk);
   321      }
   322    }
   323    return SQLITE_OK;
   324  }
   325  
   326  /* Translate an sqlite3_file* that is really a multiplexGroup* into
   327  ** the sqlite3_file* for the underlying original VFS.
   328  */
   329  static sqlite3_file *multiplexSubOpen(
   330    multiplexGroup *pGroup,
   331    int iChunk,
   332    int *rc,
   333    int *pOutFlags
   334  ){
   335    sqlite3_file *pSubOpen = 0;
   336    sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
   337    *rc = multiplexSubFilename(pGroup, iChunk);
   338    if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
   339      pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
   340      if( pSubOpen==0 ){
   341        *rc = SQLITE_NOMEM;
   342        return 0;
   343      }
   344      pGroup->aReal[iChunk].p = pSubOpen;
   345      *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
   346                            pGroup->flags, pOutFlags);
   347      if( *rc!=SQLITE_OK ){
   348        sqlite3_free(pSubOpen);
   349        pGroup->aReal[iChunk].p = 0;
   350        return 0;
   351      }
   352    }
   353    return pSubOpen;
   354  }
   355  
   356  /*
   357  ** This is the implementation of the multiplex_control() SQL function.
   358  */
   359  static void multiplexControlFunc(
   360    sqlite3_context *context,
   361    int argc,
   362    sqlite3_value **argv
   363  ){
   364    int rc = SQLITE_OK;
   365    sqlite3 *db = sqlite3_context_db_handle(context);
   366    int op;
   367    int iVal;
   368  
   369    if( !db || argc!=2 ){ 
   370      rc = SQLITE_ERROR; 
   371    }else{
   372      /* extract params */
   373      op = sqlite3_value_int(argv[0]);
   374      iVal = sqlite3_value_int(argv[1]);
   375      /* map function op to file_control op */
   376      switch( op ){
   377        case 1: 
   378          op = MULTIPLEX_CTRL_ENABLE; 
   379          break;
   380        case 2: 
   381          op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; 
   382          break;
   383        case 3: 
   384          op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; 
   385          break;
   386        default:
   387          rc = SQLITE_NOTFOUND;
   388          break;
   389      }
   390    }
   391    if( rc==SQLITE_OK ){
   392      rc = sqlite3_file_control(db, 0, op, &iVal);
   393    }
   394    sqlite3_result_error_code(context, rc);
   395  }
   396  
   397  /*
   398  ** This is the entry point to register the auto-extension for the 
   399  ** multiplex_control() function.
   400  */
   401  static int multiplexFuncInit(
   402    sqlite3 *db, 
   403    char **pzErrMsg, 
   404    const sqlite3_api_routines *pApi
   405  ){
   406    int rc;
   407    rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 
   408        0, multiplexControlFunc, 0, 0);
   409    return rc;
   410  }
   411  
   412  /*
   413  ** Close a single sub-file in the connection group.
   414  */
   415  static void multiplexSubClose(
   416    multiplexGroup *pGroup,
   417    int iChunk,
   418    sqlite3_vfs *pOrigVfs
   419  ){
   420    sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
   421    if( pSubOpen ){
   422      pSubOpen->pMethods->xClose(pSubOpen);
   423      if( pOrigVfs ) pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
   424      sqlite3_free(pGroup->aReal[iChunk].p);
   425    }
   426    sqlite3_free(pGroup->aReal[iChunk].z);
   427    memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));
   428  }
   429  
   430  /*
   431  ** Deallocate memory held by a multiplexGroup
   432  */
   433  static void multiplexFreeComponents(multiplexGroup *pGroup){
   434    int i;
   435    for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
   436    sqlite3_free(pGroup->aReal);
   437    pGroup->aReal = 0;
   438    pGroup->nReal = 0;
   439  }
   440  
   441  
   442  /************************* VFS Method Wrappers *****************************/
   443  
   444  /*
   445  ** This is the xOpen method used for the "multiplex" VFS.
   446  **
   447  ** Most of the work is done by the underlying original VFS.  This method
   448  ** simply links the new file into the appropriate multiplex group if it is a
   449  ** file that needs to be tracked.
   450  */
   451  static int multiplexOpen(
   452    sqlite3_vfs *pVfs,         /* The multiplex VFS */
   453    const char *zName,         /* Name of file to be opened */
   454    sqlite3_file *pConn,       /* Fill in this file descriptor */
   455    int flags,                 /* Flags to control the opening */
   456    int *pOutFlags             /* Flags showing results of opening */
   457  ){
   458    int rc = SQLITE_OK;                  /* Result code */
   459    multiplexConn *pMultiplexOpen;       /* The new multiplex file descriptor */
   460    multiplexGroup *pGroup;              /* Corresponding multiplexGroup object */
   461    sqlite3_file *pSubOpen = 0;                    /* Real file descriptor */
   462    sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   463    int nName;
   464    int sz;
   465    char *zToFree = 0;
   466  
   467    UNUSED_PARAMETER(pVfs);
   468    memset(pConn, 0, pVfs->szOsFile);
   469  
   470    /* We need to create a group structure and manage
   471    ** access to this group of files.
   472    */
   473    multiplexEnter();
   474    pMultiplexOpen = (multiplexConn*)pConn;
   475  
   476    /* If the second argument to this function is NULL, generate a 
   477    ** temporary file name to use.  This will be handled by the
   478    ** original xOpen method.  We just need to allocate space for
   479    ** it.
   480    */
   481    if( !zName ){
   482      zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
   483      if( zName==0 ){
   484        rc = SQLITE_NOMEM;
   485      }else{
   486        rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
   487      }
   488    }
   489  
   490    if( rc==SQLITE_OK ){
   491      /* allocate space for group */
   492      nName = multiplexStrlen30(zName);
   493      sz = sizeof(multiplexGroup)                             /* multiplexGroup */
   494         + nName + 1;                                         /* zName */
   495      pGroup = sqlite3_malloc( sz );
   496      if( pGroup==0 ){
   497        rc = SQLITE_NOMEM;
   498      }
   499    }
   500  
   501    if( rc==SQLITE_OK ){
   502      /* assign pointers to extra space allocated */
   503      char *p = (char *)&pGroup[1];
   504      pMultiplexOpen->pGroup = pGroup;
   505      memset(pGroup, 0, sz);
   506      pGroup->bEnabled = -1;
   507      pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
   508      if( flags & SQLITE_OPEN_URI ){
   509        const char *zChunkSize;
   510        zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
   511        if( zChunkSize ){
   512          unsigned int n = 0;
   513          int i;
   514          for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
   515            n = n*10 + zChunkSize[i] - '0';
   516          }
   517          if( n>0 ){
   518            pGroup->szChunk = (n+0xffff)&~0xffff;
   519          }else{
   520            /* A zero or negative chunksize disabled the multiplexor */
   521            pGroup->bEnabled = 0;
   522          }
   523        }
   524      }
   525      pGroup->zName = p;
   526      /* save off base filename, name length, and original open flags  */
   527      memcpy(pGroup->zName, zName, nName+1);
   528      pGroup->nName = nName;
   529      pGroup->flags = flags;
   530      rc = multiplexSubFilename(pGroup, 1);
   531      if( rc==SQLITE_OK ){
   532        pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
   533      }
   534      if( pSubOpen ){
   535        int exists, rc2, rc3;
   536        sqlite3_int64 sz;
   537  
   538        rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
   539        if( rc2==SQLITE_OK ){
   540          /* If the first overflow file exists and if the size of the main file
   541          ** is different from the chunk size, that means the chunk size is set
   542          ** set incorrectly.  So fix it.
   543          **
   544          ** Or, if the first overflow file does not exist and the main file is
   545          ** larger than the chunk size, that means the chunk size is too small.
   546          ** But we have no way of determining the intended chunk size, so 
   547          ** just disable the multiplexor all togethre.
   548          */
   549          rc3 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
   550              SQLITE_ACCESS_EXISTS, &exists);
   551          if( rc3==SQLITE_OK && exists && sz==(sz&0xffff0000) && sz>0
   552              && sz!=pGroup->szChunk ){
   553            pGroup->szChunk = sz;
   554          }else if( rc3==SQLITE_OK && !exists && sz>pGroup->szChunk ){
   555            pGroup->bEnabled = 0;
   556          }
   557        }
   558  
   559        if( pSubOpen->pMethods->iVersion==1 ){
   560          pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
   561        }else{
   562          pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
   563        }
   564        /* place this group at the head of our list */
   565        pGroup->pNext = gMultiplex.pGroups;
   566        if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
   567        gMultiplex.pGroups = pGroup;
   568      }else{
   569        multiplexFreeComponents(pGroup);
   570        sqlite3_free(pGroup);
   571      }
   572    }
   573    multiplexLeave();
   574    sqlite3_free(zToFree);
   575    return rc;
   576  }
   577  
   578  /*
   579  ** This is the xDelete method used for the "multiplex" VFS.
   580  ** It attempts to delete the filename specified.
   581  */
   582  static int multiplexDelete(
   583    sqlite3_vfs *pVfs,         /* The multiplex VFS */
   584    const char *zName,         /* Name of file to delete */
   585    int syncDir
   586  ){
   587    sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   588    return pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
   589  }
   590  
   591  static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
   592    return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
   593  }
   594  static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
   595    return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
   596  }
   597  static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
   598    return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
   599  }
   600  static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
   601    gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
   602  }
   603  static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
   604    return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
   605  }
   606  static void multiplexDlClose(sqlite3_vfs *a, void *b){
   607    gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
   608  }
   609  static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
   610    return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
   611  }
   612  static int multiplexSleep(sqlite3_vfs *a, int b){
   613    return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
   614  }
   615  static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
   616    return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
   617  }
   618  static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
   619    return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
   620  }
   621  static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
   622    return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
   623  }
   624  
   625  /************************ I/O Method Wrappers *******************************/
   626  
   627  /* xClose requests get passed through to the original VFS.
   628  ** We loop over all open chunk handles and close them.
   629  ** The group structure for this file is unlinked from 
   630  ** our list of groups and freed.
   631  */
   632  static int multiplexClose(sqlite3_file *pConn){
   633    multiplexConn *p = (multiplexConn*)pConn;
   634    multiplexGroup *pGroup = p->pGroup;
   635    int rc = SQLITE_OK;
   636    multiplexEnter();
   637    multiplexFreeComponents(pGroup);
   638    /* remove from linked list */
   639    if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
   640    if( pGroup->pPrev ){
   641      pGroup->pPrev->pNext = pGroup->pNext;
   642    }else{
   643      gMultiplex.pGroups = pGroup->pNext;
   644    }
   645    sqlite3_free(pGroup);
   646    multiplexLeave();
   647    return rc;
   648  }
   649  
   650  /* Pass xRead requests thru to the original VFS after
   651  ** determining the correct chunk to operate on.
   652  ** Break up reads across chunk boundaries.
   653  */
   654  static int multiplexRead(
   655    sqlite3_file *pConn,
   656    void *pBuf,
   657    int iAmt,
   658    sqlite3_int64 iOfst
   659  ){
   660    multiplexConn *p = (multiplexConn*)pConn;
   661    multiplexGroup *pGroup = p->pGroup;
   662    int rc = SQLITE_OK;
   663    multiplexEnter();
   664    if( !pGroup->bEnabled ){
   665      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
   666      if( pSubOpen==0 ){
   667        rc = SQLITE_IOERR_READ;
   668      }else{
   669        rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
   670      }
   671    }else{
   672      while( iAmt > 0 ){
   673        int i = (int)(iOfst / pGroup->szChunk);
   674        sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
   675        if( pSubOpen ){
   676          int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
   677          if( extra<0 ) extra = 0;
   678          iAmt -= extra;
   679          rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
   680                                         iOfst % pGroup->szChunk);
   681          if( rc!=SQLITE_OK ) break;
   682          pBuf = (char *)pBuf + iAmt;
   683          iOfst += iAmt;
   684          iAmt = extra;
   685        }else{
   686          rc = SQLITE_IOERR_READ;
   687          break;
   688        }
   689      }
   690    }
   691    multiplexLeave();
   692    return rc;
   693  }
   694  
   695  /* Pass xWrite requests thru to the original VFS after
   696  ** determining the correct chunk to operate on.
   697  ** Break up writes across chunk boundaries.
   698  */
   699  static int multiplexWrite(
   700    sqlite3_file *pConn,
   701    const void *pBuf,
   702    int iAmt,
   703    sqlite3_int64 iOfst
   704  ){
   705    multiplexConn *p = (multiplexConn*)pConn;
   706    multiplexGroup *pGroup = p->pGroup;
   707    int rc = SQLITE_OK;
   708    multiplexEnter();
   709    if( !pGroup->bEnabled ){
   710      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
   711      if( pSubOpen==0 ){
   712        rc = SQLITE_IOERR_WRITE;
   713      }else{
   714        rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
   715      }
   716    }else{
   717      while( iAmt > 0 ){
   718        int i = (int)(iOfst / pGroup->szChunk);
   719        sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
   720        if( pSubOpen ){
   721          int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
   722                      pGroup->szChunk;
   723          if( extra<0 ) extra = 0;
   724          iAmt -= extra;
   725          rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
   726                                          iOfst % pGroup->szChunk);
   727          if( rc!=SQLITE_OK ) break;
   728          pBuf = (char *)pBuf + iAmt;
   729          iOfst += iAmt;
   730          iAmt = extra;
   731        }else{
   732          rc = SQLITE_IOERR_WRITE;
   733          break;
   734        }
   735      }
   736    }
   737    multiplexLeave();
   738    return rc;
   739  }
   740  
   741  /* Pass xTruncate requests thru to the original VFS after
   742  ** determining the correct chunk to operate on.  Delete any
   743  ** chunks above the truncate mark.
   744  */
   745  static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
   746    multiplexConn *p = (multiplexConn*)pConn;
   747    multiplexGroup *pGroup = p->pGroup;
   748    int rc = SQLITE_OK;
   749    multiplexEnter();
   750    if( !pGroup->bEnabled ){
   751      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
   752      if( pSubOpen==0 ){
   753        rc = SQLITE_IOERR_TRUNCATE;
   754      }else{
   755        rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
   756      }
   757    }else{
   758      int rc2;
   759      int i;
   760      sqlite3_file *pSubOpen;
   761      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
   762      /* delete the chunks above the truncate limit */
   763      for(i=(int)(size / pGroup->szChunk)+1; i<pGroup->nReal; i++){
   764        multiplexSubClose(pGroup, i, pOrigVfs);
   765      }
   766      pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->szChunk), &rc2,0);
   767      if( pSubOpen ){
   768        rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
   769        if( rc2!=SQLITE_OK ) rc = rc2;
   770      }else{
   771        rc = SQLITE_IOERR_TRUNCATE;
   772      }
   773    }
   774    multiplexLeave();
   775    return rc;
   776  }
   777  
   778  /* Pass xSync requests through to the original VFS without change
   779  */
   780  static int multiplexSync(sqlite3_file *pConn, int flags){
   781    multiplexConn *p = (multiplexConn*)pConn;
   782    multiplexGroup *pGroup = p->pGroup;
   783    int rc = SQLITE_OK;
   784    int i;
   785    multiplexEnter();
   786    for(i=0; i<pGroup->nReal; i++){
   787      sqlite3_file *pSubOpen = pGroup->aReal[i].p;
   788      if( pSubOpen ){
   789        int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
   790        if( rc2!=SQLITE_OK ) rc = rc2;
   791      }
   792    }
   793    multiplexLeave();
   794    return rc;
   795  }
   796  
   797  /* Pass xFileSize requests through to the original VFS.
   798  ** Aggregate the size of all the chunks before returning.
   799  */
   800  static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
   801    multiplexConn *p = (multiplexConn*)pConn;
   802    multiplexGroup *pGroup = p->pGroup;
   803    int rc = SQLITE_OK;
   804    int rc2;
   805    int i;
   806    multiplexEnter();
   807    if( !pGroup->bEnabled ){
   808      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
   809      if( pSubOpen==0 ){
   810        rc = SQLITE_IOERR_FSTAT;
   811      }else{
   812        rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
   813      }
   814    }else{
   815      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
   816      *pSize = 0;
   817      for(i=0; 1; i++){
   818        sqlite3_file *pSubOpen = 0;
   819        int exists = 0;
   820        rc = multiplexSubFilename(pGroup, i);
   821        if( rc ) break;
   822        rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z,
   823            SQLITE_ACCESS_EXISTS, &exists);
   824        if( rc2==SQLITE_OK && exists){
   825          /* if it exists, open it */
   826          pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
   827        }else{
   828          /* stop at first "gap" */
   829          break;
   830        }
   831        if( pSubOpen ){
   832          sqlite3_int64 sz;
   833          rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
   834          if( rc2!=SQLITE_OK ){
   835            rc = rc2;
   836          }else{
   837            if( sz>pGroup->szChunk ){
   838              rc = SQLITE_IOERR_FSTAT;
   839            }
   840            *pSize += sz;
   841          }
   842        }else{
   843          break;
   844        }
   845      }
   846    }
   847    multiplexLeave();
   848    return rc;
   849  }
   850  
   851  /* Pass xLock requests through to the original VFS unchanged.
   852  */
   853  static int multiplexLock(sqlite3_file *pConn, int lock){
   854    multiplexConn *p = (multiplexConn*)pConn;
   855    int rc;
   856    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   857    if( pSubOpen ){
   858      return pSubOpen->pMethods->xLock(pSubOpen, lock);
   859    }
   860    return SQLITE_BUSY;
   861  }
   862  
   863  /* Pass xUnlock requests through to the original VFS unchanged.
   864  */
   865  static int multiplexUnlock(sqlite3_file *pConn, int lock){
   866    multiplexConn *p = (multiplexConn*)pConn;
   867    int rc;
   868    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   869    if( pSubOpen ){
   870      return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
   871    }
   872    return SQLITE_IOERR_UNLOCK;
   873  }
   874  
   875  /* Pass xCheckReservedLock requests through to the original VFS unchanged.
   876  */
   877  static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
   878    multiplexConn *p = (multiplexConn*)pConn;
   879    int rc;
   880    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   881    if( pSubOpen ){
   882      return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
   883    }
   884    return SQLITE_IOERR_CHECKRESERVEDLOCK;
   885  }
   886  
   887  /* Pass xFileControl requests through to the original VFS unchanged,
   888  ** except for any MULTIPLEX_CTRL_* requests here.
   889  */
   890  static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
   891    multiplexConn *p = (multiplexConn*)pConn;
   892    multiplexGroup *pGroup = p->pGroup;
   893    int rc = SQLITE_ERROR;
   894    sqlite3_file *pSubOpen;
   895  
   896    if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
   897    switch( op ){
   898      case MULTIPLEX_CTRL_ENABLE:
   899        if( pArg ) {
   900          int bEnabled = *(int *)pArg;
   901          pGroup->bEnabled = bEnabled;
   902          rc = SQLITE_OK;
   903        }
   904        break;
   905      case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
   906        if( pArg ) {
   907          unsigned int szChunk = *(unsigned*)pArg;
   908          if( szChunk<1 ){
   909            rc = SQLITE_MISUSE;
   910          }else{
   911            /* Round up to nearest multiple of MAX_PAGE_SIZE. */
   912            szChunk = (szChunk + (MAX_PAGE_SIZE-1));
   913            szChunk &= ~(MAX_PAGE_SIZE-1);
   914            pGroup->szChunk = szChunk;
   915            rc = SQLITE_OK;
   916          }
   917        }
   918        break;
   919      case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
   920        rc = SQLITE_OK;
   921        break;
   922      case SQLITE_FCNTL_SIZE_HINT:
   923      case SQLITE_FCNTL_CHUNK_SIZE:
   924        /* no-op these */
   925        rc = SQLITE_OK;
   926        break;
   927      default:
   928        pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
   929        if( pSubOpen ){
   930          rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
   931        }
   932        break;
   933    }
   934    return rc;
   935  }
   936  
   937  /* Pass xSectorSize requests through to the original VFS unchanged.
   938  */
   939  static int multiplexSectorSize(sqlite3_file *pConn){
   940    multiplexConn *p = (multiplexConn*)pConn;
   941    int rc;
   942    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   943    if( pSubOpen ){
   944      return pSubOpen->pMethods->xSectorSize(pSubOpen);
   945    }
   946    return DEFAULT_SECTOR_SIZE;
   947  }
   948  
   949  /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
   950  */
   951  static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
   952    multiplexConn *p = (multiplexConn*)pConn;
   953    int rc;
   954    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   955    if( pSubOpen ){
   956      return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
   957    }
   958    return 0;
   959  }
   960  
   961  /* Pass xShmMap requests through to the original VFS unchanged.
   962  */
   963  static int multiplexShmMap(
   964    sqlite3_file *pConn,            /* Handle open on database file */
   965    int iRegion,                    /* Region to retrieve */
   966    int szRegion,                   /* Size of regions */
   967    int bExtend,                    /* True to extend file if necessary */
   968    void volatile **pp              /* OUT: Mapped memory */
   969  ){
   970    multiplexConn *p = (multiplexConn*)pConn;
   971    int rc;
   972    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   973    if( pSubOpen ){
   974      return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
   975    }
   976    return SQLITE_IOERR;
   977  }
   978  
   979  /* Pass xShmLock requests through to the original VFS unchanged.
   980  */
   981  static int multiplexShmLock(
   982    sqlite3_file *pConn,       /* Database file holding the shared memory */
   983    int ofst,                  /* First lock to acquire or release */
   984    int n,                     /* Number of locks to acquire or release */
   985    int flags                  /* What to do with the lock */
   986  ){
   987    multiplexConn *p = (multiplexConn*)pConn;
   988    int rc;
   989    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
   990    if( pSubOpen ){
   991      return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
   992    }
   993    return SQLITE_BUSY;
   994  }
   995  
   996  /* Pass xShmBarrier requests through to the original VFS unchanged.
   997  */
   998  static void multiplexShmBarrier(sqlite3_file *pConn){
   999    multiplexConn *p = (multiplexConn*)pConn;
  1000    int rc;
  1001    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
  1002    if( pSubOpen ){
  1003      pSubOpen->pMethods->xShmBarrier(pSubOpen);
  1004    }
  1005  }
  1006  
  1007  /* Pass xShmUnmap requests through to the original VFS unchanged.
  1008  */
  1009  static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
  1010    multiplexConn *p = (multiplexConn*)pConn;
  1011    int rc;
  1012    sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
  1013    if( pSubOpen ){
  1014      return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
  1015    }
  1016    return SQLITE_OK;
  1017  }
  1018  
  1019  /************************** Public Interfaces *****************************/
  1020  /*
  1021  ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
  1022  **
  1023  ** Use the VFS named zOrigVfsName as the VFS that does the actual work.  
  1024  ** Use the default if zOrigVfsName==NULL.  
  1025  **
  1026  ** The multiplex VFS shim is named "multiplex".  It will become the default
  1027  ** VFS if makeDefault is non-zero.
  1028  **
  1029  ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
  1030  ** during start-up.
  1031  */
  1032  int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
  1033    sqlite3_vfs *pOrigVfs;
  1034    if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
  1035    pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
  1036    if( pOrigVfs==0 ) return SQLITE_ERROR;
  1037    assert( pOrigVfs!=&gMultiplex.sThisVfs );
  1038    gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
  1039    if( !gMultiplex.pMutex ){
  1040      return SQLITE_NOMEM;
  1041    }
  1042    gMultiplex.pGroups = NULL;
  1043    gMultiplex.isInitialized = 1;
  1044    gMultiplex.pOrigVfs = pOrigVfs;
  1045    gMultiplex.sThisVfs = *pOrigVfs;
  1046    gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
  1047    gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
  1048    gMultiplex.sThisVfs.xOpen = multiplexOpen;
  1049    gMultiplex.sThisVfs.xDelete = multiplexDelete;
  1050    gMultiplex.sThisVfs.xAccess = multiplexAccess;
  1051    gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
  1052    gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
  1053    gMultiplex.sThisVfs.xDlError = multiplexDlError;
  1054    gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
  1055    gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
  1056    gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
  1057    gMultiplex.sThisVfs.xSleep = multiplexSleep;
  1058    gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
  1059    gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
  1060    gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
  1061  
  1062    gMultiplex.sIoMethodsV1.iVersion = 1;
  1063    gMultiplex.sIoMethodsV1.xClose = multiplexClose;
  1064    gMultiplex.sIoMethodsV1.xRead = multiplexRead;
  1065    gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
  1066    gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
  1067    gMultiplex.sIoMethodsV1.xSync = multiplexSync;
  1068    gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
  1069    gMultiplex.sIoMethodsV1.xLock = multiplexLock;
  1070    gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
  1071    gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
  1072    gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
  1073    gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
  1074    gMultiplex.sIoMethodsV1.xDeviceCharacteristics =
  1075                                              multiplexDeviceCharacteristics;
  1076    gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
  1077    gMultiplex.sIoMethodsV2.iVersion = 2;
  1078    gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
  1079    gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
  1080    gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
  1081    gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
  1082    sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
  1083  
  1084    sqlite3_auto_extension((void*)multiplexFuncInit);
  1085  
  1086    return SQLITE_OK;
  1087  }
  1088  
  1089  /*
  1090  ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
  1091  **
  1092  ** All SQLite database connections must be closed before calling this
  1093  ** routine.
  1094  **
  1095  ** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
  1096  ** shutting down in order to free all remaining multiplex groups.
  1097  */
  1098  int sqlite3_multiplex_shutdown(void){
  1099    if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
  1100    if( gMultiplex.pGroups ) return SQLITE_MISUSE;
  1101    gMultiplex.isInitialized = 0;
  1102    sqlite3_mutex_free(gMultiplex.pMutex);
  1103    sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
  1104    memset(&gMultiplex, 0, sizeof(gMultiplex));
  1105    return SQLITE_OK;
  1106  }
  1107  
  1108  /***************************** Test Code ***********************************/
  1109  #ifdef SQLITE_TEST
  1110  #include <tcl.h>
  1111  extern const char *sqlite3TestErrorName(int);
  1112  
  1113  
  1114  /*
  1115  ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
  1116  */
  1117  static int test_multiplex_initialize(
  1118    void * clientData,
  1119    Tcl_Interp *interp,
  1120    int objc,
  1121    Tcl_Obj *CONST objv[]
  1122  ){
  1123    const char *zName;              /* Name of new multiplex VFS */
  1124    int makeDefault;                /* True to make the new VFS the default */
  1125    int rc;                         /* Value returned by multiplex_initialize() */
  1126  
  1127    UNUSED_PARAMETER(clientData);
  1128  
  1129    /* Process arguments */
  1130    if( objc!=3 ){
  1131      Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
  1132      return TCL_ERROR;
  1133    }
  1134    zName = Tcl_GetString(objv[1]);
  1135    if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
  1136    if( zName[0]=='\0' ) zName = 0;
  1137  
  1138    /* Call sqlite3_multiplex_initialize() */
  1139    rc = sqlite3_multiplex_initialize(zName, makeDefault);
  1140    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
  1141  
  1142    return TCL_OK;
  1143  }
  1144  
  1145  /*
  1146  ** tclcmd: sqlite3_multiplex_shutdown
  1147  */
  1148  static int test_multiplex_shutdown(
  1149    void * clientData,
  1150    Tcl_Interp *interp,
  1151    int objc,
  1152    Tcl_Obj *CONST objv[]
  1153  ){
  1154    int rc;                         /* Value returned by multiplex_shutdown() */
  1155  
  1156    UNUSED_PARAMETER(clientData);
  1157  
  1158    if( objc!=1 ){
  1159      Tcl_WrongNumArgs(interp, 1, objv, "");
  1160      return TCL_ERROR;
  1161    }
  1162  
  1163    /* Call sqlite3_multiplex_shutdown() */
  1164    rc = sqlite3_multiplex_shutdown();
  1165    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
  1166  
  1167    return TCL_OK;
  1168  }
  1169  
  1170  /*
  1171  ** tclcmd:  sqlite3_multiplex_dump
  1172  */
  1173  static int test_multiplex_dump(
  1174    void * clientData,
  1175    Tcl_Interp *interp,
  1176    int objc,
  1177    Tcl_Obj *CONST objv[]
  1178  ){
  1179    Tcl_Obj *pResult;
  1180    Tcl_Obj *pGroupTerm;
  1181    multiplexGroup *pGroup;
  1182    int i;
  1183    int nChunks = 0;
  1184  
  1185    UNUSED_PARAMETER(clientData);
  1186    UNUSED_PARAMETER(objc);
  1187    UNUSED_PARAMETER(objv);
  1188  
  1189    pResult = Tcl_NewObj();
  1190    multiplexEnter();
  1191    for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
  1192      pGroupTerm = Tcl_NewObj();
  1193  
  1194      pGroup->zName[pGroup->nName] = '\0';
  1195      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1196            Tcl_NewStringObj(pGroup->zName, -1));
  1197      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1198            Tcl_NewIntObj(pGroup->nName));
  1199      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1200            Tcl_NewIntObj(pGroup->flags));
  1201  
  1202      /* count number of chunks with open handles */
  1203      for(i=0; i<pGroup->nReal; i++){
  1204        if( pGroup->aReal[i].p!=0 ) nChunks++;
  1205      }
  1206      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1207            Tcl_NewIntObj(nChunks));
  1208  
  1209      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1210            Tcl_NewIntObj(pGroup->szChunk));
  1211      Tcl_ListObjAppendElement(interp, pGroupTerm,
  1212            Tcl_NewIntObj(pGroup->nReal));
  1213  
  1214      Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
  1215    }
  1216    multiplexLeave();
  1217    Tcl_SetObjResult(interp, pResult);
  1218    return TCL_OK;
  1219  }
  1220  
  1221  /*
  1222  ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
  1223  */
  1224  static int test_multiplex_control(
  1225    ClientData cd,
  1226    Tcl_Interp *interp,
  1227    int objc,
  1228    Tcl_Obj *CONST objv[]
  1229  ){
  1230    int rc;                         /* Return code from file_control() */
  1231    int idx;                        /* Index in aSub[] */
  1232    Tcl_CmdInfo cmdInfo;            /* Command info structure for HANDLE */
  1233    sqlite3 *db;                    /* Underlying db handle for HANDLE */
  1234    int iValue = 0;
  1235    void *pArg = 0;
  1236  
  1237    struct SubCommand {
  1238      const char *zName;
  1239      int op;
  1240      int argtype;
  1241    } aSub[] = {
  1242      { "enable",       MULTIPLEX_CTRL_ENABLE,           1 },
  1243      { "chunk_size",   MULTIPLEX_CTRL_SET_CHUNK_SIZE,   1 },
  1244      { "max_chunks",   MULTIPLEX_CTRL_SET_MAX_CHUNKS,   1 },
  1245      { 0, 0, 0 }
  1246    };
  1247  
  1248    if( objc!=5 ){
  1249      Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
  1250      return TCL_ERROR;
  1251    }
  1252  
  1253    if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
  1254      Tcl_AppendResult(interp, "expected database handle, got \"", 0);
  1255      Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
  1256      return TCL_ERROR;
  1257    }else{
  1258      db = *(sqlite3 **)cmdInfo.objClientData;
  1259    }
  1260  
  1261    rc = Tcl_GetIndexFromObjStruct(
  1262        interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
  1263    );
  1264    if( rc!=TCL_OK ) return rc;
  1265  
  1266    switch( aSub[idx].argtype ){
  1267      case 1:
  1268        if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
  1269          return TCL_ERROR;
  1270        }
  1271        pArg = (void *)&iValue;
  1272        break;
  1273      default:
  1274        Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
  1275        return TCL_ERROR;
  1276    }
  1277  
  1278    rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
  1279    Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
  1280    return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
  1281  }
  1282  
  1283  /*
  1284  ** This routine registers the custom TCL commands defined in this
  1285  ** module.  This should be the only procedure visible from outside
  1286  ** of this module.
  1287  */
  1288  int Sqlitemultiplex_Init(Tcl_Interp *interp){
  1289    static struct {
  1290       char *zName;
  1291       Tcl_ObjCmdProc *xProc;
  1292    } aCmd[] = {
  1293      { "sqlite3_multiplex_initialize", test_multiplex_initialize },
  1294      { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
  1295      { "sqlite3_multiplex_dump", test_multiplex_dump },
  1296      { "sqlite3_multiplex_control", test_multiplex_control },
  1297    };
  1298    int i;
  1299  
  1300    for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
  1301      Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
  1302    }
  1303  
  1304    return TCL_OK;
  1305  }
  1306  #endif