18a922f75Sshaneh /* 28a922f75Sshaneh ** 2010 October 28 38a922f75Sshaneh ** 48a922f75Sshaneh ** The author disclaims copyright to this source code. In place of 58a922f75Sshaneh ** a legal notice, here is a blessing: 68a922f75Sshaneh ** 78a922f75Sshaneh ** May you do good and not evil. 88a922f75Sshaneh ** May you find forgiveness for yourself and forgive others. 98a922f75Sshaneh ** May you share freely, never taking more than you give. 108a922f75Sshaneh ** 118a922f75Sshaneh ************************************************************************* 128a922f75Sshaneh ** 138a922f75Sshaneh ** This file contains a VFS "shim" - a layer that sits in between the 148a922f75Sshaneh ** pager and the real VFS. 158a922f75Sshaneh ** 168a922f75Sshaneh ** This particular shim enforces a multiplex system on DB files. 178a922f75Sshaneh ** This shim shards/partitions a single DB file into smaller 188a922f75Sshaneh ** "chunks" such that the total DB file size may exceed the maximum 198a922f75Sshaneh ** file size of the underlying file system. 208a922f75Sshaneh ** 218a922f75Sshaneh */ 228a922f75Sshaneh #include "sqlite3.h" 238a922f75Sshaneh #include <string.h> 248a922f75Sshaneh #include <assert.h> 25d50deeebSshaneh #include "test_multiplex.h" 26d50deeebSshaneh 2778c4de4cSshaneh #ifndef SQLITE_CORE 2878c4de4cSshaneh #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ 2978c4de4cSshaneh #endif 3078c4de4cSshaneh #include "sqlite3ext.h" 3178c4de4cSshaneh 32bb201344Sshaneh /* 333801b65dSshaneh ** These should be defined to be the same as the values in 343801b65dSshaneh ** sqliteInt.h. They are defined seperately here so that 353801b65dSshaneh ** the multiplex VFS shim can be built as a loadable 363801b65dSshaneh ** module. 373801b65dSshaneh */ 383801b65dSshaneh #define UNUSED_PARAMETER(x) (void)(x) 393801b65dSshaneh #define MAX_PAGE_SIZE 0x10000 403801b65dSshaneh #define DEFAULT_SECTOR_SIZE 0x1000 413801b65dSshaneh 423801b65dSshaneh /* 43bb201344Sshaneh ** For a build without mutexes, no-op the mutex calls. 44bb201344Sshaneh */ 45bb201344Sshaneh #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 46bb201344Sshaneh #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) 47bb201344Sshaneh #define sqlite3_mutex_free(X) 48bb201344Sshaneh #define sqlite3_mutex_enter(X) 49bb201344Sshaneh #define sqlite3_mutex_try(X) SQLITE_OK 50bb201344Sshaneh #define sqlite3_mutex_leave(X) 51bb201344Sshaneh #define sqlite3_mutex_held(X) ((void)(X),1) 52bb201344Sshaneh #define sqlite3_mutex_notheld(X) ((void)(X),1) 53bb201344Sshaneh #endif /* SQLITE_THREADSAFE==0 */ 54bb201344Sshaneh 55bb201344Sshaneh 568a922f75Sshaneh /************************ Shim Definitions ******************************/ 578a922f75Sshaneh 58d9523b97Sshaneh #define SQLITE_MULTIPLEX_VFS_NAME "multiplex" 59d9523b97Sshaneh 60050d09abSshaneh /* This is the limit on the chunk size. It may be changed by calling 6178c4de4cSshaneh ** the xFileControl() interface. It will be rounded up to a 623801b65dSshaneh ** multiple of MAX_PAGE_SIZE. We default it here to 1GB. 63050d09abSshaneh */ 643801b65dSshaneh #define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384) 65c27fa4b0Sshaneh 66050d09abSshaneh /* Default limit on number of chunks. Care should be taken 67050d09abSshaneh ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT 68050d09abSshaneh ** format specifier. It may be changed by calling 69d50deeebSshaneh ** the xFileControl() interface. 70050d09abSshaneh */ 718a922f75Sshaneh #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 72050d09abSshaneh 73050d09abSshaneh /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the 74050d09abSshaneh ** last SQLITE_MULTIPLEX_EXT_SZ characters of the 75050d09abSshaneh ** filename will be overwritten, otherwise, the 76050d09abSshaneh ** multiplex extension is simply appended to the filename. 77050d09abSshaneh ** Ex. (undefined) test.db -> test.db01 78050d09abSshaneh ** (defined) test.db -> test.01 79050d09abSshaneh ** Chunk 0 does not have a modified extension. 80050d09abSshaneh */ 81050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_FMT "%02d" 82050d09abSshaneh #define SQLITE_MULTIPLEX_EXT_SZ 2 838a922f75Sshaneh 848a922f75Sshaneh /************************ Object Definitions ******************************/ 858a922f75Sshaneh 868a922f75Sshaneh /* Forward declaration of all object types */ 878a922f75Sshaneh typedef struct multiplexGroup multiplexGroup; 888a922f75Sshaneh typedef struct multiplexConn multiplexConn; 898a922f75Sshaneh 908a922f75Sshaneh /* 918a922f75Sshaneh ** A "multiplex group" is a collection of files that collectively 928a922f75Sshaneh ** makeup a single SQLite DB file. This allows the size of the DB 938a922f75Sshaneh ** to exceed the limits imposed by the file system. 948a922f75Sshaneh ** 958a922f75Sshaneh ** There is an instance of the following object for each defined multiplex 968a922f75Sshaneh ** group. 978a922f75Sshaneh */ 988a922f75Sshaneh struct multiplexGroup { 99050d09abSshaneh sqlite3_file **pReal; /* Handles to each chunk */ 100bb201344Sshaneh char *bOpen; /* array of bools - 0 if chunk not opened */ 1018a922f75Sshaneh char *zName; /* Base filename of this group */ 1028a922f75Sshaneh int nName; /* Length of base filename */ 1038a922f75Sshaneh int flags; /* Flags used for original opening */ 104d50deeebSshaneh int nChunkSize; /* Chunk size used for this group */ 105d50deeebSshaneh int nMaxChunks; /* Max number of chunks for this group */ 106d50deeebSshaneh int bEnabled; /* TRUE to use Multiplex VFS for this file */ 1078a922f75Sshaneh multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ 1088a922f75Sshaneh }; 1098a922f75Sshaneh 1108a922f75Sshaneh /* 1118a922f75Sshaneh ** An instance of the following object represents each open connection 1128a922f75Sshaneh ** to a file that is multiplex'ed. This object is a 1138a922f75Sshaneh ** subclass of sqlite3_file. The sqlite3_file object for the underlying 1148a922f75Sshaneh ** VFS is appended to this structure. 1158a922f75Sshaneh */ 1168a922f75Sshaneh struct multiplexConn { 1178a922f75Sshaneh sqlite3_file base; /* Base class - must be first */ 1188a922f75Sshaneh multiplexGroup *pGroup; /* The underlying group of files */ 1198a922f75Sshaneh }; 1208a922f75Sshaneh 1218a922f75Sshaneh /************************* Global Variables **********************************/ 1228a922f75Sshaneh /* 1238a922f75Sshaneh ** All global variables used by this file are containing within the following 1248a922f75Sshaneh ** gMultiplex structure. 1258a922f75Sshaneh */ 1268a922f75Sshaneh static struct { 1278a922f75Sshaneh /* The pOrigVfs is the real, original underlying VFS implementation. 1288a922f75Sshaneh ** Most operations pass-through to the real VFS. This value is read-only 1298a922f75Sshaneh ** during operation. It is only modified at start-time and thus does not 1308a922f75Sshaneh ** require a mutex. 1318a922f75Sshaneh */ 1328a922f75Sshaneh sqlite3_vfs *pOrigVfs; 1338a922f75Sshaneh 1348a922f75Sshaneh /* The sThisVfs is the VFS structure used by this shim. It is initialized 1358a922f75Sshaneh ** at start-time and thus does not require a mutex 1368a922f75Sshaneh */ 1378a922f75Sshaneh sqlite3_vfs sThisVfs; 1388a922f75Sshaneh 1398a922f75Sshaneh /* The sIoMethods defines the methods used by sqlite3_file objects 1408a922f75Sshaneh ** associated with this shim. It is initialized at start-time and does 1418a922f75Sshaneh ** not require a mutex. 1428a922f75Sshaneh ** 1438a922f75Sshaneh ** When the underlying VFS is called to open a file, it might return 1448a922f75Sshaneh ** either a version 1 or a version 2 sqlite3_file object. This shim 1458a922f75Sshaneh ** has to create a wrapper sqlite3_file of the same version. Hence 1468a922f75Sshaneh ** there are two I/O method structures, one for version 1 and the other 1478a922f75Sshaneh ** for version 2. 1488a922f75Sshaneh */ 1498a922f75Sshaneh sqlite3_io_methods sIoMethodsV1; 1508a922f75Sshaneh sqlite3_io_methods sIoMethodsV2; 1518a922f75Sshaneh 152fd1552f2Sshaneh /* True when this shim has been initialized. 1538a922f75Sshaneh */ 1548a922f75Sshaneh int isInitialized; 1558a922f75Sshaneh 1568a922f75Sshaneh /* For run-time access any of the other global data structures in this 1578a922f75Sshaneh ** shim, the following mutex must be held. 1588a922f75Sshaneh */ 1598a922f75Sshaneh sqlite3_mutex *pMutex; 1608a922f75Sshaneh 1618a922f75Sshaneh /* List of multiplexGroup objects. 1628a922f75Sshaneh */ 1638a922f75Sshaneh multiplexGroup *pGroups; 1648a922f75Sshaneh 165b5830294Sshaneh /* Storage for temp file names. Allocated during 166b5830294Sshaneh ** initialization to the max pathname of the underlying VFS. 167b5830294Sshaneh */ 168b5830294Sshaneh char *zName; 169b5830294Sshaneh 1708a922f75Sshaneh } gMultiplex; 1718a922f75Sshaneh 1728a922f75Sshaneh /************************* Utility Routines *********************************/ 1738a922f75Sshaneh /* 1748a922f75Sshaneh ** Acquire and release the mutex used to serialize access to the 1758a922f75Sshaneh ** list of multiplexGroups. 1768a922f75Sshaneh */ 1778a922f75Sshaneh static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } 1788a922f75Sshaneh static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } 1798a922f75Sshaneh 1803801b65dSshaneh /* 1813801b65dSshaneh ** Compute a string length that is limited to what can be stored in 1823801b65dSshaneh ** lower 30 bits of a 32-bit signed integer. 1833801b65dSshaneh ** 1843801b65dSshaneh ** The value returned will never be negative. Nor will it ever be greater 1853801b65dSshaneh ** than the actual length of the string. For very long strings (greater 1863801b65dSshaneh ** than 1GiB) the value returned might be less than the true string length. 1873801b65dSshaneh */ 188*cc4e19beSshaneh static int multiplexStrlen30(const char *z){ 1893801b65dSshaneh const char *z2 = z; 1903801b65dSshaneh if( z==0 ) return 0; 1913801b65dSshaneh while( *z2 ){ z2++; } 1923801b65dSshaneh return 0x3fffffff & (int)(z2 - z); 1933801b65dSshaneh } 1943801b65dSshaneh 195*cc4e19beSshaneh /* 196*cc4e19beSshaneh ** Create a temporary file name in zBuf. zBuf must be big enough to 197*cc4e19beSshaneh ** hold at pOrigVfs->mxPathname characters. This function departs 198*cc4e19beSshaneh ** from the traditional temporary name generation in the os_win 199*cc4e19beSshaneh ** and os_unix VFS in several ways, but is necessary so that 200*cc4e19beSshaneh ** the file name is known for temporary files (like those used 201*cc4e19beSshaneh ** during vacuum.) 202*cc4e19beSshaneh ** 203*cc4e19beSshaneh ** N.B. This routine assumes your underlying VFS is ok with using 204*cc4e19beSshaneh ** "/" as a directory seperator. This is the default for UNIXs 205*cc4e19beSshaneh ** and is allowed (even mixed) for most versions of Windows. 206*cc4e19beSshaneh */ 207*cc4e19beSshaneh static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){ 208*cc4e19beSshaneh static char zChars[] = 209*cc4e19beSshaneh "abcdefghijklmnopqrstuvwxyz" 210*cc4e19beSshaneh "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 211*cc4e19beSshaneh "0123456789"; 212*cc4e19beSshaneh int i,j; 213*cc4e19beSshaneh int attempts = 0; 214*cc4e19beSshaneh int exists = 0; 215*cc4e19beSshaneh int rc = SQLITE_ERROR; 216*cc4e19beSshaneh 217*cc4e19beSshaneh /* Check that the output buffer is large enough for 218*cc4e19beSshaneh ** pVfs->mxPathname characters. 219*cc4e19beSshaneh */ 220*cc4e19beSshaneh if( pOrigVfs->mxPathname <= nBuf ){ 221*cc4e19beSshaneh 222*cc4e19beSshaneh /* sqlite3_temp_directory should always be less than 223*cc4e19beSshaneh ** pVfs->mxPathname characters. 224*cc4e19beSshaneh */ 225*cc4e19beSshaneh sqlite3_snprintf(pOrigVfs->mxPathname, 226*cc4e19beSshaneh zBuf, 227*cc4e19beSshaneh "%s/", 228*cc4e19beSshaneh sqlite3_temp_directory ? sqlite3_temp_directory : "."); 229*cc4e19beSshaneh 230*cc4e19beSshaneh /* Check that the output buffer is large enough for the temporary file 231*cc4e19beSshaneh ** name. 232*cc4e19beSshaneh */ 233*cc4e19beSshaneh j = multiplexStrlen30(zBuf); 234*cc4e19beSshaneh if( (j + 8 + 1 + 3 + 1) <= nBuf ){ 235*cc4e19beSshaneh /* Make 3 attempts to generate a unique name. */ 236*cc4e19beSshaneh do { 237*cc4e19beSshaneh attempts++; 238*cc4e19beSshaneh sqlite3_randomness(8, &zBuf[j]); 239*cc4e19beSshaneh for(i=0; i<8; i++){ 240*cc4e19beSshaneh zBuf[j+i] = (char)zChars[ ((unsigned char)zBuf[j+i])%(sizeof(zChars)-1) ]; 241*cc4e19beSshaneh } 242*cc4e19beSshaneh memcpy(&zBuf[j+i], ".tmp", 5); 243*cc4e19beSshaneh rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists); 244*cc4e19beSshaneh } while ( (rc==SQLITE_OK) && exists && (attempts<3) ); 245*cc4e19beSshaneh if( rc==SQLITE_OK && exists ){ 246*cc4e19beSshaneh rc = SQLITE_ERROR; 247*cc4e19beSshaneh } 248*cc4e19beSshaneh } 249*cc4e19beSshaneh } 250*cc4e19beSshaneh 251*cc4e19beSshaneh return rc; 252*cc4e19beSshaneh } 253*cc4e19beSshaneh 2548a922f75Sshaneh /* Translate an sqlite3_file* that is really a multiplexGroup* into 2558a922f75Sshaneh ** the sqlite3_file* for the underlying original VFS. 2568a922f75Sshaneh */ 2578a922f75Sshaneh static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ 2588a922f75Sshaneh multiplexGroup *pGroup = pConn->pGroup; 2598a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 260d50deeebSshaneh if( iChunk<pGroup->nMaxChunks ){ 2618a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ 2628a922f75Sshaneh if( !pGroup->bOpen[iChunk] ){ 263050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 264050d09abSshaneh if( iChunk ){ 265050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 266050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk); 267050d09abSshaneh #else 268050d09abSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk); 269050d09abSshaneh #endif 270050d09abSshaneh } 271050d09abSshaneh *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags); 2728a922f75Sshaneh if( *rc==SQLITE_OK ){ 2738a922f75Sshaneh pGroup->bOpen[iChunk] = -1; 2748a922f75Sshaneh return pSubOpen; 2758a922f75Sshaneh } 2768a922f75Sshaneh return NULL; 2778a922f75Sshaneh } 2788a922f75Sshaneh *rc = SQLITE_OK; 2798a922f75Sshaneh return pSubOpen; 2808a922f75Sshaneh } 281b5830294Sshaneh *rc = SQLITE_FULL; 2828a922f75Sshaneh return NULL; 2838a922f75Sshaneh } 2848a922f75Sshaneh 2853801b65dSshaneh /* 2863801b65dSshaneh ** This is the implementation of the multiplex_control() SQL function. 2873801b65dSshaneh */ 288d50deeebSshaneh static void multiplexControlFunc( 289d50deeebSshaneh sqlite3_context *context, 290d50deeebSshaneh int argc, 291d50deeebSshaneh sqlite3_value **argv 292d50deeebSshaneh ){ 29378c4de4cSshaneh int rc = SQLITE_OK; 294d8ce22bbSshaneh sqlite3 *db = sqlite3_context_db_handle(context); 295d8ce22bbSshaneh int op; 296d8ce22bbSshaneh int iVal; 297d8ce22bbSshaneh 298d8ce22bbSshaneh if( !db || argc!=2 ){ 299d8ce22bbSshaneh rc = SQLITE_ERROR; 300d8ce22bbSshaneh }else{ 301d8ce22bbSshaneh /* extract params */ 302d8ce22bbSshaneh op = sqlite3_value_int(argv[0]); 303d8ce22bbSshaneh iVal = sqlite3_value_int(argv[1]); 304d8ce22bbSshaneh /* map function op to file_control op */ 30578c4de4cSshaneh switch( op ){ 30678c4de4cSshaneh case 1: 30778c4de4cSshaneh op = MULTIPLEX_CTRL_ENABLE; 30878c4de4cSshaneh break; 30978c4de4cSshaneh case 2: 31078c4de4cSshaneh op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; 31178c4de4cSshaneh break; 31278c4de4cSshaneh case 3: 31378c4de4cSshaneh op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; 31478c4de4cSshaneh break; 31578c4de4cSshaneh default: 3163801b65dSshaneh rc = SQLITE_NOTFOUND; 31778c4de4cSshaneh break; 31878c4de4cSshaneh } 319d8ce22bbSshaneh } 32078c4de4cSshaneh if( rc==SQLITE_OK ){ 321d8ce22bbSshaneh rc = sqlite3_file_control(db, 0, op, &iVal); 322d9523b97Sshaneh } 3233801b65dSshaneh sqlite3_result_error_code(context, rc); 324d9523b97Sshaneh } 325d9523b97Sshaneh 326d9523b97Sshaneh /* 3273801b65dSshaneh ** This is the entry point to register the auto-extension for the 3283801b65dSshaneh ** multiplex_control() function. 329d9523b97Sshaneh */ 330d50deeebSshaneh static int multiplexFuncInit( 33178c4de4cSshaneh sqlite3 *db, 33278c4de4cSshaneh char **pzErrMsg, 33378c4de4cSshaneh const sqlite3_api_routines *pApi 334d50deeebSshaneh ){ 335ac039688Sshaneh int rc; 336ac039688Sshaneh rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 337d8ce22bbSshaneh 0, multiplexControlFunc, 0, 0); 338ac039688Sshaneh return rc; 339d9523b97Sshaneh } 340d9523b97Sshaneh 3418a922f75Sshaneh /************************* VFS Method Wrappers *****************************/ 342b5830294Sshaneh 3438a922f75Sshaneh /* 3448a922f75Sshaneh ** This is the xOpen method used for the "multiplex" VFS. 3458a922f75Sshaneh ** 3468a922f75Sshaneh ** Most of the work is done by the underlying original VFS. This method 3478a922f75Sshaneh ** simply links the new file into the appropriate multiplex group if it is a 3488a922f75Sshaneh ** file that needs to be tracked. 3498a922f75Sshaneh */ 3508a922f75Sshaneh static int multiplexOpen( 3518a922f75Sshaneh sqlite3_vfs *pVfs, /* The multiplex VFS */ 3528a922f75Sshaneh const char *zName, /* Name of file to be opened */ 3538a922f75Sshaneh sqlite3_file *pConn, /* Fill in this file descriptor */ 3548a922f75Sshaneh int flags, /* Flags to control the opening */ 3558a922f75Sshaneh int *pOutFlags /* Flags showing results of opening */ 3568a922f75Sshaneh ){ 357*cc4e19beSshaneh int rc = SQLITE_OK; /* Result code */ 3588a922f75Sshaneh multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ 3598a922f75Sshaneh multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ 3608a922f75Sshaneh sqlite3_file *pSubOpen; /* Real file descriptor */ 3618a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 362*cc4e19beSshaneh int nName; 3638a922f75Sshaneh int i; 364050d09abSshaneh int sz; 3658a922f75Sshaneh 3668a922f75Sshaneh UNUSED_PARAMETER(pVfs); 3678a922f75Sshaneh 3688a922f75Sshaneh /* We need to create a group structure and manage 3698a922f75Sshaneh ** access to this group of files. 3708a922f75Sshaneh */ 3718a922f75Sshaneh multiplexEnter(); 3728a922f75Sshaneh pMultiplexOpen = (multiplexConn*)pConn; 373*cc4e19beSshaneh 374*cc4e19beSshaneh /* If the second argument to this function is NULL, generate a 375*cc4e19beSshaneh ** temporary file name to use. This will be handled by the 376*cc4e19beSshaneh ** original xOpen method. We just need to allocate space for 377*cc4e19beSshaneh ** it. 378*cc4e19beSshaneh */ 379*cc4e19beSshaneh if( !zName ){ 380*cc4e19beSshaneh rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, gMultiplex.zName); 381*cc4e19beSshaneh zName = gMultiplex.zName; 382*cc4e19beSshaneh } 383*cc4e19beSshaneh 384*cc4e19beSshaneh if( rc==SQLITE_OK ){ 385050d09abSshaneh /* allocate space for group */ 386*cc4e19beSshaneh nName = multiplexStrlen30(zName); 387050d09abSshaneh sz = sizeof(multiplexGroup) /* multiplexGroup */ 388d50deeebSshaneh + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */ 389d50deeebSshaneh + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */ 390d50deeebSshaneh + SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */ 391050d09abSshaneh + nName + 1; /* zName */ 392050d09abSshaneh #ifndef SQLITE_MULTIPLEX_EXT_OVWR 393050d09abSshaneh sz += SQLITE_MULTIPLEX_EXT_SZ; 394050d09abSshaneh assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); 395050d09abSshaneh #else 396050d09abSshaneh assert(nName >= SQLITE_MULTIPLEX_EXT_SZ); 397050d09abSshaneh assert(nName < pOrigVfs->mxPathname); 398050d09abSshaneh #endif 399050d09abSshaneh pGroup = sqlite3_malloc( sz ); 4008a922f75Sshaneh if( pGroup==0 ){ 4018a922f75Sshaneh rc=SQLITE_NOMEM; 402*cc4e19beSshaneh } 403*cc4e19beSshaneh } 404*cc4e19beSshaneh 405*cc4e19beSshaneh if( rc==SQLITE_OK ){ 406050d09abSshaneh /* assign pointers to extra space allocated */ 407050d09abSshaneh char *p = (char *)&pGroup[1]; 4088a922f75Sshaneh pMultiplexOpen->pGroup = pGroup; 409050d09abSshaneh memset(pGroup, 0, sz); 410c27fa4b0Sshaneh pGroup->bEnabled = -1; 411d50deeebSshaneh pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; 412d50deeebSshaneh pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; 413050d09abSshaneh pGroup->pReal = (sqlite3_file **)p; 414d50deeebSshaneh p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks); 415d50deeebSshaneh for(i=0; i<pGroup->nMaxChunks; i++){ 416050d09abSshaneh pGroup->pReal[i] = (sqlite3_file *)p; 417050d09abSshaneh p += pOrigVfs->szOsFile; 4188a922f75Sshaneh } 419d50deeebSshaneh /* bOpen[] vals should all be zero from memset above */ 420050d09abSshaneh pGroup->bOpen = p; 421d50deeebSshaneh p += pGroup->nMaxChunks; 422050d09abSshaneh pGroup->zName = p; 423050d09abSshaneh /* save off base filename, name length, and original open flags */ 4248a922f75Sshaneh memcpy(pGroup->zName, zName, nName+1); 4258a922f75Sshaneh pGroup->nName = nName; 4268a922f75Sshaneh pGroup->flags = flags; 4278a922f75Sshaneh pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); 4288a922f75Sshaneh if( pSubOpen ){ 429c27fa4b0Sshaneh /* if this file is already larger than chunk size, disable 430c27fa4b0Sshaneh ** the multiplex feature. 431c27fa4b0Sshaneh */ 432c27fa4b0Sshaneh sqlite3_int64 sz; 433c27fa4b0Sshaneh int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); 434c27fa4b0Sshaneh if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){ 435c27fa4b0Sshaneh pGroup->bEnabled = 0; 436c27fa4b0Sshaneh } 4378a922f75Sshaneh if( pSubOpen->pMethods->iVersion==1 ){ 4388a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; 4398a922f75Sshaneh }else{ 4408a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; 4418a922f75Sshaneh } 4428a922f75Sshaneh /* place this group at the head of our list */ 4438a922f75Sshaneh pGroup->pNext = gMultiplex.pGroups; 4448a922f75Sshaneh if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; 4458a922f75Sshaneh gMultiplex.pGroups = pGroup; 4468a922f75Sshaneh }else{ 4478a922f75Sshaneh sqlite3_free(pGroup); 4488a922f75Sshaneh } 4498a922f75Sshaneh } 4508a922f75Sshaneh multiplexLeave(); 4518a922f75Sshaneh return rc; 4528a922f75Sshaneh } 4538a922f75Sshaneh 454b5830294Sshaneh /* 455b5830294Sshaneh ** This is the xDelete method used for the "multiplex" VFS. 456b5830294Sshaneh ** It attempts to delete the filename specified, as well 457050d09abSshaneh ** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension. 458b5830294Sshaneh */ 459b5830294Sshaneh static int multiplexDelete( 460b5830294Sshaneh sqlite3_vfs *pVfs, /* The multiplex VFS */ 461b5830294Sshaneh const char *zName, /* Name of file to delete */ 462b5830294Sshaneh int syncDir 463b5830294Sshaneh ){ 464b5830294Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 465b5830294Sshaneh int rc = SQLITE_OK; 4663801b65dSshaneh int nName = multiplexStrlen30(zName); 467b5830294Sshaneh int i; 468b5830294Sshaneh 469b5830294Sshaneh UNUSED_PARAMETER(pVfs); 470b5830294Sshaneh 471b5830294Sshaneh multiplexEnter(); 472b5830294Sshaneh memcpy(gMultiplex.zName, zName, nName+1); 473d50deeebSshaneh for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){ 474b5830294Sshaneh int rc2; 475b5830294Sshaneh int exists = 0; 476050d09abSshaneh if( i ){ 477050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 4783801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 4793801b65dSshaneh gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, 4803801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 481050d09abSshaneh #else 4823801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 4833801b65dSshaneh gMultiplex.zName+nName, 4843801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 485050d09abSshaneh #endif 486050d09abSshaneh } 4873801b65dSshaneh rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, 4883801b65dSshaneh SQLITE_ACCESS_EXISTS, &exists); 489b5830294Sshaneh if( rc2==SQLITE_OK && exists ){ 490b5830294Sshaneh /* if it exists, delete it */ 491d50deeebSshaneh rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); 492b5830294Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 493b5830294Sshaneh }else{ 494b5830294Sshaneh /* stop at first "gap" */ 495b5830294Sshaneh break; 496b5830294Sshaneh } 497b5830294Sshaneh } 498b5830294Sshaneh multiplexLeave(); 499b5830294Sshaneh return rc; 500b5830294Sshaneh } 501b5830294Sshaneh 502d50deeebSshaneh static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){ 503d50deeebSshaneh return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d); 504fffadc52Sdan } 505d50deeebSshaneh static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){ 506d50deeebSshaneh return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d); 507fffadc52Sdan } 508fffadc52Sdan static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){ 509fffadc52Sdan return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b); 510fffadc52Sdan } 511fffadc52Sdan static void multiplexDlError(sqlite3_vfs *a, int b, char *c){ 512fffadc52Sdan gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c); 513fffadc52Sdan } 514fffadc52Sdan static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){ 515fffadc52Sdan return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c); 516fffadc52Sdan } 517fffadc52Sdan static void multiplexDlClose(sqlite3_vfs *a, void *b){ 518fffadc52Sdan gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b); 519fffadc52Sdan } 520fffadc52Sdan static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){ 521fffadc52Sdan return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c); 522fffadc52Sdan } 523fffadc52Sdan static int multiplexSleep(sqlite3_vfs *a, int b){ 524fffadc52Sdan return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b); 525fffadc52Sdan } 526fffadc52Sdan static int multiplexCurrentTime(sqlite3_vfs *a, double *b){ 527fffadc52Sdan return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b); 528fffadc52Sdan } 529fffadc52Sdan static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){ 530fffadc52Sdan return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c); 531fffadc52Sdan } 532fffadc52Sdan static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){ 533fffadc52Sdan return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b); 534fffadc52Sdan } 535fffadc52Sdan 5368a922f75Sshaneh /************************ I/O Method Wrappers *******************************/ 5378a922f75Sshaneh 5388a922f75Sshaneh /* xClose requests get passed through to the original VFS. 5398a922f75Sshaneh ** We loop over all open chunk handles and close them. 5408a922f75Sshaneh ** The group structure for this file is unlinked from 5418a922f75Sshaneh ** our list of groups and freed. 5428a922f75Sshaneh */ 5438a922f75Sshaneh static int multiplexClose(sqlite3_file *pConn){ 5448a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5458a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 5468a922f75Sshaneh int rc = SQLITE_OK; 5478a922f75Sshaneh int i; 5488a922f75Sshaneh multiplexEnter(); 549b5830294Sshaneh /* close any open handles */ 550d50deeebSshaneh for(i=0; i<pGroup->nMaxChunks; i++){ 5518a922f75Sshaneh if( pGroup->bOpen[i] ){ 5528a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 5538a922f75Sshaneh int rc2 = pSubOpen->pMethods->xClose(pSubOpen); 5548a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 5558a922f75Sshaneh pGroup->bOpen[i] = 0; 5568a922f75Sshaneh } 5578a922f75Sshaneh } 558b5830294Sshaneh /* remove from linked list */ 5598a922f75Sshaneh if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; 5608a922f75Sshaneh if( pGroup->pPrev ){ 5618a922f75Sshaneh pGroup->pPrev->pNext = pGroup->pNext; 5628a922f75Sshaneh }else{ 5638a922f75Sshaneh gMultiplex.pGroups = pGroup->pNext; 5648a922f75Sshaneh } 5658a922f75Sshaneh sqlite3_free(pGroup); 5668a922f75Sshaneh multiplexLeave(); 5678a922f75Sshaneh return rc; 5688a922f75Sshaneh } 5698a922f75Sshaneh 5708a922f75Sshaneh /* Pass xRead requests thru to the original VFS after 5718a922f75Sshaneh ** determining the correct chunk to operate on. 572fd1552f2Sshaneh ** Break up reads across chunk boundaries. 5738a922f75Sshaneh */ 5748a922f75Sshaneh static int multiplexRead( 5758a922f75Sshaneh sqlite3_file *pConn, 5768a922f75Sshaneh void *pBuf, 5778a922f75Sshaneh int iAmt, 5788a922f75Sshaneh sqlite3_int64 iOfst 5798a922f75Sshaneh ){ 5808a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 581d50deeebSshaneh multiplexGroup *pGroup = p->pGroup; 5828a922f75Sshaneh int rc = SQLITE_OK; 5838a922f75Sshaneh multiplexEnter(); 584c27fa4b0Sshaneh if( !pGroup->bEnabled ){ 585c27fa4b0Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 586c27fa4b0Sshaneh rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); 587c27fa4b0Sshaneh }else{ 5888a922f75Sshaneh while( iAmt > 0 ){ 589d50deeebSshaneh int i = (int)(iOfst / pGroup->nChunkSize); 5908a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 5918a922f75Sshaneh if( pSubOpen ){ 592d50deeebSshaneh int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; 5938a922f75Sshaneh if( extra<0 ) extra = 0; 5948a922f75Sshaneh iAmt -= extra; 595d50deeebSshaneh rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); 5968a922f75Sshaneh if( rc!=SQLITE_OK ) break; 5978a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 5988a922f75Sshaneh iOfst += iAmt; 5998a922f75Sshaneh iAmt = extra; 6008a922f75Sshaneh }else{ 6018a922f75Sshaneh rc = SQLITE_IOERR_READ; 6028a922f75Sshaneh break; 6038a922f75Sshaneh } 6048a922f75Sshaneh } 605c27fa4b0Sshaneh } 6068a922f75Sshaneh multiplexLeave(); 6078a922f75Sshaneh return rc; 6088a922f75Sshaneh } 6098a922f75Sshaneh 6108a922f75Sshaneh /* Pass xWrite requests thru to the original VFS after 6118a922f75Sshaneh ** determining the correct chunk to operate on. 612fd1552f2Sshaneh ** Break up writes across chunk boundaries. 6138a922f75Sshaneh */ 6148a922f75Sshaneh static int multiplexWrite( 6158a922f75Sshaneh sqlite3_file *pConn, 6168a922f75Sshaneh const void *pBuf, 6178a922f75Sshaneh int iAmt, 6188a922f75Sshaneh sqlite3_int64 iOfst 6198a922f75Sshaneh ){ 6208a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 621d50deeebSshaneh multiplexGroup *pGroup = p->pGroup; 6228a922f75Sshaneh int rc = SQLITE_OK; 6238a922f75Sshaneh multiplexEnter(); 624c27fa4b0Sshaneh if( !pGroup->bEnabled ){ 625c27fa4b0Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 626c27fa4b0Sshaneh rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); 627c27fa4b0Sshaneh }else{ 6288a922f75Sshaneh while( iAmt > 0 ){ 629d50deeebSshaneh int i = (int)(iOfst / pGroup->nChunkSize); 6308a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 6318a922f75Sshaneh if( pSubOpen ){ 632d50deeebSshaneh int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; 6338a922f75Sshaneh if( extra<0 ) extra = 0; 6348a922f75Sshaneh iAmt -= extra; 635d50deeebSshaneh rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); 6368a922f75Sshaneh if( rc!=SQLITE_OK ) break; 6378a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 6388a922f75Sshaneh iOfst += iAmt; 6398a922f75Sshaneh iAmt = extra; 6408a922f75Sshaneh }else{ 6418a922f75Sshaneh rc = SQLITE_IOERR_WRITE; 6428a922f75Sshaneh break; 6438a922f75Sshaneh } 6448a922f75Sshaneh } 645c27fa4b0Sshaneh } 6468a922f75Sshaneh multiplexLeave(); 6478a922f75Sshaneh return rc; 6488a922f75Sshaneh } 6498a922f75Sshaneh 6508a922f75Sshaneh /* Pass xTruncate requests thru to the original VFS after 6518a922f75Sshaneh ** determining the correct chunk to operate on. Delete any 6528a922f75Sshaneh ** chunks above the truncate mark. 6538a922f75Sshaneh */ 6548a922f75Sshaneh static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ 6558a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 6568a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 6578a922f75Sshaneh int rc = SQLITE_OK; 658c27fa4b0Sshaneh multiplexEnter(); 659c27fa4b0Sshaneh if( !pGroup->bEnabled ){ 660c27fa4b0Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 661c27fa4b0Sshaneh rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size); 662c27fa4b0Sshaneh }else{ 6638a922f75Sshaneh int rc2; 6648a922f75Sshaneh int i; 6658a922f75Sshaneh sqlite3_file *pSubOpen; 6668a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 667050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 6688a922f75Sshaneh /* delete the chunks above the truncate limit */ 669d50deeebSshaneh for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){ 670fd1552f2Sshaneh /* close any open chunks before deleting them */ 6718a922f75Sshaneh if( pGroup->bOpen[i] ){ 6728a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 6738a922f75Sshaneh rc2 = pSubOpen->pMethods->xClose(pSubOpen); 6748a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 6750596beecSshaneh pGroup->bOpen[i] = 0; 6768a922f75Sshaneh } 677050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 6783801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 6793801b65dSshaneh gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, 6803801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 681050d09abSshaneh #else 6823801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 6833801b65dSshaneh gMultiplex.zName+pGroup->nName, 6843801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 685050d09abSshaneh #endif 686d50deeebSshaneh rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); 6878a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 6888a922f75Sshaneh } 689d50deeebSshaneh pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL); 6908a922f75Sshaneh if( pSubOpen ){ 691d50deeebSshaneh rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize); 6928a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 6938a922f75Sshaneh }else{ 6948a922f75Sshaneh rc = SQLITE_IOERR_TRUNCATE; 6958a922f75Sshaneh } 696c27fa4b0Sshaneh } 6978a922f75Sshaneh multiplexLeave(); 6988a922f75Sshaneh return rc; 6998a922f75Sshaneh } 7008a922f75Sshaneh 7018a922f75Sshaneh /* Pass xSync requests through to the original VFS without change 7028a922f75Sshaneh */ 7038a922f75Sshaneh static int multiplexSync(sqlite3_file *pConn, int flags){ 7048a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 7058a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 7068a922f75Sshaneh int rc = SQLITE_OK; 7078a922f75Sshaneh int i; 7088a922f75Sshaneh multiplexEnter(); 709d50deeebSshaneh for(i=0; i<pGroup->nMaxChunks; i++){ 7108a922f75Sshaneh /* if we don't have it open, we don't need to sync it */ 7118a922f75Sshaneh if( pGroup->bOpen[i] ){ 7128a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 7138a922f75Sshaneh int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); 7148a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 7158a922f75Sshaneh } 7168a922f75Sshaneh } 7178a922f75Sshaneh multiplexLeave(); 7188a922f75Sshaneh return rc; 7198a922f75Sshaneh } 7208a922f75Sshaneh 7218a922f75Sshaneh /* Pass xFileSize requests through to the original VFS. 7228a922f75Sshaneh ** Aggregate the size of all the chunks before returning. 7238a922f75Sshaneh */ 7248a922f75Sshaneh static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ 7258a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 7268a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 7278a922f75Sshaneh int rc = SQLITE_OK; 7288a922f75Sshaneh int rc2; 7298a922f75Sshaneh int i; 7308a922f75Sshaneh multiplexEnter(); 731c27fa4b0Sshaneh if( !pGroup->bEnabled ){ 732c27fa4b0Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 733c27fa4b0Sshaneh rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize); 734c27fa4b0Sshaneh }else{ 7358a922f75Sshaneh *pSize = 0; 736d50deeebSshaneh for(i=0; i<pGroup->nMaxChunks; i++){ 7378a922f75Sshaneh sqlite3_file *pSubOpen = NULL; 7388a922f75Sshaneh /* if not opened already, check to see if the chunk exists */ 7398a922f75Sshaneh if( pGroup->bOpen[i] ){ 7408a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 7418a922f75Sshaneh }else{ 7428a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 7438a922f75Sshaneh int exists = 0; 744050d09abSshaneh memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); 745050d09abSshaneh if( i ){ 746050d09abSshaneh #ifdef SQLITE_MULTIPLEX_EXT_OVWR 7473801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 7483801b65dSshaneh gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, 7493801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 750050d09abSshaneh #else 7513801b65dSshaneh sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, 7523801b65dSshaneh gMultiplex.zName+pGroup->nName, 7533801b65dSshaneh SQLITE_MULTIPLEX_EXT_FMT, i); 754050d09abSshaneh #endif 755050d09abSshaneh } 7563801b65dSshaneh rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, 7573801b65dSshaneh SQLITE_ACCESS_EXISTS, &exists); 7588a922f75Sshaneh if( rc2==SQLITE_OK && exists){ 7598a922f75Sshaneh /* if it exists, open it */ 7608a922f75Sshaneh pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 7618a922f75Sshaneh }else{ 7628a922f75Sshaneh /* stop at first "gap" */ 7638a922f75Sshaneh break; 7648a922f75Sshaneh } 7658a922f75Sshaneh } 7668a922f75Sshaneh if( pSubOpen ){ 767fd1552f2Sshaneh sqlite3_int64 sz; 7688a922f75Sshaneh rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); 7698a922f75Sshaneh if( rc2!=SQLITE_OK ){ 7708a922f75Sshaneh rc = rc2; 7718a922f75Sshaneh }else{ 772d50deeebSshaneh if( sz>pGroup->nChunkSize ){ 7738fcfc344Sshaneh rc = SQLITE_IOERR_FSTAT; 7748fcfc344Sshaneh } 7758a922f75Sshaneh *pSize += sz; 7768a922f75Sshaneh } 7778a922f75Sshaneh }else{ 7788a922f75Sshaneh break; 7798a922f75Sshaneh } 7808a922f75Sshaneh } 781c27fa4b0Sshaneh } 7828a922f75Sshaneh multiplexLeave(); 7838a922f75Sshaneh return rc; 7848a922f75Sshaneh } 7858a922f75Sshaneh 7868a922f75Sshaneh /* Pass xLock requests through to the original VFS unchanged. 7878a922f75Sshaneh */ 7888a922f75Sshaneh static int multiplexLock(sqlite3_file *pConn, int lock){ 7898a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 7908a922f75Sshaneh int rc; 7918a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 7928a922f75Sshaneh if( pSubOpen ){ 7938a922f75Sshaneh return pSubOpen->pMethods->xLock(pSubOpen, lock); 7948a922f75Sshaneh } 7958a922f75Sshaneh return SQLITE_BUSY; 7968a922f75Sshaneh } 7978a922f75Sshaneh 7988a922f75Sshaneh /* Pass xUnlock requests through to the original VFS unchanged. 7998a922f75Sshaneh */ 8008a922f75Sshaneh static int multiplexUnlock(sqlite3_file *pConn, int lock){ 8018a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 8028a922f75Sshaneh int rc; 8038a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 8048a922f75Sshaneh if( pSubOpen ){ 8058a922f75Sshaneh return pSubOpen->pMethods->xUnlock(pSubOpen, lock); 8068a922f75Sshaneh } 8078a922f75Sshaneh return SQLITE_IOERR_UNLOCK; 8088a922f75Sshaneh } 8098a922f75Sshaneh 8108a922f75Sshaneh /* Pass xCheckReservedLock requests through to the original VFS unchanged. 8118a922f75Sshaneh */ 8128a922f75Sshaneh static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ 8138a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 8148a922f75Sshaneh int rc; 8158a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 8168a922f75Sshaneh if( pSubOpen ){ 8178a922f75Sshaneh return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); 8188a922f75Sshaneh } 8198a922f75Sshaneh return SQLITE_IOERR_CHECKRESERVEDLOCK; 8208a922f75Sshaneh } 8218a922f75Sshaneh 822c27fa4b0Sshaneh /* Pass xFileControl requests through to the original VFS unchanged, 823c27fa4b0Sshaneh ** except for any MULTIPLEX_CTRL_* requests here. 8248a922f75Sshaneh */ 8258a922f75Sshaneh static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ 8268a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 827d50deeebSshaneh multiplexGroup *pGroup = p->pGroup; 828d50deeebSshaneh int rc = SQLITE_ERROR; 8298a922f75Sshaneh sqlite3_file *pSubOpen; 830d50deeebSshaneh 831d50deeebSshaneh if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; 832d50deeebSshaneh switch( op ){ 833d50deeebSshaneh case MULTIPLEX_CTRL_ENABLE: 834d50deeebSshaneh if( pArg ) { 835d50deeebSshaneh int bEnabled = *(int *)pArg; 836d50deeebSshaneh pGroup->bEnabled = bEnabled; 837d50deeebSshaneh rc = SQLITE_OK; 838d50deeebSshaneh } 839d50deeebSshaneh break; 840d50deeebSshaneh case MULTIPLEX_CTRL_SET_CHUNK_SIZE: 841d50deeebSshaneh if( pArg ) { 842d50deeebSshaneh int nChunkSize = *(int *)pArg; 84378c4de4cSshaneh if( nChunkSize<1 ){ 844d50deeebSshaneh rc = SQLITE_MISUSE; 845d50deeebSshaneh }else{ 8463801b65dSshaneh /* Round up to nearest multiple of MAX_PAGE_SIZE. */ 8473801b65dSshaneh nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1)); 8483801b65dSshaneh nChunkSize &= ~(MAX_PAGE_SIZE-1); 849d50deeebSshaneh pGroup->nChunkSize = nChunkSize; 850d50deeebSshaneh rc = SQLITE_OK; 851d50deeebSshaneh } 852d50deeebSshaneh } 853d50deeebSshaneh break; 854d50deeebSshaneh case MULTIPLEX_CTRL_SET_MAX_CHUNKS: 855d50deeebSshaneh if( pArg ) { 856d50deeebSshaneh int nMaxChunks = *(int *)pArg; 85778c4de4cSshaneh if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){ 858d50deeebSshaneh rc = SQLITE_MISUSE; 859d50deeebSshaneh }else{ 860d50deeebSshaneh pGroup->nMaxChunks = nMaxChunks; 861d50deeebSshaneh rc = SQLITE_OK; 862d50deeebSshaneh } 863d50deeebSshaneh } 864d50deeebSshaneh break; 865ac039688Sshaneh case SQLITE_FCNTL_SIZE_HINT: 866ac039688Sshaneh case SQLITE_FCNTL_CHUNK_SIZE: 867ac039688Sshaneh /* no-op these */ 868ac039688Sshaneh rc = SQLITE_OK; 869ac039688Sshaneh break; 870d50deeebSshaneh default: 8718a922f75Sshaneh pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 8728a922f75Sshaneh if( pSubOpen ){ 873d50deeebSshaneh rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); 8748a922f75Sshaneh } 875d50deeebSshaneh break; 876d50deeebSshaneh } 877d50deeebSshaneh return rc; 8788a922f75Sshaneh } 8798a922f75Sshaneh 8808a922f75Sshaneh /* Pass xSectorSize requests through to the original VFS unchanged. 8818a922f75Sshaneh */ 8828a922f75Sshaneh static int multiplexSectorSize(sqlite3_file *pConn){ 8838a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 8848a922f75Sshaneh int rc; 8858a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 8868a922f75Sshaneh if( pSubOpen ){ 8878a922f75Sshaneh return pSubOpen->pMethods->xSectorSize(pSubOpen); 8888a922f75Sshaneh } 8893801b65dSshaneh return DEFAULT_SECTOR_SIZE; 8908a922f75Sshaneh } 8918a922f75Sshaneh 8928a922f75Sshaneh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. 8938a922f75Sshaneh */ 8948a922f75Sshaneh static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ 8958a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 8968a922f75Sshaneh int rc; 8978a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 8988a922f75Sshaneh if( pSubOpen ){ 8998a922f75Sshaneh return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); 9008a922f75Sshaneh } 9018a922f75Sshaneh return 0; 9028a922f75Sshaneh } 9038a922f75Sshaneh 9048a922f75Sshaneh /* Pass xShmMap requests through to the original VFS unchanged. 9058a922f75Sshaneh */ 9068a922f75Sshaneh static int multiplexShmMap( 9078a922f75Sshaneh sqlite3_file *pConn, /* Handle open on database file */ 9088a922f75Sshaneh int iRegion, /* Region to retrieve */ 9098a922f75Sshaneh int szRegion, /* Size of regions */ 9108a922f75Sshaneh int bExtend, /* True to extend file if necessary */ 9118a922f75Sshaneh void volatile **pp /* OUT: Mapped memory */ 9128a922f75Sshaneh ){ 9138a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 9148a922f75Sshaneh int rc; 9158a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 9168a922f75Sshaneh if( pSubOpen ){ 9178a922f75Sshaneh return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); 9188a922f75Sshaneh } 9198a922f75Sshaneh return SQLITE_IOERR; 9208a922f75Sshaneh } 9218a922f75Sshaneh 9228a922f75Sshaneh /* Pass xShmLock requests through to the original VFS unchanged. 9238a922f75Sshaneh */ 9248a922f75Sshaneh static int multiplexShmLock( 9258a922f75Sshaneh sqlite3_file *pConn, /* Database file holding the shared memory */ 9268a922f75Sshaneh int ofst, /* First lock to acquire or release */ 9278a922f75Sshaneh int n, /* Number of locks to acquire or release */ 9288a922f75Sshaneh int flags /* What to do with the lock */ 9298a922f75Sshaneh ){ 9308a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 9318a922f75Sshaneh int rc; 9328a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 9338a922f75Sshaneh if( pSubOpen ){ 9348a922f75Sshaneh return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); 9358a922f75Sshaneh } 9368a922f75Sshaneh return SQLITE_BUSY; 9378a922f75Sshaneh } 9388a922f75Sshaneh 9398a922f75Sshaneh /* Pass xShmBarrier requests through to the original VFS unchanged. 9408a922f75Sshaneh */ 9418a922f75Sshaneh static void multiplexShmBarrier(sqlite3_file *pConn){ 9428a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 9438a922f75Sshaneh int rc; 9448a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 9458a922f75Sshaneh if( pSubOpen ){ 9468a922f75Sshaneh pSubOpen->pMethods->xShmBarrier(pSubOpen); 9478a922f75Sshaneh } 9488a922f75Sshaneh } 9498a922f75Sshaneh 9508a922f75Sshaneh /* Pass xShmUnmap requests through to the original VFS unchanged. 9518a922f75Sshaneh */ 9528a922f75Sshaneh static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ 9538a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 9548a922f75Sshaneh int rc; 9558a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 9568a922f75Sshaneh if( pSubOpen ){ 9578a922f75Sshaneh return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); 9588a922f75Sshaneh } 9598a922f75Sshaneh return SQLITE_OK; 9608a922f75Sshaneh } 9618a922f75Sshaneh 9628a922f75Sshaneh /************************** Public Interfaces *****************************/ 9638a922f75Sshaneh /* 96478c4de4cSshaneh ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() 96578c4de4cSshaneh ** 96678c4de4cSshaneh ** Use the VFS named zOrigVfsName as the VFS that does the actual work. 96778c4de4cSshaneh ** Use the default if zOrigVfsName==NULL. 9688a922f75Sshaneh ** 9698a922f75Sshaneh ** The multiplex VFS shim is named "multiplex". It will become the default 9708a922f75Sshaneh ** VFS if makeDefault is non-zero. 9718a922f75Sshaneh ** 9728a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once 9738a922f75Sshaneh ** during start-up. 9748a922f75Sshaneh */ 9758a922f75Sshaneh int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ 9768a922f75Sshaneh sqlite3_vfs *pOrigVfs; 9778a922f75Sshaneh if( gMultiplex.isInitialized ) return SQLITE_MISUSE; 9788a922f75Sshaneh pOrigVfs = sqlite3_vfs_find(zOrigVfsName); 9798a922f75Sshaneh if( pOrigVfs==0 ) return SQLITE_ERROR; 9808a922f75Sshaneh assert( pOrigVfs!=&gMultiplex.sThisVfs ); 9818a922f75Sshaneh gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 9828a922f75Sshaneh if( !gMultiplex.pMutex ){ 9838a922f75Sshaneh return SQLITE_NOMEM; 9848a922f75Sshaneh } 985b5830294Sshaneh gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); 986b5830294Sshaneh if( !gMultiplex.zName ){ 987b5830294Sshaneh sqlite3_mutex_free(gMultiplex.pMutex); 988b5830294Sshaneh return SQLITE_NOMEM; 989b5830294Sshaneh } 9908a922f75Sshaneh gMultiplex.pGroups = NULL; 9918a922f75Sshaneh gMultiplex.isInitialized = 1; 9928a922f75Sshaneh gMultiplex.pOrigVfs = pOrigVfs; 9938a922f75Sshaneh gMultiplex.sThisVfs = *pOrigVfs; 9948a922f75Sshaneh gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); 995d9523b97Sshaneh gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; 9968a922f75Sshaneh gMultiplex.sThisVfs.xOpen = multiplexOpen; 997b5830294Sshaneh gMultiplex.sThisVfs.xDelete = multiplexDelete; 998fffadc52Sdan gMultiplex.sThisVfs.xAccess = multiplexAccess; 999fffadc52Sdan gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; 1000fffadc52Sdan gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; 1001fffadc52Sdan gMultiplex.sThisVfs.xDlError = multiplexDlError; 1002fffadc52Sdan gMultiplex.sThisVfs.xDlSym = multiplexDlSym; 1003fffadc52Sdan gMultiplex.sThisVfs.xDlClose = multiplexDlClose; 1004fffadc52Sdan gMultiplex.sThisVfs.xRandomness = multiplexRandomness; 1005fffadc52Sdan gMultiplex.sThisVfs.xSleep = multiplexSleep; 1006fffadc52Sdan gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime; 1007fffadc52Sdan gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError; 1008fffadc52Sdan gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64; 1009fffadc52Sdan 10108a922f75Sshaneh gMultiplex.sIoMethodsV1.iVersion = 1; 10118a922f75Sshaneh gMultiplex.sIoMethodsV1.xClose = multiplexClose; 10128a922f75Sshaneh gMultiplex.sIoMethodsV1.xRead = multiplexRead; 10138a922f75Sshaneh gMultiplex.sIoMethodsV1.xWrite = multiplexWrite; 10148a922f75Sshaneh gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate; 10158a922f75Sshaneh gMultiplex.sIoMethodsV1.xSync = multiplexSync; 10168a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize; 10178a922f75Sshaneh gMultiplex.sIoMethodsV1.xLock = multiplexLock; 10188a922f75Sshaneh gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock; 10198a922f75Sshaneh gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock; 10208a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl; 10218a922f75Sshaneh gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize; 10228a922f75Sshaneh gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics; 10238a922f75Sshaneh gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; 10248a922f75Sshaneh gMultiplex.sIoMethodsV2.iVersion = 2; 10258a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; 10268a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; 10278a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; 10288a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; 10298a922f75Sshaneh sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); 1030d50deeebSshaneh 1031d50deeebSshaneh sqlite3_auto_extension((void*)multiplexFuncInit); 1032d50deeebSshaneh 10338a922f75Sshaneh return SQLITE_OK; 10348a922f75Sshaneh } 10358a922f75Sshaneh 10368a922f75Sshaneh /* 103778c4de4cSshaneh ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() 10388a922f75Sshaneh ** 10398a922f75Sshaneh ** All SQLite database connections must be closed before calling this 10408a922f75Sshaneh ** routine. 10418a922f75Sshaneh ** 10428a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while 10438a922f75Sshaneh ** shutting down in order to free all remaining multiplex groups. 10448a922f75Sshaneh */ 10458a922f75Sshaneh int sqlite3_multiplex_shutdown(void){ 10468a922f75Sshaneh if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; 10478a922f75Sshaneh if( gMultiplex.pGroups ) return SQLITE_MISUSE; 10488a922f75Sshaneh gMultiplex.isInitialized = 0; 1049b5830294Sshaneh sqlite3_free(gMultiplex.zName); 10508a922f75Sshaneh sqlite3_mutex_free(gMultiplex.pMutex); 10518a922f75Sshaneh sqlite3_vfs_unregister(&gMultiplex.sThisVfs); 10528a922f75Sshaneh memset(&gMultiplex, 0, sizeof(gMultiplex)); 10538a922f75Sshaneh return SQLITE_OK; 10548a922f75Sshaneh } 10558a922f75Sshaneh 10568a922f75Sshaneh /***************************** Test Code ***********************************/ 10578a922f75Sshaneh #ifdef SQLITE_TEST 10588a922f75Sshaneh #include <tcl.h> 10593801b65dSshaneh extern const char *sqlite3TestErrorName(int); 10608a922f75Sshaneh 10618a922f75Sshaneh 10628a922f75Sshaneh /* 10638a922f75Sshaneh ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT 10648a922f75Sshaneh */ 10658a922f75Sshaneh static int test_multiplex_initialize( 10668a922f75Sshaneh void * clientData, 10678a922f75Sshaneh Tcl_Interp *interp, 10688a922f75Sshaneh int objc, 10698a922f75Sshaneh Tcl_Obj *CONST objv[] 10708a922f75Sshaneh ){ 10718a922f75Sshaneh const char *zName; /* Name of new multiplex VFS */ 10728a922f75Sshaneh int makeDefault; /* True to make the new VFS the default */ 10738a922f75Sshaneh int rc; /* Value returned by multiplex_initialize() */ 10748a922f75Sshaneh 10758a922f75Sshaneh UNUSED_PARAMETER(clientData); 10768a922f75Sshaneh 10778a922f75Sshaneh /* Process arguments */ 10788a922f75Sshaneh if( objc!=3 ){ 10798a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); 10808a922f75Sshaneh return TCL_ERROR; 10818a922f75Sshaneh } 10828a922f75Sshaneh zName = Tcl_GetString(objv[1]); 10838a922f75Sshaneh if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; 10848a922f75Sshaneh if( zName[0]=='\0' ) zName = 0; 10858a922f75Sshaneh 10868a922f75Sshaneh /* Call sqlite3_multiplex_initialize() */ 10878a922f75Sshaneh rc = sqlite3_multiplex_initialize(zName, makeDefault); 10888a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 10898a922f75Sshaneh 10908a922f75Sshaneh return TCL_OK; 10918a922f75Sshaneh } 10928a922f75Sshaneh 10938a922f75Sshaneh /* 10948a922f75Sshaneh ** tclcmd: sqlite3_multiplex_shutdown 10958a922f75Sshaneh */ 10968a922f75Sshaneh static int test_multiplex_shutdown( 10978a922f75Sshaneh void * clientData, 10988a922f75Sshaneh Tcl_Interp *interp, 10998a922f75Sshaneh int objc, 11008a922f75Sshaneh Tcl_Obj *CONST objv[] 11018a922f75Sshaneh ){ 11028a922f75Sshaneh int rc; /* Value returned by multiplex_shutdown() */ 11038a922f75Sshaneh 11048a922f75Sshaneh UNUSED_PARAMETER(clientData); 11058a922f75Sshaneh 11068a922f75Sshaneh if( objc!=1 ){ 11078a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, ""); 11088a922f75Sshaneh return TCL_ERROR; 11098a922f75Sshaneh } 11108a922f75Sshaneh 11118a922f75Sshaneh /* Call sqlite3_multiplex_shutdown() */ 11128a922f75Sshaneh rc = sqlite3_multiplex_shutdown(); 11138a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 11148a922f75Sshaneh 11158a922f75Sshaneh return TCL_OK; 11168a922f75Sshaneh } 11178a922f75Sshaneh 11188a922f75Sshaneh /* 11198a922f75Sshaneh ** tclcmd: sqlite3_multiplex_dump 11208a922f75Sshaneh */ 11218a922f75Sshaneh static int test_multiplex_dump( 11228a922f75Sshaneh void * clientData, 11238a922f75Sshaneh Tcl_Interp *interp, 11248a922f75Sshaneh int objc, 11258a922f75Sshaneh Tcl_Obj *CONST objv[] 11268a922f75Sshaneh ){ 11278a922f75Sshaneh Tcl_Obj *pResult; 11288a922f75Sshaneh Tcl_Obj *pGroupTerm; 11298a922f75Sshaneh multiplexGroup *pGroup; 11308a922f75Sshaneh int i; 11318a922f75Sshaneh int nChunks = 0; 11328a922f75Sshaneh 11338a922f75Sshaneh UNUSED_PARAMETER(clientData); 11348a922f75Sshaneh UNUSED_PARAMETER(objc); 11358a922f75Sshaneh UNUSED_PARAMETER(objv); 11368a922f75Sshaneh 11378a922f75Sshaneh pResult = Tcl_NewObj(); 11388a922f75Sshaneh multiplexEnter(); 11398a922f75Sshaneh for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){ 11408a922f75Sshaneh pGroupTerm = Tcl_NewObj(); 11418a922f75Sshaneh 11428a922f75Sshaneh pGroup->zName[pGroup->nName] = '\0'; 11438a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 11448a922f75Sshaneh Tcl_NewStringObj(pGroup->zName, -1)); 11458a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 11468a922f75Sshaneh Tcl_NewIntObj(pGroup->nName)); 11478a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 11488a922f75Sshaneh Tcl_NewIntObj(pGroup->flags)); 11498a922f75Sshaneh 11508a922f75Sshaneh /* count number of chunks with open handles */ 1151d50deeebSshaneh for(i=0; i<pGroup->nMaxChunks; i++){ 11528a922f75Sshaneh if( pGroup->bOpen[i] ) nChunks++; 11538a922f75Sshaneh } 11548a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 11558a922f75Sshaneh Tcl_NewIntObj(nChunks)); 11568a922f75Sshaneh 11578a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 1158d50deeebSshaneh Tcl_NewIntObj(pGroup->nChunkSize)); 11598a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 1160d50deeebSshaneh Tcl_NewIntObj(pGroup->nMaxChunks)); 11618a922f75Sshaneh 11628a922f75Sshaneh Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); 11638a922f75Sshaneh } 11648a922f75Sshaneh multiplexLeave(); 11658a922f75Sshaneh Tcl_SetObjResult(interp, pResult); 11668a922f75Sshaneh return TCL_OK; 11678a922f75Sshaneh } 11688a922f75Sshaneh 11698a922f75Sshaneh /* 1170d50deeebSshaneh ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? 1171d50deeebSshaneh */ 1172d50deeebSshaneh static int test_multiplex_control( 1173d50deeebSshaneh ClientData cd, 1174d50deeebSshaneh Tcl_Interp *interp, 1175d50deeebSshaneh int objc, 1176d50deeebSshaneh Tcl_Obj *CONST objv[] 1177d50deeebSshaneh ){ 1178d50deeebSshaneh int rc; /* Return code from file_control() */ 1179d50deeebSshaneh int idx; /* Index in aSub[] */ 1180d50deeebSshaneh Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ 1181d50deeebSshaneh sqlite3 *db; /* Underlying db handle for HANDLE */ 1182d50deeebSshaneh int iValue = 0; 1183d50deeebSshaneh void *pArg = 0; 1184d50deeebSshaneh 1185d50deeebSshaneh struct SubCommand { 1186d50deeebSshaneh const char *zName; 1187d50deeebSshaneh int op; 1188d50deeebSshaneh int argtype; 1189d50deeebSshaneh } aSub[] = { 1190d50deeebSshaneh { "enable", MULTIPLEX_CTRL_ENABLE, 1 }, 1191d50deeebSshaneh { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 }, 1192d50deeebSshaneh { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 }, 1193d50deeebSshaneh { 0, 0, 0 } 1194d50deeebSshaneh }; 1195d50deeebSshaneh 1196c27fa4b0Sshaneh if( objc!=5 ){ 1197c27fa4b0Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE"); 1198d50deeebSshaneh return TCL_ERROR; 1199d50deeebSshaneh } 1200d50deeebSshaneh 1201d50deeebSshaneh if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ 1202d50deeebSshaneh Tcl_AppendResult(interp, "expected database handle, got \"", 0); 1203d50deeebSshaneh Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0); 1204d50deeebSshaneh return TCL_ERROR; 1205d50deeebSshaneh }else{ 1206d50deeebSshaneh db = *(sqlite3 **)cmdInfo.objClientData; 1207d50deeebSshaneh } 1208d50deeebSshaneh 1209d50deeebSshaneh rc = Tcl_GetIndexFromObjStruct( 1210d50deeebSshaneh interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx 1211d50deeebSshaneh ); 1212d50deeebSshaneh if( rc!=TCL_OK ) return rc; 1213d50deeebSshaneh 1214d50deeebSshaneh switch( aSub[idx].argtype ){ 1215d50deeebSshaneh case 1: 1216d50deeebSshaneh if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){ 1217d50deeebSshaneh return TCL_ERROR; 1218d50deeebSshaneh } 1219d50deeebSshaneh pArg = (void *)&iValue; 1220d50deeebSshaneh break; 1221d50deeebSshaneh default: 1222d50deeebSshaneh Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND"); 1223d50deeebSshaneh return TCL_ERROR; 1224d50deeebSshaneh } 1225d50deeebSshaneh 1226d50deeebSshaneh rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg); 1227d50deeebSshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 1228d50deeebSshaneh return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR; 1229d50deeebSshaneh } 1230d50deeebSshaneh 1231d50deeebSshaneh /* 12328a922f75Sshaneh ** This routine registers the custom TCL commands defined in this 12338a922f75Sshaneh ** module. This should be the only procedure visible from outside 12348a922f75Sshaneh ** of this module. 12358a922f75Sshaneh */ 12368a922f75Sshaneh int Sqlitemultiplex_Init(Tcl_Interp *interp){ 12378a922f75Sshaneh static struct { 12388a922f75Sshaneh char *zName; 12398a922f75Sshaneh Tcl_ObjCmdProc *xProc; 12408a922f75Sshaneh } aCmd[] = { 12418a922f75Sshaneh { "sqlite3_multiplex_initialize", test_multiplex_initialize }, 12428a922f75Sshaneh { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, 12438a922f75Sshaneh { "sqlite3_multiplex_dump", test_multiplex_dump }, 1244d50deeebSshaneh { "sqlite3_multiplex_control", test_multiplex_control }, 12458a922f75Sshaneh }; 12468a922f75Sshaneh int i; 12478a922f75Sshaneh 12488a922f75Sshaneh for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 12498a922f75Sshaneh Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 12508a922f75Sshaneh } 12518a922f75Sshaneh 12528a922f75Sshaneh return TCL_OK; 12538a922f75Sshaneh } 12548a922f75Sshaneh #endif 1255