Index: src/test_multiplex.c ================================================================== --- src/test_multiplex.c +++ src/test_multiplex.c @@ -124,17 +124,19 @@ ** ** There is an instance of the following object for each defined multiplex ** group. */ struct multiplexGroup { - sqlite3_file **pReal; /* Handles to each chunk */ - char *bOpen; /* array of bools - 0 if chunk not opened */ + struct multiplexReal { /* For each chunk */ + sqlite3_file *p; /* Handle for the chunk */ + char *z; /* Name of this chunk */ + } *aReal; /* list of all chunks */ + int nReal; /* Number of chunks */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ int nChunkSize; /* Chunk size used for this group */ - int nMaxChunks; /* Max number of chunks for this group */ int bEnabled; /* TRUE to use Multiplex VFS for this file */ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ }; /* @@ -189,16 +191,10 @@ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; - - /* Storage for temp file names. Allocated during - ** initialization to the max pathname of the underlying VFS. - */ - char *zName; - } gMultiplex; /************************* Utility Routines *********************************/ /* ** Acquire and release the mutex used to serialize access to the @@ -284,50 +280,74 @@ } } return rc; } + +/* Compute the filename for the iChunk-th chunk +*/ +static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){ + if( iChunk>=pGroup->nReal ){ + struct multiplexReal *p; + p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p)); + if( p==0 ){ + return SQLITE_NOMEM; + } + memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal)); + pGroup->aReal = p; + pGroup->nReal = iChunk+1; + } + if( pGroup->aReal[iChunk].z==0 ){ + char *z; + int n = pGroup->nName; + pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+3 ); + if( z==0 ){ + return SQLITE_NOMEM; + } + memcpy(z, pGroup->zName, n+1); + if( iChunk>0 ){ +#ifdef SQLITE_ENABLE_8_3_NAMES + if( n>3 && z[n-3]=='.' ){ + n--; + }else if( n>4 && z[n-4]=='.' ){ + n -= 2; + } +#endif + sqlite3_snprintf(3,&z[n],"%02d",iChunk); + } + } + return SQLITE_OK; +} /* Translate an sqlite3_file* that is really a multiplexGroup* into ** the sqlite3_file* for the underlying original VFS. */ static sqlite3_file *multiplexSubOpen( - multiplexConn *pConn, + multiplexGroup *pGroup, int iChunk, int *rc, int *pOutFlags ){ - multiplexGroup *pGroup = pConn->pGroup; + sqlite3_file *pSubOpen = 0; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - if( iChunknMaxChunks ){ - sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ - if( !pGroup->bOpen[iChunk] ){ - memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); - if( iChunk ){ -#ifdef SQLITE_MULTIPLEX_EXT_OVWR - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, - SQLITE_MULTIPLEX_EXT_FMT, iChunk); -#else - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName, - SQLITE_MULTIPLEX_EXT_FMT, iChunk); -#endif - } - *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, - pGroup->flags, pOutFlags); - if( *rc==SQLITE_OK ){ - pGroup->bOpen[iChunk] = -1; - return pSubOpen; - } - return NULL; - } - *rc = SQLITE_OK; - return pSubOpen; - } - *rc = SQLITE_FULL; - return NULL; + *rc = multiplexSubFilename(pGroup, iChunk); + if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){ + pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile ); + if( pSubOpen==0 ){ + *rc = SQLITE_NOMEM; + return 0; + } + pGroup->aReal[iChunk].p = pSubOpen; + *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen, + pGroup->flags, pOutFlags); + if( *rc!=SQLITE_OK ){ + sqlite3_free(pSubOpen); + pGroup->aReal[iChunk].p = 0; + return 0; + } + } + return pSubOpen; } /* ** This is the implementation of the multiplex_control() SQL function. */ @@ -381,10 +401,40 @@ int rc; rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 0, multiplexControlFunc, 0, 0); return rc; } + +/* +** Close a single sub-file in the connection group. +*/ +static void multiplexSubClose( + multiplexGroup *pGroup, + int iChunk, + sqlite3_vfs *pOrigVfs +){ + sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p; + if( pSubOpen ){ + if( pOrigVfs ) pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0); + pSubOpen->pMethods->xClose(pSubOpen); + sqlite3_free(pGroup->aReal[iChunk].p); + } + sqlite3_free(pGroup->aReal[iChunk].z); + memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk])); +} + +/* +** Deallocate memory held by a multiplexGroup +*/ +static void multiplexFreeComponents(multiplexGroup *pGroup){ + int i; + for(i=0; inReal; i++){ multiplexSubClose(pGroup, i, 0); } + sqlite3_free(pGroup->aReal); + pGroup->aReal = 0; + pGroup->nReal = 0; +} + /************************* VFS Method Wrappers *****************************/ /* ** This is the xOpen method used for the "multiplex" VFS. @@ -404,14 +454,15 @@ multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int nName; - int i; int sz; + char *zToFree = 0; UNUSED_PARAMETER(pVfs); + memset(pConn, 0, pVfs->szOsFile); /* We need to create a group structure and manage ** access to this group of files. */ multiplexEnter(); @@ -421,32 +472,26 @@ ** temporary file name to use. This will be handled by the ** original xOpen method. We just need to allocate space for ** it. */ if( !zName ){ - rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, gMultiplex.zName); - zName = gMultiplex.zName; + zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 ); + if( zName==0 ){ + rc = SQLITE_NOMEM; + }else{ + rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree); + } } if( rc==SQLITE_OK ){ /* allocate space for group */ nName = multiplexStrlen30(zName); sz = sizeof(multiplexGroup) /* multiplexGroup */ - + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */ - + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */ - + SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */ - + nName + 1; /* zName */ -#ifndef SQLITE_MULTIPLEX_EXT_OVWR - sz += SQLITE_MULTIPLEX_EXT_SZ; - assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); -#else - assert(nName >= SQLITE_MULTIPLEX_EXT_SZ); - assert(nName < pOrigVfs->mxPathname); -#endif + + nName + 1; /* zName */ pGroup = sqlite3_malloc( sz ); if( pGroup==0 ){ - rc=SQLITE_NOMEM; + rc = SQLITE_NOMEM; } } if( rc==SQLITE_OK ){ const char *zChunkSize; @@ -454,31 +499,23 @@ char *p = (char *)&pGroup[1]; pMultiplexOpen->pGroup = pGroup; memset(pGroup, 0, sz); pGroup->bEnabled = -1; pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; - zChunkSize = sqlite3_uri_parameter(zName, "chunksize"); - if( zChunkSize ){ - int n = atoi(zChunkSize); - if( n>0 ) pGroup->nChunkSize = (n+0xffff)&~0xffff; - } - pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; - pGroup->pReal = (sqlite3_file **)p; - p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks); - for(i=0; inMaxChunks; i++){ - pGroup->pReal[i] = (sqlite3_file *)p; - p += pOrigVfs->szOsFile; - } - /* bOpen[] vals should all be zero from memset above */ - pGroup->bOpen = p; - p += pGroup->nMaxChunks; + if( flags & SQLITE_OPEN_URI ){ + zChunkSize = sqlite3_uri_parameter(zName, "chunksize"); + if( zChunkSize ){ + int n = atoi(zChunkSize); + if( n>0 ) pGroup->nChunkSize = (n+0xffff)&~0xffff; + } + } pGroup->zName = p; /* save off base filename, name length, and original open flags */ memcpy(pGroup->zName, zName, nName+1); pGroup->nName = nName; pGroup->flags = flags; - pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); + pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags); if( pSubOpen ){ /* if this file is already larger than chunk size, disable ** the multiplex feature. */ sqlite3_int64 sz; @@ -494,63 +531,30 @@ /* place this group at the head of our list */ pGroup->pNext = gMultiplex.pGroups; if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; gMultiplex.pGroups = pGroup; }else{ + multiplexFreeComponents(pGroup); sqlite3_free(pGroup); } } multiplexLeave(); + sqlite3_free(zToFree); return rc; } /* ** This is the xDelete method used for the "multiplex" VFS. -** It attempts to delete the filename specified, as well -** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension. +** It attempts to delete the filename specified. */ static int multiplexDelete( sqlite3_vfs *pVfs, /* The multiplex VFS */ const char *zName, /* Name of file to delete */ int syncDir ){ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - int rc = SQLITE_OK; - int nName = multiplexStrlen30(zName); - int i; - - UNUSED_PARAMETER(pVfs); - - multiplexEnter(); - memcpy(gMultiplex.zName, zName, nName+1); - for(i=0; ixAccess(pOrigVfs, gMultiplex.zName, - SQLITE_ACCESS_EXISTS, &exists); - if( rc2==SQLITE_OK && exists ){ - /* if it exists, delete it */ - rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); - if( rc2!=SQLITE_OK ) rc = rc2; - }else{ - /* stop at first "gap" */ - break; - } - } - multiplexLeave(); - return rc; + return pOrigVfs->xDelete(pOrigVfs, zName, syncDir); } static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){ return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d); } @@ -594,21 +598,12 @@ */ static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; - int i; multiplexEnter(); - /* close any open handles */ - for(i=0; inMaxChunks; i++){ - if( pGroup->bOpen[i] ){ - sqlite3_file *pSubOpen = pGroup->pReal[i]; - int rc2 = pSubOpen->pMethods->xClose(pSubOpen); - if( rc2!=SQLITE_OK ) rc = rc2; - pGroup->bOpen[i] = 0; - } - } + multiplexFreeComponents(pGroup); /* remove from linked list */ if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; if( pGroup->pPrev ){ pGroup->pPrev->pNext = pGroup->pNext; }else{ @@ -632,20 +627,20 @@ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); - if( !pSubOpen ){ + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL); + if( pSubOpen==0 ){ rc = SQLITE_IOERR_READ; }else{ rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); } }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); - sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; @@ -678,20 +673,20 @@ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL); if( pSubOpen==0 ){ rc = SQLITE_IOERR_WRITE; }else{ rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); } }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); - sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; @@ -719,11 +714,11 @@ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL); if( pSubOpen==0 ){ rc = SQLITE_IOERR_TRUNCATE; }else{ rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); } @@ -730,33 +725,15 @@ }else{ int rc2; int i; sqlite3_file *pSubOpen; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); /* delete the chunks above the truncate limit */ - for(i=(int)(size / pGroup->nChunkSize)+1; inMaxChunks; i++){ - /* close any open chunks before deleting them */ - if( pGroup->bOpen[i] ){ - pSubOpen = pGroup->pReal[i]; - rc2 = pSubOpen->pMethods->xClose(pSubOpen); - if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; - pGroup->bOpen[i] = 0; - } -#ifdef SQLITE_MULTIPLEX_EXT_OVWR - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, - SQLITE_MULTIPLEX_EXT_FMT, i); -#else - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName, - SQLITE_MULTIPLEX_EXT_FMT, i); -#endif - rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); - if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; - } - pSubOpen = multiplexSubOpen(p, (int)(size/pGroup->nChunkSize), &rc2, NULL); + for(i=(int)(size / pGroup->nChunkSize)+1; inReal; i++){ + multiplexSubClose(pGroup, i, pOrigVfs); + } + pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->nChunkSize), &rc2,0); if( pSubOpen ){ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize); if( rc2!=SQLITE_OK ) rc = rc2; }else{ rc = SQLITE_IOERR_TRUNCATE; @@ -772,14 +749,13 @@ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); - for(i=0; inMaxChunks; i++){ - /* if we don't have it open, we don't need to sync it */ - if( pGroup->bOpen[i] ){ - sqlite3_file *pSubOpen = pGroup->pReal[i]; + for(i=0; inReal; i++){ + sqlite3_file *pSubOpen = pGroup->aReal[i].p; + if( pSubOpen ){ int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); if( rc2!=SQLITE_OK ) rc = rc2; } } multiplexLeave(); @@ -795,47 +771,32 @@ int rc = SQLITE_OK; int rc2; int i; multiplexEnter(); if( !pGroup->bEnabled ){ - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL); if( pSubOpen==0 ){ rc = SQLITE_IOERR_FSTAT; }else{ rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize); } }else{ *pSize = 0; - for(i=0; inMaxChunks; i++){ - sqlite3_file *pSubOpen = NULL; - /* if not opened already, check to see if the chunk exists */ - if( pGroup->bOpen[i] ){ - pSubOpen = pGroup->pReal[i]; - }else{ - sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ - int exists = 0; - memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); - if( i ){ -#ifdef SQLITE_MULTIPLEX_EXT_OVWR - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, - SQLITE_MULTIPLEX_EXT_FMT, i); -#else - sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, - gMultiplex.zName+pGroup->nName, - SQLITE_MULTIPLEX_EXT_FMT, i); -#endif - } - rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, - SQLITE_ACCESS_EXISTS, &exists); - if( rc2==SQLITE_OK && exists){ - /* if it exists, open it */ - pSubOpen = multiplexSubOpen(p, i, &rc, NULL); - }else{ - /* stop at first "gap" */ - break; - } + for(i=0; 1; i++){ + sqlite3_file *pSubOpen = 0; + sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; + int exists = 0; + rc = multiplexSubFilename(pGroup, i); + if( rc ) break; + rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[i].z, + SQLITE_ACCESS_EXISTS, &exists); + if( rc2==SQLITE_OK && exists){ + /* if it exists, open it */ + pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL); + }else{ + /* stop at first "gap" */ + break; } if( pSubOpen ){ sqlite3_int64 sz; rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( rc2!=SQLITE_OK ){ @@ -858,11 +819,11 @@ /* Pass xLock requests through to the original VFS unchanged. */ static int multiplexLock(sqlite3_file *pConn, int lock){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xLock(pSubOpen, lock); } return SQLITE_BUSY; } @@ -870,11 +831,11 @@ /* Pass xUnlock requests through to the original VFS unchanged. */ static int multiplexUnlock(sqlite3_file *pConn, int lock){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xUnlock(pSubOpen, lock); } return SQLITE_IOERR_UNLOCK; } @@ -882,11 +843,11 @@ /* Pass xCheckReservedLock requests through to the original VFS unchanged. */ static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } return SQLITE_IOERR_CHECKRESERVEDLOCK; } @@ -922,27 +883,19 @@ rc = SQLITE_OK; } } break; case MULTIPLEX_CTRL_SET_MAX_CHUNKS: - if( pArg ) { - int nMaxChunks = *(int *)pArg; - if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){ - rc = SQLITE_MISUSE; - }else{ - pGroup->nMaxChunks = nMaxChunks; - rc = SQLITE_OK; - } - } + rc = SQLITE_OK; break; case SQLITE_FCNTL_SIZE_HINT: case SQLITE_FCNTL_CHUNK_SIZE: /* no-op these */ rc = SQLITE_OK; break; default: - pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL); if( pSubOpen ){ rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg); } break; } @@ -952,11 +905,11 @@ /* Pass xSectorSize requests through to the original VFS unchanged. */ static int multiplexSectorSize(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xSectorSize(pSubOpen); } return DEFAULT_SECTOR_SIZE; } @@ -964,11 +917,11 @@ /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. */ static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen); } return 0; } @@ -982,11 +935,11 @@ int bExtend, /* True to extend file if necessary */ void volatile **pp /* OUT: Mapped memory */ ){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp); } return SQLITE_IOERR; } @@ -999,11 +952,11 @@ int n, /* Number of locks to acquire or release */ int flags /* What to do with the lock */ ){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags); } return SQLITE_BUSY; } @@ -1011,11 +964,11 @@ /* Pass xShmBarrier requests through to the original VFS unchanged. */ static void multiplexShmBarrier(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ pSubOpen->pMethods->xShmBarrier(pSubOpen); } } @@ -1022,11 +975,11 @@ /* Pass xShmUnmap requests through to the original VFS unchanged. */ static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){ multiplexConn *p = (multiplexConn*)pConn; int rc; - sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); + sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } @@ -1052,15 +1005,10 @@ assert( pOrigVfs!=&gMultiplex.sThisVfs ); gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); if( !gMultiplex.pMutex ){ return SQLITE_NOMEM; } - gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); - if( !gMultiplex.zName ){ - sqlite3_mutex_free(gMultiplex.pMutex); - return SQLITE_NOMEM; - } gMultiplex.pGroups = NULL; gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); @@ -1117,11 +1065,10 @@ */ int sqlite3_multiplex_shutdown(void){ if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; if( gMultiplex.pGroups ) return SQLITE_MISUSE; gMultiplex.isInitialized = 0; - sqlite3_free(gMultiplex.zName); sqlite3_mutex_free(gMultiplex.pMutex); sqlite3_vfs_unregister(&gMultiplex.sThisVfs); memset(&gMultiplex, 0, sizeof(gMultiplex)); return SQLITE_OK; } @@ -1219,20 +1166,20 @@ Tcl_NewIntObj(pGroup->nName)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->flags)); /* count number of chunks with open handles */ - for(i=0; inMaxChunks; i++){ - if( pGroup->bOpen[i] ) nChunks++; + for(i=0; inReal; i++){ + if( pGroup->aReal[i].p!=0 ) nChunks++; } Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(nChunks)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nChunkSize)); Tcl_ListObjAppendElement(interp, pGroupTerm, - Tcl_NewIntObj(pGroup->nMaxChunks)); + Tcl_NewIntObj(pGroup->nReal)); Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } multiplexLeave(); Tcl_SetObjResult(interp, pResult); Index: test/multiplex.test ================================================================== --- test/multiplex.test +++ test/multiplex.test @@ -47,10 +47,11 @@ # This attempts to delete the base file and # and files with the chunk extension. proc multiplex_delete {name} { global g_max_chunks + forcedelete $name for {set i 0} {$i<$g_max_chunks} {incr i} { forcedelete [multiplex_name $name $i] forcedelete [multiplex_name $name-journal $i] forcedelete [multiplex_name $name-wal $i] } @@ -76,25 +77,25 @@ do_test multiplex-1.9.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.9.2 { sqlite3 db test.db } {} do_test multiplex-1.9.3 { multiplex_set db main 32768 16 } {SQLITE_OK} -do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_MISUSE} +do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_OK} do_test multiplex-1.9.5 { multiplex_set db main -1 16 } {SQLITE_MISUSE} do_test multiplex-1.9.6 { multiplex_set db main 31 16 } {SQLITE_OK} -do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_MISUSE} +do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_OK} do_test multiplex-1.9.8 { multiplex_set db main 1073741824 1 } {SQLITE_OK} do_test multiplex-1.9.9 { db close } {} do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.10.2 { sqlite3 db test.db } {} do_test multiplex-1.10.3 { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 } {0} -do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {1} +do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {0} do_test multiplex-1.10.5 { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 } {1} do_test multiplex-1.10.6 { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 } {0} -do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {1} +do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {0} do_test multiplex-1.10.8 { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0} do_test multiplex-1.10.9 { db close } {} do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.11.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} @@ -144,12 +145,13 @@ # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 +file delete -force test.x do_test multiplex-2.1.2 { - sqlite3 db test.db + sqlite3 db test.x execsql { PRAGMA page_size=1024; PRAGMA auto_vacuum=OFF; PRAGMA journal_mode=DELETE; } @@ -157,22 +159,22 @@ CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(1100)); INSERT INTO t1 VALUES(2, randomblob(1100)); } } {} -do_test multiplex-2.1.3 { file size [multiplex_name test.db 0] } {4096} +do_test multiplex-2.1.3 { file size [multiplex_name test.x 0] } {4096} do_test multiplex-2.1.4 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.2.1 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} -do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} +do_test multiplex-2.2.3 { file size [multiplex_name test.x 0] } {6144} do_test multiplex-2.3.1 { - sqlite3 db2 test2.db + sqlite3 db2 test2.x db2 close } {} do_test multiplex-2.4.1 { @@ -179,21 +181,21 @@ sqlite3_multiplex_shutdown } {SQLITE_MISUSE} do_test multiplex-2.4.2 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} -do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168} +do_test multiplex-2.4.4 { file size [multiplex_name test.x 0] } {7168} do_test multiplex-2.4.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-2.5.1 { - multiplex_delete test.db + multiplex_delete test.x sqlite3_multiplex_initialize "" 1 - sqlite3 db test.db + sqlite3 db test.x multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-2.5.2 { execsql { @@ -234,12 +236,12 @@ do_test multiplex-2.5.8 { db eval {SELECT a,length(b) FROM t1 WHERE a=4} } {4 4000} -do_test multiplex-2.5.9 { file size [multiplex_name test.db 0] } [list $g_chunk_size] -do_test multiplex-2.5.10 { file size [multiplex_name test.db 1] } [list $g_chunk_size] +do_test multiplex-2.5.9 { file size [multiplex_name test.x 0] } [list $g_chunk_size] +do_test multiplex-2.5.10 { file size [multiplex_name test.x 1] } [list $g_chunk_size] do_test multiplex-2.5.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} @@ -521,64 +523,21 @@ } -body { sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 } -# test that mismatch filesize is detected -# -# Do not run this test if $::G(perm:presql) is set. If it is set, then the -# expected IO error will occur within the Tcl [sqlite3] wrapper, not within -# the first SQL statement executed below. This breaks the test case. -# -if {0==[info exists ::G(perm:presql)] || $::G(perm:presql) == ""} { - set all_journal_modes {delete persist truncate memory off} - foreach jmode $all_journal_modes { - do_test multiplex-5.6.1.$jmode { - sqlite3_multiplex_shutdown - multiplex_delete test.db - sqlite3 db test.db - db eval { - PRAGMA page_size = 1024; - PRAGMA auto_vacuum = off; - } - db eval "PRAGMA journal_mode = $jmode;" - } $jmode - do_test multiplex-5.6.2.$jmode { - execsql { - CREATE TABLE t1(a, b); - INSERT INTO t1 VALUES(1, randomblob(15000)); - INSERT INTO t1 VALUES(2, randomblob(15000)); - INSERT INTO t1 VALUES(3, randomblob(15000)); - INSERT INTO t1 VALUES(4, randomblob(15000)); - INSERT INTO t1 VALUES(5, randomblob(15000)); - } - db close - sqlite3_multiplex_initialize "" 1 - sqlite3 db test.db - multiplex_set db main 4096 16 - } {SQLITE_OK} - do_test multiplex-5.6.3.$jmode { - catchsql { - INSERT INTO t1 VALUES(6, randomblob(15000)); - } - } {1 {disk I/O error}} - do_test multiplex-5.6.4.$jmode { - db close - } {} - } -} - #------------------------------------------------------------------------- # Test that you can vacuum a multiplex'ed DB. ifcapable vacuum { sqlite3_multiplex_shutdown do_test multiplex-6.0.0 { multiplex_delete test.db + multiplex_delete test.x sqlite3_multiplex_initialize "" 1 - sqlite3 db test.db + sqlite3 db test.x multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-6.1.0 { execsql { @@ -590,23 +549,23 @@ CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob($g_chunk_size)); INSERT INTO t1 VALUES(2, randomblob($g_chunk_size)); } } {} -do_test multiplex-6.2.1 { file size [multiplex_name test.db 0] } [list $g_chunk_size] -do_test multiplex-6.2.2 { file size [multiplex_name test.db 1] } [list $g_chunk_size] +do_test multiplex-6.2.1 { file size [multiplex_name test.x 0] } [list $g_chunk_size] +do_test multiplex-6.2.2 { file size [multiplex_name test.x 1] } [list $g_chunk_size] do_test multiplex-6.3.0 { execsql { VACUUM } } {} do_test multiplex-6.99 { db close - multiplex_delete test.db + multiplex_delete test.x sqlite3_multiplex_shutdown } {SQLITE_OK} } catch { sqlite3_multiplex_shutdown } finish_test