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> 258a922f75Sshaneh #include "sqliteInt.h" 268a922f75Sshaneh 278a922f75Sshaneh /************************ Shim Definitions ******************************/ 288a922f75Sshaneh 298a922f75Sshaneh #define SQLITE_MULTIPLEX_CHUNK_SIZE 0x80000000 308a922f75Sshaneh #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 318a922f75Sshaneh 328a922f75Sshaneh /************************ Object Definitions ******************************/ 338a922f75Sshaneh 348a922f75Sshaneh /* Forward declaration of all object types */ 358a922f75Sshaneh typedef struct multiplexGroup multiplexGroup; 368a922f75Sshaneh typedef struct multiplexConn multiplexConn; 378a922f75Sshaneh 388a922f75Sshaneh /* 398a922f75Sshaneh ** A "multiplex group" is a collection of files that collectively 408a922f75Sshaneh ** makeup a single SQLite DB file. This allows the size of the DB 418a922f75Sshaneh ** to exceed the limits imposed by the file system. 428a922f75Sshaneh ** 438a922f75Sshaneh ** There is an instance of the following object for each defined multiplex 448a922f75Sshaneh ** group. 458a922f75Sshaneh */ 468a922f75Sshaneh struct multiplexGroup { 478a922f75Sshaneh sqlite3_file *pReal[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* Handles to each chunk */ 488a922f75Sshaneh char bOpen[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* 0 if chunk not opened */ 498a922f75Sshaneh char *zName; /* Base filename of this group */ 508a922f75Sshaneh int nName; /* Length of base filename */ 518a922f75Sshaneh int flags; /* Flags used for original opening */ 528a922f75Sshaneh multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ 538a922f75Sshaneh }; 548a922f75Sshaneh 558a922f75Sshaneh /* 568a922f75Sshaneh ** An instance of the following object represents each open connection 578a922f75Sshaneh ** to a file that is multiplex'ed. This object is a 588a922f75Sshaneh ** subclass of sqlite3_file. The sqlite3_file object for the underlying 598a922f75Sshaneh ** VFS is appended to this structure. 608a922f75Sshaneh */ 618a922f75Sshaneh struct multiplexConn { 628a922f75Sshaneh sqlite3_file base; /* Base class - must be first */ 638a922f75Sshaneh multiplexGroup *pGroup; /* The underlying group of files */ 648a922f75Sshaneh }; 658a922f75Sshaneh 668a922f75Sshaneh /************************* Global Variables **********************************/ 678a922f75Sshaneh /* 688a922f75Sshaneh ** All global variables used by this file are containing within the following 698a922f75Sshaneh ** gMultiplex structure. 708a922f75Sshaneh */ 718a922f75Sshaneh static struct { 728a922f75Sshaneh /* The pOrigVfs is the real, original underlying VFS implementation. 738a922f75Sshaneh ** Most operations pass-through to the real VFS. This value is read-only 748a922f75Sshaneh ** during operation. It is only modified at start-time and thus does not 758a922f75Sshaneh ** require a mutex. 768a922f75Sshaneh */ 778a922f75Sshaneh sqlite3_vfs *pOrigVfs; 788a922f75Sshaneh 798a922f75Sshaneh /* The sThisVfs is the VFS structure used by this shim. It is initialized 808a922f75Sshaneh ** at start-time and thus does not require a mutex 818a922f75Sshaneh */ 828a922f75Sshaneh sqlite3_vfs sThisVfs; 838a922f75Sshaneh 848a922f75Sshaneh /* The sIoMethods defines the methods used by sqlite3_file objects 858a922f75Sshaneh ** associated with this shim. It is initialized at start-time and does 868a922f75Sshaneh ** not require a mutex. 878a922f75Sshaneh ** 888a922f75Sshaneh ** When the underlying VFS is called to open a file, it might return 898a922f75Sshaneh ** either a version 1 or a version 2 sqlite3_file object. This shim 908a922f75Sshaneh ** has to create a wrapper sqlite3_file of the same version. Hence 918a922f75Sshaneh ** there are two I/O method structures, one for version 1 and the other 928a922f75Sshaneh ** for version 2. 938a922f75Sshaneh */ 948a922f75Sshaneh sqlite3_io_methods sIoMethodsV1; 958a922f75Sshaneh sqlite3_io_methods sIoMethodsV2; 968a922f75Sshaneh 97fd1552f2Sshaneh /* True when this shim has been initialized. 988a922f75Sshaneh */ 998a922f75Sshaneh int isInitialized; 1008a922f75Sshaneh 1018a922f75Sshaneh /* For run-time access any of the other global data structures in this 1028a922f75Sshaneh ** shim, the following mutex must be held. 1038a922f75Sshaneh */ 1048a922f75Sshaneh sqlite3_mutex *pMutex; 1058a922f75Sshaneh 1068a922f75Sshaneh /* List of multiplexGroup objects. 1078a922f75Sshaneh */ 1088a922f75Sshaneh multiplexGroup *pGroups; 1098a922f75Sshaneh 110fd1552f2Sshaneh /* Chunk params. 1118a922f75Sshaneh */ 1128a922f75Sshaneh int nChunkSize; 1138a922f75Sshaneh int nMaxChunks; 1148a922f75Sshaneh 1158a922f75Sshaneh } gMultiplex; 1168a922f75Sshaneh 1178a922f75Sshaneh /************************* Utility Routines *********************************/ 1188a922f75Sshaneh /* 1198a922f75Sshaneh ** Acquire and release the mutex used to serialize access to the 1208a922f75Sshaneh ** list of multiplexGroups. 1218a922f75Sshaneh */ 1228a922f75Sshaneh static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } 1238a922f75Sshaneh static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } 1248a922f75Sshaneh 1258a922f75Sshaneh /* Translate an sqlite3_file* that is really a multiplexGroup* into 1268a922f75Sshaneh ** the sqlite3_file* for the underlying original VFS. 1278a922f75Sshaneh */ 1288a922f75Sshaneh static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ 1298a922f75Sshaneh multiplexGroup *pGroup = pConn->pGroup; 1308a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 1318a922f75Sshaneh if( iChunk<gMultiplex.nMaxChunks ){ 1328a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ 1338a922f75Sshaneh if( !pGroup->bOpen[iChunk] ){ 1348a922f75Sshaneh pGroup->zName[pGroup->nName] = '\0'; 1358a922f75Sshaneh if( iChunk ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", iChunk); 1368a922f75Sshaneh *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->zName, pSubOpen, pGroup->flags, pOutFlags); 1378a922f75Sshaneh if( *rc==SQLITE_OK ){ 1388a922f75Sshaneh pGroup->bOpen[iChunk] = -1; 1398a922f75Sshaneh return pSubOpen; 1408a922f75Sshaneh } 1418a922f75Sshaneh return NULL; 1428a922f75Sshaneh } 1438a922f75Sshaneh *rc = SQLITE_OK; 1448a922f75Sshaneh return pSubOpen; 1458a922f75Sshaneh } 1468a922f75Sshaneh *rc = SQLITE_ERROR; 1478a922f75Sshaneh return NULL; 1488a922f75Sshaneh } 1498a922f75Sshaneh 1508a922f75Sshaneh /************************* VFS Method Wrappers *****************************/ 1518a922f75Sshaneh /* 1528a922f75Sshaneh ** This is the xOpen method used for the "multiplex" VFS. 1538a922f75Sshaneh ** 1548a922f75Sshaneh ** Most of the work is done by the underlying original VFS. This method 1558a922f75Sshaneh ** simply links the new file into the appropriate multiplex group if it is a 1568a922f75Sshaneh ** file that needs to be tracked. 1578a922f75Sshaneh */ 1588a922f75Sshaneh static int multiplexOpen( 1598a922f75Sshaneh sqlite3_vfs *pVfs, /* The multiplex VFS */ 1608a922f75Sshaneh const char *zName, /* Name of file to be opened */ 1618a922f75Sshaneh sqlite3_file *pConn, /* Fill in this file descriptor */ 1628a922f75Sshaneh int flags, /* Flags to control the opening */ 1638a922f75Sshaneh int *pOutFlags /* Flags showing results of opening */ 1648a922f75Sshaneh ){ 1658a922f75Sshaneh int rc; /* Result code */ 1668a922f75Sshaneh multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ 1678a922f75Sshaneh multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ 1688a922f75Sshaneh sqlite3_file *pSubOpen; /* Real file descriptor */ 1698a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 1708a922f75Sshaneh int nName = sqlite3Strlen30(zName); 1718a922f75Sshaneh int i; 1728a922f75Sshaneh 1738a922f75Sshaneh UNUSED_PARAMETER(pVfs); 1748a922f75Sshaneh 1758a922f75Sshaneh /* If the file is not a main database file or a WAL, then use the 1768a922f75Sshaneh ** normal xOpen method. 1778a922f75Sshaneh */ 1788a922f75Sshaneh if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))==0 ){ 1798a922f75Sshaneh return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags); 1808a922f75Sshaneh } 1818a922f75Sshaneh 1828a922f75Sshaneh /* We need to create a group structure and manage 1838a922f75Sshaneh ** access to this group of files. 1848a922f75Sshaneh */ 1858a922f75Sshaneh multiplexEnter(); 1868a922f75Sshaneh pMultiplexOpen = (multiplexConn*)pConn; 187fd1552f2Sshaneh /* allocate space for group, file handles, 188fd1552f2Sshaneh ** and file name (+ extra for "-0000\0") 189fd1552f2Sshaneh */ 1908a922f75Sshaneh pGroup = sqlite3_malloc( sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6 ); 1918a922f75Sshaneh if( pGroup==0 ){ 1928a922f75Sshaneh rc=SQLITE_NOMEM; 1938a922f75Sshaneh }else{ 1948a922f75Sshaneh pMultiplexOpen->pGroup = pGroup; 1958a922f75Sshaneh memset(pGroup, 0, sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6); 196fd1552f2Sshaneh /* assign pointers to extra space for file handles */ 1978a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 1988a922f75Sshaneh pGroup->pReal[i] = (sqlite3_file *)((char *)&pGroup[1] + (pOrigVfs->szOsFile*i)); 1998a922f75Sshaneh } 2008a922f75Sshaneh pGroup->zName = (char *)&pGroup[1] + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks); 2018a922f75Sshaneh memcpy(pGroup->zName, zName, nName+1); 2028a922f75Sshaneh pGroup->nName = nName; 2038a922f75Sshaneh pGroup->flags = flags; 2048a922f75Sshaneh pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); 2058a922f75Sshaneh if( pSubOpen ){ 2068a922f75Sshaneh if( pSubOpen->pMethods->iVersion==1 ){ 2078a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; 2088a922f75Sshaneh }else{ 2098a922f75Sshaneh pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; 2108a922f75Sshaneh } 2118a922f75Sshaneh /* place this group at the head of our list */ 2128a922f75Sshaneh pGroup->pNext = gMultiplex.pGroups; 2138a922f75Sshaneh if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; 2148a922f75Sshaneh gMultiplex.pGroups = pGroup; 2158a922f75Sshaneh }else{ 2168a922f75Sshaneh sqlite3_free(pGroup); 2178a922f75Sshaneh } 2188a922f75Sshaneh } 2198a922f75Sshaneh multiplexLeave(); 2208a922f75Sshaneh return rc; 2218a922f75Sshaneh } 2228a922f75Sshaneh 2238a922f75Sshaneh /************************ I/O Method Wrappers *******************************/ 2248a922f75Sshaneh 2258a922f75Sshaneh /* xClose requests get passed through to the original VFS. 2268a922f75Sshaneh ** We loop over all open chunk handles and close them. 2278a922f75Sshaneh ** The group structure for this file is unlinked from 2288a922f75Sshaneh ** our list of groups and freed. 2298a922f75Sshaneh */ 2308a922f75Sshaneh static int multiplexClose(sqlite3_file *pConn){ 2318a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 2328a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 2338a922f75Sshaneh int rc = SQLITE_OK; 2348a922f75Sshaneh int i; 2358a922f75Sshaneh multiplexEnter(); 2368a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 2378a922f75Sshaneh if( pGroup->bOpen[i] ){ 2388a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 2398a922f75Sshaneh int rc2 = pSubOpen->pMethods->xClose(pSubOpen); 2408a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 2418a922f75Sshaneh pGroup->bOpen[i] = 0; 2428a922f75Sshaneh } 2438a922f75Sshaneh } 2448a922f75Sshaneh if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; 2458a922f75Sshaneh if( pGroup->pPrev ){ 2468a922f75Sshaneh pGroup->pPrev->pNext = pGroup->pNext; 2478a922f75Sshaneh }else{ 2488a922f75Sshaneh gMultiplex.pGroups = pGroup->pNext; 2498a922f75Sshaneh } 2508a922f75Sshaneh sqlite3_free(pGroup); 2518a922f75Sshaneh multiplexLeave(); 2528a922f75Sshaneh return rc; 2538a922f75Sshaneh } 2548a922f75Sshaneh 2558a922f75Sshaneh /* Pass xRead requests thru to the original VFS after 2568a922f75Sshaneh ** determining the correct chunk to operate on. 257fd1552f2Sshaneh ** Break up reads across chunk boundaries. 2588a922f75Sshaneh */ 2598a922f75Sshaneh static int multiplexRead( 2608a922f75Sshaneh sqlite3_file *pConn, 2618a922f75Sshaneh void *pBuf, 2628a922f75Sshaneh int iAmt, 2638a922f75Sshaneh sqlite3_int64 iOfst 2648a922f75Sshaneh ){ 2658a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 2668a922f75Sshaneh int rc = SQLITE_OK; 2678a922f75Sshaneh multiplexEnter(); 2688a922f75Sshaneh while( iAmt > 0 ){ 2698a922f75Sshaneh int i = (int)(iOfst/gMultiplex.nChunkSize); 2708a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 2718a922f75Sshaneh if( pSubOpen ){ 2728a922f75Sshaneh int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; 2738a922f75Sshaneh if( extra<0 ) extra = 0; 2748a922f75Sshaneh iAmt -= extra; 2758a922f75Sshaneh rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); 2768a922f75Sshaneh if( rc!=SQLITE_OK ) break; 2778a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 2788a922f75Sshaneh iOfst += iAmt; 2798a922f75Sshaneh iAmt = extra; 2808a922f75Sshaneh }else{ 2818a922f75Sshaneh rc = SQLITE_IOERR_READ; 2828a922f75Sshaneh break; 2838a922f75Sshaneh } 2848a922f75Sshaneh } 2858a922f75Sshaneh multiplexLeave(); 2868a922f75Sshaneh return rc; 2878a922f75Sshaneh } 2888a922f75Sshaneh 2898a922f75Sshaneh /* Pass xWrite requests thru to the original VFS after 2908a922f75Sshaneh ** determining the correct chunk to operate on. 291fd1552f2Sshaneh ** Break up writes across chunk boundaries. 2928a922f75Sshaneh */ 2938a922f75Sshaneh static int multiplexWrite( 2948a922f75Sshaneh sqlite3_file *pConn, 2958a922f75Sshaneh const void *pBuf, 2968a922f75Sshaneh int iAmt, 2978a922f75Sshaneh sqlite3_int64 iOfst 2988a922f75Sshaneh ){ 2998a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3008a922f75Sshaneh int rc = SQLITE_OK; 3018a922f75Sshaneh multiplexEnter(); 3028a922f75Sshaneh while( iAmt > 0 ){ 3038a922f75Sshaneh int i = (int)(iOfst/gMultiplex.nChunkSize); 3048a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 3058a922f75Sshaneh if( pSubOpen ){ 3068a922f75Sshaneh int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize; 3078a922f75Sshaneh if( extra<0 ) extra = 0; 3088a922f75Sshaneh iAmt -= extra; 3098a922f75Sshaneh rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize); 3108a922f75Sshaneh if( rc!=SQLITE_OK ) break; 3118a922f75Sshaneh pBuf = (char *)pBuf + iAmt; 3128a922f75Sshaneh iOfst += iAmt; 3138a922f75Sshaneh iAmt = extra; 3148a922f75Sshaneh }else{ 3158a922f75Sshaneh rc = SQLITE_IOERR_WRITE; 3168a922f75Sshaneh break; 3178a922f75Sshaneh } 3188a922f75Sshaneh } 3198a922f75Sshaneh multiplexLeave(); 3208a922f75Sshaneh return rc; 3218a922f75Sshaneh } 3228a922f75Sshaneh 3238a922f75Sshaneh /* Pass xTruncate requests thru to the original VFS after 3248a922f75Sshaneh ** determining the correct chunk to operate on. Delete any 3258a922f75Sshaneh ** chunks above the truncate mark. 3268a922f75Sshaneh */ 3278a922f75Sshaneh static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ 3288a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3298a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 3308a922f75Sshaneh int rc = SQLITE_OK; 3318a922f75Sshaneh int rc2; 3328a922f75Sshaneh int i; 3338a922f75Sshaneh sqlite3_file *pSubOpen; 3348a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 3358a922f75Sshaneh multiplexEnter(); 3368a922f75Sshaneh /* delete the chunks above the truncate limit */ 3378a922f75Sshaneh for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){ 338fd1552f2Sshaneh /* close any open chunks before deleting them */ 3398a922f75Sshaneh if( pGroup->bOpen[i] ){ 3408a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 3418a922f75Sshaneh rc2 = pSubOpen->pMethods->xClose(pSubOpen); 3428a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 3438a922f75Sshaneh } 344*e5a6ad6dSshaneh sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i); 3458a922f75Sshaneh rc2 = pOrigVfs->xDelete(pOrigVfs, pGroup->zName, 0); 3468a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; 3478a922f75Sshaneh } 3488a922f75Sshaneh pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL); 3498a922f75Sshaneh if( pSubOpen ){ 3508a922f75Sshaneh rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize); 3518a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 3528a922f75Sshaneh }else{ 3538a922f75Sshaneh rc = SQLITE_IOERR_TRUNCATE; 3548a922f75Sshaneh } 3558a922f75Sshaneh multiplexLeave(); 3568a922f75Sshaneh return rc; 3578a922f75Sshaneh } 3588a922f75Sshaneh 3598a922f75Sshaneh /* Pass xSync requests through to the original VFS without change 3608a922f75Sshaneh */ 3618a922f75Sshaneh static int multiplexSync(sqlite3_file *pConn, int flags){ 3628a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3638a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 3648a922f75Sshaneh int rc = SQLITE_OK; 3658a922f75Sshaneh int i; 3668a922f75Sshaneh multiplexEnter(); 3678a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 3688a922f75Sshaneh /* if we don't have it open, we don't need to sync it */ 3698a922f75Sshaneh if( pGroup->bOpen[i] ){ 3708a922f75Sshaneh sqlite3_file *pSubOpen = pGroup->pReal[i]; 3718a922f75Sshaneh int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); 3728a922f75Sshaneh if( rc2!=SQLITE_OK ) rc = rc2; 3738a922f75Sshaneh } 3748a922f75Sshaneh } 3758a922f75Sshaneh multiplexLeave(); 3768a922f75Sshaneh return rc; 3778a922f75Sshaneh } 3788a922f75Sshaneh 3798a922f75Sshaneh /* Pass xFileSize requests through to the original VFS. 3808a922f75Sshaneh ** Aggregate the size of all the chunks before returning. 3818a922f75Sshaneh */ 3828a922f75Sshaneh static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ 3838a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 3848a922f75Sshaneh multiplexGroup *pGroup = p->pGroup; 3858a922f75Sshaneh int rc = SQLITE_OK; 3868a922f75Sshaneh int rc2; 3878a922f75Sshaneh int i; 3888a922f75Sshaneh multiplexEnter(); 3898a922f75Sshaneh *pSize = 0; 3908a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 3918a922f75Sshaneh sqlite3_file *pSubOpen = NULL; 3928a922f75Sshaneh /* if not opened already, check to see if the chunk exists */ 3938a922f75Sshaneh if( pGroup->bOpen[i] ){ 3948a922f75Sshaneh pSubOpen = pGroup->pReal[i]; 3958a922f75Sshaneh }else{ 3968a922f75Sshaneh sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ 3978a922f75Sshaneh int exists = 0; 3988a922f75Sshaneh pGroup->zName[pGroup->nName] = '\0'; 3998a922f75Sshaneh if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i); 4008a922f75Sshaneh rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->zName, SQLITE_ACCESS_EXISTS, &exists); 4018a922f75Sshaneh if( rc2==SQLITE_OK && exists){ 4028a922f75Sshaneh /* if it exists, open it */ 4038a922f75Sshaneh pSubOpen = multiplexSubOpen(p, i, &rc, NULL); 4048a922f75Sshaneh }else{ 4058a922f75Sshaneh /* stop at first "gap" */ 4068a922f75Sshaneh break; 4078a922f75Sshaneh } 4088a922f75Sshaneh } 4098a922f75Sshaneh if( pSubOpen ){ 410fd1552f2Sshaneh sqlite3_int64 sz; 4118a922f75Sshaneh rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); 4128a922f75Sshaneh if( rc2!=SQLITE_OK ){ 4138a922f75Sshaneh rc = rc2; 4148a922f75Sshaneh }else{ 4158a922f75Sshaneh *pSize += sz; 4168a922f75Sshaneh } 4178a922f75Sshaneh }else{ 4188a922f75Sshaneh break; 4198a922f75Sshaneh } 4208a922f75Sshaneh } 4218a922f75Sshaneh multiplexLeave(); 4228a922f75Sshaneh return rc; 4238a922f75Sshaneh } 4248a922f75Sshaneh 4258a922f75Sshaneh /* Pass xLock requests through to the original VFS unchanged. 4268a922f75Sshaneh */ 4278a922f75Sshaneh static int multiplexLock(sqlite3_file *pConn, int lock){ 4288a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4298a922f75Sshaneh int rc; 4308a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4318a922f75Sshaneh if( pSubOpen ){ 4328a922f75Sshaneh return pSubOpen->pMethods->xLock(pSubOpen, lock); 4338a922f75Sshaneh } 4348a922f75Sshaneh return SQLITE_BUSY; 4358a922f75Sshaneh } 4368a922f75Sshaneh 4378a922f75Sshaneh /* Pass xUnlock requests through to the original VFS unchanged. 4388a922f75Sshaneh */ 4398a922f75Sshaneh static int multiplexUnlock(sqlite3_file *pConn, int lock){ 4408a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4418a922f75Sshaneh int rc; 4428a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4438a922f75Sshaneh if( pSubOpen ){ 4448a922f75Sshaneh return pSubOpen->pMethods->xUnlock(pSubOpen, lock); 4458a922f75Sshaneh } 4468a922f75Sshaneh return SQLITE_IOERR_UNLOCK; 4478a922f75Sshaneh } 4488a922f75Sshaneh 4498a922f75Sshaneh /* Pass xCheckReservedLock requests through to the original VFS unchanged. 4508a922f75Sshaneh */ 4518a922f75Sshaneh static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ 4528a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4538a922f75Sshaneh int rc; 4548a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4558a922f75Sshaneh if( pSubOpen ){ 4568a922f75Sshaneh return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); 4578a922f75Sshaneh } 4588a922f75Sshaneh return SQLITE_IOERR_CHECKRESERVEDLOCK; 4598a922f75Sshaneh } 4608a922f75Sshaneh 4618a922f75Sshaneh /* Pass xFileControl requests through to the original VFS unchanged. 4628a922f75Sshaneh */ 4638a922f75Sshaneh static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ 4648a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4658a922f75Sshaneh int rc; 4668a922f75Sshaneh sqlite3_file *pSubOpen; 4678a922f75Sshaneh if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK; 4688a922f75Sshaneh pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4698a922f75Sshaneh if( pSubOpen ){ 4708a922f75Sshaneh return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); 4718a922f75Sshaneh } 4728a922f75Sshaneh return SQLITE_ERROR; 4738a922f75Sshaneh } 4748a922f75Sshaneh 4758a922f75Sshaneh /* Pass xSectorSize requests through to the original VFS unchanged. 4768a922f75Sshaneh */ 4778a922f75Sshaneh static int multiplexSectorSize(sqlite3_file *pConn){ 4788a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4798a922f75Sshaneh int rc; 4808a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4818a922f75Sshaneh if( pSubOpen ){ 4828a922f75Sshaneh return pSubOpen->pMethods->xSectorSize(pSubOpen); 4838a922f75Sshaneh } 4848a922f75Sshaneh return SQLITE_DEFAULT_SECTOR_SIZE; 4858a922f75Sshaneh } 4868a922f75Sshaneh 4878a922f75Sshaneh /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. 4888a922f75Sshaneh */ 4898a922f75Sshaneh static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ 4908a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 4918a922f75Sshaneh int rc; 4928a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 4938a922f75Sshaneh if( pSubOpen ){ 4948a922f75Sshaneh return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); 4958a922f75Sshaneh } 4968a922f75Sshaneh return 0; 4978a922f75Sshaneh } 4988a922f75Sshaneh 4998a922f75Sshaneh /* Pass xShmMap requests through to the original VFS unchanged. 5008a922f75Sshaneh */ 5018a922f75Sshaneh static int multiplexShmMap( 5028a922f75Sshaneh sqlite3_file *pConn, /* Handle open on database file */ 5038a922f75Sshaneh int iRegion, /* Region to retrieve */ 5048a922f75Sshaneh int szRegion, /* Size of regions */ 5058a922f75Sshaneh int bExtend, /* True to extend file if necessary */ 5068a922f75Sshaneh void volatile **pp /* OUT: Mapped memory */ 5078a922f75Sshaneh ){ 5088a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5098a922f75Sshaneh int rc; 5108a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5118a922f75Sshaneh if( pSubOpen ){ 5128a922f75Sshaneh return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp); 5138a922f75Sshaneh } 5148a922f75Sshaneh return SQLITE_IOERR; 5158a922f75Sshaneh } 5168a922f75Sshaneh 5178a922f75Sshaneh /* Pass xShmLock requests through to the original VFS unchanged. 5188a922f75Sshaneh */ 5198a922f75Sshaneh static int multiplexShmLock( 5208a922f75Sshaneh sqlite3_file *pConn, /* Database file holding the shared memory */ 5218a922f75Sshaneh int ofst, /* First lock to acquire or release */ 5228a922f75Sshaneh int n, /* Number of locks to acquire or release */ 5238a922f75Sshaneh int flags /* What to do with the lock */ 5248a922f75Sshaneh ){ 5258a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5268a922f75Sshaneh int rc; 5278a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5288a922f75Sshaneh if( pSubOpen ){ 5298a922f75Sshaneh return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); 5308a922f75Sshaneh } 5318a922f75Sshaneh return SQLITE_BUSY; 5328a922f75Sshaneh } 5338a922f75Sshaneh 5348a922f75Sshaneh /* Pass xShmBarrier requests through to the original VFS unchanged. 5358a922f75Sshaneh */ 5368a922f75Sshaneh static void multiplexShmBarrier(sqlite3_file *pConn){ 5378a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5388a922f75Sshaneh int rc; 5398a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5408a922f75Sshaneh if( pSubOpen ){ 5418a922f75Sshaneh pSubOpen->pMethods->xShmBarrier(pSubOpen); 5428a922f75Sshaneh } 5438a922f75Sshaneh } 5448a922f75Sshaneh 5458a922f75Sshaneh /* Pass xShmUnmap requests through to the original VFS unchanged. 5468a922f75Sshaneh */ 5478a922f75Sshaneh static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ 5488a922f75Sshaneh multiplexConn *p = (multiplexConn*)pConn; 5498a922f75Sshaneh int rc; 5508a922f75Sshaneh sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); 5518a922f75Sshaneh if( pSubOpen ){ 5528a922f75Sshaneh return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); 5538a922f75Sshaneh } 5548a922f75Sshaneh return SQLITE_OK; 5558a922f75Sshaneh } 5568a922f75Sshaneh 5578a922f75Sshaneh /************************** Public Interfaces *****************************/ 5588a922f75Sshaneh /* 5598a922f75Sshaneh ** Initialize the multiplex VFS shim. Use the VFS named zOrigVfsName 5608a922f75Sshaneh ** as the VFS that does the actual work. Use the default if 5618a922f75Sshaneh ** zOrigVfsName==NULL. 5628a922f75Sshaneh ** 5638a922f75Sshaneh ** The multiplex VFS shim is named "multiplex". It will become the default 5648a922f75Sshaneh ** VFS if makeDefault is non-zero. 5658a922f75Sshaneh ** 5668a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once 5678a922f75Sshaneh ** during start-up. 5688a922f75Sshaneh */ 5698a922f75Sshaneh int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ 5708a922f75Sshaneh sqlite3_vfs *pOrigVfs; 5718a922f75Sshaneh if( gMultiplex.isInitialized ) return SQLITE_MISUSE; 5728a922f75Sshaneh pOrigVfs = sqlite3_vfs_find(zOrigVfsName); 5738a922f75Sshaneh if( pOrigVfs==0 ) return SQLITE_ERROR; 5748a922f75Sshaneh assert( pOrigVfs!=&gMultiplex.sThisVfs ); 5758a922f75Sshaneh gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 5768a922f75Sshaneh if( !gMultiplex.pMutex ){ 5778a922f75Sshaneh return SQLITE_NOMEM; 5788a922f75Sshaneh } 5798a922f75Sshaneh gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; 5808a922f75Sshaneh gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; 5818a922f75Sshaneh gMultiplex.pGroups = NULL; 5828a922f75Sshaneh gMultiplex.isInitialized = 1; 5838a922f75Sshaneh gMultiplex.pOrigVfs = pOrigVfs; 5848a922f75Sshaneh gMultiplex.sThisVfs = *pOrigVfs; 5858a922f75Sshaneh gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); 5868a922f75Sshaneh gMultiplex.sThisVfs.zName = "multiplex"; 5878a922f75Sshaneh gMultiplex.sThisVfs.xOpen = multiplexOpen; 5888a922f75Sshaneh gMultiplex.sIoMethodsV1.iVersion = 1; 5898a922f75Sshaneh gMultiplex.sIoMethodsV1.xClose = multiplexClose; 5908a922f75Sshaneh gMultiplex.sIoMethodsV1.xRead = multiplexRead; 5918a922f75Sshaneh gMultiplex.sIoMethodsV1.xWrite = multiplexWrite; 5928a922f75Sshaneh gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate; 5938a922f75Sshaneh gMultiplex.sIoMethodsV1.xSync = multiplexSync; 5948a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize; 5958a922f75Sshaneh gMultiplex.sIoMethodsV1.xLock = multiplexLock; 5968a922f75Sshaneh gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock; 5978a922f75Sshaneh gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock; 5988a922f75Sshaneh gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl; 5998a922f75Sshaneh gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize; 6008a922f75Sshaneh gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics; 6018a922f75Sshaneh gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; 6028a922f75Sshaneh gMultiplex.sIoMethodsV2.iVersion = 2; 6038a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; 6048a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; 6058a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; 6068a922f75Sshaneh gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; 6078a922f75Sshaneh sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); 6088a922f75Sshaneh return SQLITE_OK; 6098a922f75Sshaneh } 6108a922f75Sshaneh 6118a922f75Sshaneh /* 6128a922f75Sshaneh ** Shutdown the multiplex system. 6138a922f75Sshaneh ** 6148a922f75Sshaneh ** All SQLite database connections must be closed before calling this 6158a922f75Sshaneh ** routine. 6168a922f75Sshaneh ** 6178a922f75Sshaneh ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while 6188a922f75Sshaneh ** shutting down in order to free all remaining multiplex groups. 6198a922f75Sshaneh */ 6208a922f75Sshaneh int sqlite3_multiplex_shutdown(void){ 6218a922f75Sshaneh if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; 6228a922f75Sshaneh if( gMultiplex.pGroups ) return SQLITE_MISUSE; 6238a922f75Sshaneh gMultiplex.isInitialized = 0; 6248a922f75Sshaneh sqlite3_mutex_free(gMultiplex.pMutex); 6258a922f75Sshaneh sqlite3_vfs_unregister(&gMultiplex.sThisVfs); 6268a922f75Sshaneh memset(&gMultiplex, 0, sizeof(gMultiplex)); 6278a922f75Sshaneh return SQLITE_OK; 6288a922f75Sshaneh } 6298a922f75Sshaneh 6308a922f75Sshaneh /* 6318a922f75Sshaneh ** Adjust chunking params. VFS should be initialized first. 6328a922f75Sshaneh ** No files should be open. Re-intializing will reset these 6338a922f75Sshaneh ** to the default. 6348a922f75Sshaneh */ 6358a922f75Sshaneh int sqlite3_multiplex_set( 6368a922f75Sshaneh int nChunkSize, /* Max chunk size */ 6378a922f75Sshaneh int nMaxChunks /* Max number of chunks */ 6388a922f75Sshaneh ){ 6398a922f75Sshaneh if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; 6408a922f75Sshaneh if( gMultiplex.pGroups ) return SQLITE_MISUSE; 641*e5a6ad6dSshaneh if( nChunkSize<32 ) return SQLITE_MISUSE; 642*e5a6ad6dSshaneh if( nMaxChunks<1 ) return SQLITE_MISUSE; 6438a922f75Sshaneh if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ) return SQLITE_MISUSE; 6448a922f75Sshaneh multiplexEnter(); 6458a922f75Sshaneh gMultiplex.nChunkSize = nChunkSize; 6468a922f75Sshaneh gMultiplex.nMaxChunks = nMaxChunks; 6478a922f75Sshaneh multiplexLeave(); 6488a922f75Sshaneh return SQLITE_OK; 6498a922f75Sshaneh } 6508a922f75Sshaneh 6518a922f75Sshaneh /***************************** Test Code ***********************************/ 6528a922f75Sshaneh #ifdef SQLITE_TEST 6538a922f75Sshaneh #include <tcl.h> 6548a922f75Sshaneh 6558a922f75Sshaneh extern const char *sqlite3TestErrorName(int); 6568a922f75Sshaneh 6578a922f75Sshaneh 6588a922f75Sshaneh /* 6598a922f75Sshaneh ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT 6608a922f75Sshaneh */ 6618a922f75Sshaneh static int test_multiplex_initialize( 6628a922f75Sshaneh void * clientData, 6638a922f75Sshaneh Tcl_Interp *interp, 6648a922f75Sshaneh int objc, 6658a922f75Sshaneh Tcl_Obj *CONST objv[] 6668a922f75Sshaneh ){ 6678a922f75Sshaneh const char *zName; /* Name of new multiplex VFS */ 6688a922f75Sshaneh int makeDefault; /* True to make the new VFS the default */ 6698a922f75Sshaneh int rc; /* Value returned by multiplex_initialize() */ 6708a922f75Sshaneh 6718a922f75Sshaneh UNUSED_PARAMETER(clientData); 6728a922f75Sshaneh 6738a922f75Sshaneh /* Process arguments */ 6748a922f75Sshaneh if( objc!=3 ){ 6758a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT"); 6768a922f75Sshaneh return TCL_ERROR; 6778a922f75Sshaneh } 6788a922f75Sshaneh zName = Tcl_GetString(objv[1]); 6798a922f75Sshaneh if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR; 6808a922f75Sshaneh if( zName[0]=='\0' ) zName = 0; 6818a922f75Sshaneh 6828a922f75Sshaneh /* Call sqlite3_multiplex_initialize() */ 6838a922f75Sshaneh rc = sqlite3_multiplex_initialize(zName, makeDefault); 6848a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 6858a922f75Sshaneh 6868a922f75Sshaneh return TCL_OK; 6878a922f75Sshaneh } 6888a922f75Sshaneh 6898a922f75Sshaneh /* 6908a922f75Sshaneh ** tclcmd: sqlite3_multiplex_shutdown 6918a922f75Sshaneh */ 6928a922f75Sshaneh static int test_multiplex_shutdown( 6938a922f75Sshaneh void * clientData, 6948a922f75Sshaneh Tcl_Interp *interp, 6958a922f75Sshaneh int objc, 6968a922f75Sshaneh Tcl_Obj *CONST objv[] 6978a922f75Sshaneh ){ 6988a922f75Sshaneh int rc; /* Value returned by multiplex_shutdown() */ 6998a922f75Sshaneh 7008a922f75Sshaneh UNUSED_PARAMETER(clientData); 7018a922f75Sshaneh 7028a922f75Sshaneh if( objc!=1 ){ 7038a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, ""); 7048a922f75Sshaneh return TCL_ERROR; 7058a922f75Sshaneh } 7068a922f75Sshaneh 7078a922f75Sshaneh /* Call sqlite3_multiplex_shutdown() */ 7088a922f75Sshaneh rc = sqlite3_multiplex_shutdown(); 7098a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 7108a922f75Sshaneh 7118a922f75Sshaneh return TCL_OK; 7128a922f75Sshaneh } 7138a922f75Sshaneh 7148a922f75Sshaneh /* 7158a922f75Sshaneh ** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS 7168a922f75Sshaneh */ 7178a922f75Sshaneh static int test_multiplex_set( 7188a922f75Sshaneh void * clientData, 7198a922f75Sshaneh Tcl_Interp *interp, 7208a922f75Sshaneh int objc, 7218a922f75Sshaneh Tcl_Obj *CONST objv[] 7228a922f75Sshaneh ){ 7238a922f75Sshaneh int nChunkSize; /* Max chunk size */ 7248a922f75Sshaneh int nMaxChunks; /* Max number of chunks */ 7258a922f75Sshaneh int rc; /* Value returned by sqlite3_multiplex_set() */ 7268a922f75Sshaneh 7278a922f75Sshaneh UNUSED_PARAMETER(clientData); 7288a922f75Sshaneh 7298a922f75Sshaneh /* Process arguments */ 7308a922f75Sshaneh if( objc!=3 ){ 7318a922f75Sshaneh Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS"); 7328a922f75Sshaneh return TCL_ERROR; 7338a922f75Sshaneh } 7348a922f75Sshaneh if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR; 7358a922f75Sshaneh if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR; 7368a922f75Sshaneh 7378a922f75Sshaneh /* Invoke sqlite3_multiplex_set() */ 7388a922f75Sshaneh rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks); 7398a922f75Sshaneh 7408a922f75Sshaneh Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); 7418a922f75Sshaneh return TCL_OK; 7428a922f75Sshaneh } 7438a922f75Sshaneh 7448a922f75Sshaneh /* 7458a922f75Sshaneh ** tclcmd: sqlite3_multiplex_dump 7468a922f75Sshaneh */ 7478a922f75Sshaneh static int test_multiplex_dump( 7488a922f75Sshaneh void * clientData, 7498a922f75Sshaneh Tcl_Interp *interp, 7508a922f75Sshaneh int objc, 7518a922f75Sshaneh Tcl_Obj *CONST objv[] 7528a922f75Sshaneh ){ 7538a922f75Sshaneh Tcl_Obj *pResult; 7548a922f75Sshaneh Tcl_Obj *pGroupTerm; 7558a922f75Sshaneh multiplexGroup *pGroup; 7568a922f75Sshaneh int i; 7578a922f75Sshaneh int nChunks = 0; 7588a922f75Sshaneh 7598a922f75Sshaneh UNUSED_PARAMETER(clientData); 7608a922f75Sshaneh UNUSED_PARAMETER(objc); 7618a922f75Sshaneh UNUSED_PARAMETER(objv); 7628a922f75Sshaneh 7638a922f75Sshaneh pResult = Tcl_NewObj(); 7648a922f75Sshaneh multiplexEnter(); 7658a922f75Sshaneh for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){ 7668a922f75Sshaneh pGroupTerm = Tcl_NewObj(); 7678a922f75Sshaneh 7688a922f75Sshaneh pGroup->zName[pGroup->nName] = '\0'; 7698a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7708a922f75Sshaneh Tcl_NewStringObj(pGroup->zName, -1)); 7718a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7728a922f75Sshaneh Tcl_NewIntObj(pGroup->nName)); 7738a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7748a922f75Sshaneh Tcl_NewIntObj(pGroup->flags)); 7758a922f75Sshaneh 7768a922f75Sshaneh /* count number of chunks with open handles */ 7778a922f75Sshaneh for(i=0; i<gMultiplex.nMaxChunks; i++){ 7788a922f75Sshaneh if( pGroup->bOpen[i] ) nChunks++; 7798a922f75Sshaneh } 7808a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7818a922f75Sshaneh Tcl_NewIntObj(nChunks)); 7828a922f75Sshaneh 7838a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7848a922f75Sshaneh Tcl_NewIntObj(gMultiplex.nChunkSize)); 7858a922f75Sshaneh Tcl_ListObjAppendElement(interp, pGroupTerm, 7868a922f75Sshaneh Tcl_NewIntObj(gMultiplex.nMaxChunks)); 7878a922f75Sshaneh 7888a922f75Sshaneh Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); 7898a922f75Sshaneh } 7908a922f75Sshaneh multiplexLeave(); 7918a922f75Sshaneh Tcl_SetObjResult(interp, pResult); 7928a922f75Sshaneh return TCL_OK; 7938a922f75Sshaneh } 7948a922f75Sshaneh 7958a922f75Sshaneh /* 7968a922f75Sshaneh ** This routine registers the custom TCL commands defined in this 7978a922f75Sshaneh ** module. This should be the only procedure visible from outside 7988a922f75Sshaneh ** of this module. 7998a922f75Sshaneh */ 8008a922f75Sshaneh int Sqlitemultiplex_Init(Tcl_Interp *interp){ 8018a922f75Sshaneh static struct { 8028a922f75Sshaneh char *zName; 8038a922f75Sshaneh Tcl_ObjCmdProc *xProc; 8048a922f75Sshaneh } aCmd[] = { 8058a922f75Sshaneh { "sqlite3_multiplex_initialize", test_multiplex_initialize }, 8068a922f75Sshaneh { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, 8078a922f75Sshaneh { "sqlite3_multiplex_set", test_multiplex_set }, 8088a922f75Sshaneh { "sqlite3_multiplex_dump", test_multiplex_dump }, 8098a922f75Sshaneh }; 8108a922f75Sshaneh int i; 8118a922f75Sshaneh 8128a922f75Sshaneh for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ 8138a922f75Sshaneh Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); 8148a922f75Sshaneh } 8158a922f75Sshaneh 8168a922f75Sshaneh return TCL_OK; 8178a922f75Sshaneh } 8188a922f75Sshaneh #endif 819