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