Annotation of embedaddon/sqlite3/src/test_multiplex.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>