Annotation of embedaddon/sqlite3/src/test_multiplex.c, revision 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>