int nName; /* Length of base filename */
int flags; /* Flags used for original opening */
unsigned int szChunk; /* Chunk size used for this group */
- int bEnabled; /* TRUE to use Multiplex VFS for this file */
+ unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */
+ unsigned char bTruncate; /* TRUE to enable truncation of databases */
multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
};
/* Translate an sqlite3_file* that is really a multiplexGroup* into
** the sqlite3_file* for the underlying original VFS.
+**
+** For chunk 0, the pGroup->flags determines whether or not a new file
+** is created if it does not already exist. For chunks 1 and higher, the
+** file is created only if createFlag is 1.
*/
static sqlite3_file *multiplexSubOpen(
- multiplexGroup *pGroup,
- int iChunk,
- int *rc,
- int *pOutFlags
+ multiplexGroup *pGroup, /* The multiplexor group */
+ int iChunk, /* Which chunk to open. 0==original file */
+ int *rc, /* Result code in and out */
+ int *pOutFlags, /* Output flags */
+ int createFlag /* True to create if iChunk>0 */
){
sqlite3_file *pSubOpen = 0;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
*rc = multiplexSubFilename(pGroup, iChunk);
if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
+ int flags, rc2, bExists;
+ if( iChunk==0 ) createFlag = (pGroup->flags & SQLITE_OPEN_CREATE)!=0;
+ flags = pGroup->flags;
+ if( createFlag ){
+ flags |= SQLITE_OPEN_CREATE;
+ }else if( pGroup->aReal[iChunk].z==0 ){
+ return 0;
+ }else{
+ *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
+ SQLITE_ACCESS_EXISTS, &bExists);
+ if( *rc || !bExists ) return 0;
+ flags &= ~SQLITE_OPEN_CREATE;
+ }
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 ){
+ rc2 = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
+ flags, pOutFlags);
+ if( rc2!=SQLITE_OK ){
sqlite3_free(pSubOpen);
pGroup->aReal[iChunk].p = 0;
return 0;
return pSubOpen;
}
+/*
+** Return the size, in bytes, of chunk number iChunk. If that chunk
+** does not exist, then return 0. This function does not distingish between
+** non-existant files and zero-length files.
+*/
+static sqlite3_int64 multiplexSubSize(
+ multiplexGroup *pGroup, /* The multiplexor group */
+ int iChunk, /* Which chunk to open. 0==original file */
+ int *rc /* Result code in and out */
+){
+ sqlite3_file *pSub;
+ sqlite3_int64 sz = 0;
+
+ pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
+ if( pSub==0 ) return 0;
+ *rc = pSub->pMethods->xFileSize(pSub, &sz);
+ return sz;
+}
+
/*
** This is the implementation of the multiplex_control() SQL function.
*/
memset(pGroup, 0, sz);
pMultiplexOpen->pGroup = pGroup;
pGroup->bEnabled = -1;
+ pGroup->bTruncate = (flags & SQLITE_OPEN_MAIN_DB)==0;
pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;
if( zName ){
pGroup->bEnabled = 0;
}
}
+ if( sqlite3_uri_parameter(zName, "truncate") ) pGroup->bTruncate = 1;
}
pGroup->zName = p;
memcpy(pGroup->zName, zName, nName+1);
pGroup->flags = flags;
rc = multiplexSubFilename(pGroup, 1);
if( rc==SQLITE_OK ){
- pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
- assert( pSubOpen || rc!=SQLITE_OK );
+ pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
+ if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
}
if( rc==SQLITE_OK ){
sqlite3_int64 sz;
}
}else{
/* If the first overflow file exists and if the size of the main file
- ** is different from the chunk size, that means the chunk size is set
- ** set incorrectly. So fix it.
- **
- ** Or, if the first overflow file does not exist and the main file is
- ** larger than the chunk size, that means the chunk size is too small.
- ** But we have no way of determining the intended chunk size, so
- ** just disable the multiplexor all togethre.
- */
+ ** is different from the chunk size, that means the chunk size is set
+ ** set incorrectly. So fix it.
+ **
+ ** Or, if the first overflow file does not exist and the main file is
+ ** larger than the chunk size, that means the chunk size is too small.
+ ** But we have no way of determining the intended chunk size, so
+ ** just disable the multiplexor all togethre.
+ */
rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
SQLITE_ACCESS_EXISTS, &bExists);
- if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
+ bExists = multiplexSubSize(pGroup, 1, &rc)>0;
+ if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
&& sz!=pGroup->szChunk ){
pGroup->szChunk = sz;
}else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_READ;
}else{
}else{
while( iAmt > 0 ){
int i = (int)(iOfst / pGroup->szChunk);
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
if( extra<0 ) extra = 0;
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_WRITE;
}else{
}else{
while( rc==SQLITE_OK && iAmt>0 ){
int i = (int)(iOfst / pGroup->szChunk);
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
if( pSubOpen ){
int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
pGroup->szChunk;
int rc = SQLITE_OK;
multiplexEnter();
if( !pGroup->bEnabled ){
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_TRUNCATE;
}else{
rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
}
- }else if( (pGroup->flags & SQLITE_OPEN_MAIN_DB)==0 ){
- int rc2;
+ }else{
int i;
+ int iBaseGroup = (int)(size / pGroup->szChunk);
sqlite3_file *pSubOpen;
sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
/* delete the chunks above the truncate limit */
- for(i=(int)(size / pGroup->szChunk)+1; i<pGroup->nReal; i++){
- multiplexSubClose(pGroup, i, pOrigVfs);
+ for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
+ if( pGroup->bTruncate ){
+ multiplexSubClose(pGroup, i, pOrigVfs);
+ }else{
+ pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
+ if( pSubOpen ){
+ rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
+ }
+ }
}
- pSubOpen = multiplexSubOpen(pGroup, (int)(size/pGroup->szChunk), &rc2,0);
- if( pSubOpen ){
- rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
- if( rc2!=SQLITE_OK ) rc = rc2;
- }else{
- rc = SQLITE_IOERR_TRUNCATE;
+ if( rc==SQLITE_OK ){
+ pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
+ if( pSubOpen ){
+ rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
+ }
}
+ if( rc ) rc = SQLITE_IOERR_TRUNCATE;
}
multiplexLeave();
return rc;
int i;
multiplexEnter();
if( !pGroup->bEnabled ){
- sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen==0 ){
rc = SQLITE_IOERR_FSTAT;
}else{
rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
}
}else{
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;
*pSize = 0;
for(i=0; rc==SQLITE_OK; i++){
- sqlite3_file *pSubOpen = 0;
- int exists = 0;
- rc = multiplexSubFilename(pGroup, i);
- if( rc!=SQLITE_OK ) break;
- if( pGroup->nReal>i && pGroup->aReal[i].p!=0 ){
- exists = 1;
- }else if( (pGroup->flags & SQLITE_OPEN_DELETEONCLOSE)==0 ){
- const char *zReal = pGroup->aReal[i].z;
- rc = pOrigVfs->xAccess(pOrigVfs, zReal, SQLITE_ACCESS_EXISTS, &exists);
- }
- if( exists==0 ){
- /* stop at first "gap" or IO error. */
- break;
- }
- if( rc==SQLITE_OK ){
- pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
- }
- assert( pSubOpen || rc!=SQLITE_OK );
- if( rc==SQLITE_OK ){
- sqlite3_int64 sz = 0;
- rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
- if( rc==SQLITE_OK && sz>pGroup->szChunk ){
- rc = SQLITE_IOERR_FSTAT;
- }
- *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
- }
+ sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
+ if( sz==0 ) break;
+ *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
}
}
multiplexLeave();
static int multiplexLock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xLock(pSubOpen, lock);
}
static int multiplexUnlock(sqlite3_file *pConn, int lock){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
}
static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}
rc = SQLITE_OK;
break;
default:
- pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
+ pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
static int multiplexSectorSize(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
return pSubOpen->pMethods->xSectorSize(pSubOpen);
}
static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
}
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
}
){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
}
static void multiplexShmBarrier(sqlite3_file *pConn){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
pSubOpen->pMethods->xShmBarrier(pSubOpen);
}
static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
multiplexConn *p = (multiplexConn*)pConn;
int rc;
- sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
+ sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
if( pSubOpen ){
return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
}