/* This is the limit on the chunk size. It may be changed by calling
** the xFileControl() interface. It will be rounded up to a
-** multiple of SQLITE_MAX_PAGE_SIZE.
+** multiple of SQLITE_MAX_PAGE_SIZE. We default it here to 1GB.
*/
-#define SQLITE_MULTIPLEX_CHUNK_SIZE (SQLITE_MAX_PAGE_SIZE*32)
+#define SQLITE_MULTIPLEX_CHUNK_SIZE (SQLITE_MAX_PAGE_SIZE*16384)
+
/* Default limit on number of chunks. Care should be taken
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
** format specifier. It may be changed by calling
char *p = (char *)&pGroup[1];
pMultiplexOpen->pGroup = pGroup;
memset(pGroup, 0, sz);
+ pGroup->bEnabled = -1;
pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
pGroup->pReal = (sqlite3_file **)p;
pGroup->flags = flags;
pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
if( pSubOpen ){
+ /* if this file is already larger than chunk size, disable
+ ** the multiplex feature.
+ */
+ sqlite3_int64 sz;
+ int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
+ pGroup->bEnabled = 0;
+ }
if( pSubOpen->pMethods->iVersion==1 ){
pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
}else{
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
- while( iAmt > 0 ){
- int i = (int)(iOfst / pGroup->nChunkSize);
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
- if( pSubOpen ){
- int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
- if( extra<0 ) extra = 0;
- iAmt -= extra;
- rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
- if( rc!=SQLITE_OK ) break;
- pBuf = (char *)pBuf + iAmt;
- iOfst += iAmt;
- iAmt = extra;
- }else{
- rc = SQLITE_IOERR_READ;
- break;
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : 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);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_READ;
+ break;
+ }
}
}
multiplexLeave();
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
multiplexEnter();
- while( iAmt > 0 ){
- int i = (int)(iOfst / pGroup->nChunkSize);
- sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
- if( pSubOpen ){
- int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
- if( extra<0 ) extra = 0;
- iAmt -= extra;
- rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
- if( rc!=SQLITE_OK ) break;
- pBuf = (char *)pBuf + iAmt;
- iOfst += iAmt;
- iAmt = extra;
- }else{
- rc = SQLITE_IOERR_WRITE;
- break;
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : 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);
+ if( pSubOpen ){
+ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
+ if( extra<0 ) extra = 0;
+ iAmt -= extra;
+ rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
+ if( rc!=SQLITE_OK ) break;
+ pBuf = (char *)pBuf + iAmt;
+ iOfst += iAmt;
+ iAmt = extra;
+ }else{
+ rc = SQLITE_IOERR_WRITE;
+ break;
+ }
}
}
multiplexLeave();
multiplexConn *p = (multiplexConn*)pConn;
multiplexGroup *pGroup = p->pGroup;
int rc = SQLITE_OK;
- int rc2;
- int i;
- sqlite3_file *pSubOpen;
- sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
multiplexEnter();
- memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
- /* delete the chunks above the truncate limit */
- for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; 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;
- }
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
+ }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; i<pGroup->nMaxChunks; 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);
+ 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);
+ 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);
- if( pSubOpen ){
- rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
- if( rc2!=SQLITE_OK ) rc = rc2;
- }else{
- rc = SQLITE_IOERR_TRUNCATE;
+ rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
+ if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
+ }
+ pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL);
+ if( pSubOpen ){
+ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
+ if( rc2!=SQLITE_OK ) rc = rc2;
+ }else{
+ rc = SQLITE_IOERR_TRUNCATE;
+ }
}
multiplexLeave();
return rc;
int rc2;
int i;
multiplexEnter();
- *pSize = 0;
- for(i=0; i<pGroup->nMaxChunks; 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 ){
+ if( !pGroup->bEnabled ){
+ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
+ rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
+ }else{
+ *pSize = 0;
+ for(i=0; i<pGroup->nMaxChunks; 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);
+ 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);
+ 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;
+ }
}
- 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);
+ if( pSubOpen ){
+ sqlite3_int64 sz;
+ rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
+ if( rc2!=SQLITE_OK ){
+ rc = rc2;
+ }else{
+ if( sz>pGroup->nChunkSize ){
+ rc = SQLITE_IOERR_FSTAT;
+ }
+ *pSize += sz;
+ }
}else{
- /* stop at first "gap" */
break;
}
}
- if( pSubOpen ){
- sqlite3_int64 sz;
- rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
- if( rc2!=SQLITE_OK ){
- rc = rc2;
- }else{
- if( sz>pGroup->nChunkSize ){
- rc = SQLITE_IOERR_FSTAT;
- }
- *pSize += sz;
- }
- }else{
- break;
- }
}
multiplexLeave();
return rc;
return SQLITE_IOERR_CHECKRESERVEDLOCK;
}
-/* Pass xFileControl requests through to the original VFS unchanged.
+/* Pass xFileControl requests through to the original VFS unchanged,
+** except for any MULTIPLEX_CTRL_* requests here.
*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
multiplexConn *p = (multiplexConn*)pConn;
{ 0, 0, 0 }
};
- if( objc!=4 && objc!=5 ){
- Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND ?INT-VALUE?");
+ if( objc!=5 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
return TCL_ERROR;
}
switch( aSub[idx].argtype ){
case 1:
- if( objc!=5 ){
- Tcl_WrongNumArgs(interp, 4, objv, "INT-VALUE");
- return TCL_ERROR;
- }
if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
return TCL_ERROR;
}
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
-set g_chunk_size 2147483648
+set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
set g_max_chunks 32
# This handles appending the chunk number
do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK}
-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.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.8 { db close } {}
-do_test multiplex-1.9.9 { 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 { execsql { SELECT multiplex_control(2, 32768); } } {SQLITE_OK}
-do_test multiplex-1.10.4 { execsql { SELECT multiplex_control(3, -1); } } {SQLITE_MISUSE}
-do_test multiplex-1.10.5 { execsql { SELECT multiplex_control(2, -1); } } {SQLITE_MISUSE}
-do_test multiplex-1.10.6 { execsql { SELECT multiplex_control(2, 31); } } {SQLITE_OK}
-do_test multiplex-1.10.7 { execsql { SELECT multiplex_control(3, 100); } } {SQLITE_MISUSE}
-do_test multiplex-1.10.8 { db close } {}
-do_test multiplex-1.10.9 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+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.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.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 { execsql { SELECT multiplex_control(2, 32768); } } {SQLITE_OK}
+do_test multiplex-1.10.4 { execsql { SELECT multiplex_control(3, -1); } } {SQLITE_MISUSE}
+do_test multiplex-1.10.5 { execsql { SELECT multiplex_control(2, -1); } } {SQLITE_MISUSE}
+do_test multiplex-1.10.6 { execsql { SELECT multiplex_control(2, 31); } } {SQLITE_OK}
+do_test multiplex-1.10.7 { execsql { SELECT multiplex_control(3, 100); } } {SQLITE_MISUSE}
+do_test multiplex-1.10.8 { execsql { SELECT multiplex_control(2, 1073741824); } } {SQLITE_OK}
+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}
+do_test multiplex-1.11.2 { sqlite3 db test.db } {}
+do_test multiplex-1.11.3 { sqlite3_multiplex_control db main enable 0 } {SQLITE_OK}
+do_test multiplex-1.11.4 { sqlite3_multiplex_control db main enable 1 } {SQLITE_OK}
+do_test multiplex-1.11.5 { sqlite3_multiplex_control db main enable -1 } {SQLITE_OK}
+do_test multiplex-1.11.6 { db close } {}
+do_test multiplex-1.11.7 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+do_test multiplex-1.12.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-1.12.2 { sqlite3 db test.db } {}
+do_test multiplex-1.12.3 { execsql { SELECT multiplex_control(1, 0); } } {SQLITE_OK}
+do_test multiplex-1.12.4 { execsql { SELECT multiplex_control(1, 1); } } {SQLITE_OK}
+do_test multiplex-1.12.5 { execsql { SELECT multiplex_control(1, -1); } } {SQLITE_OK}
+do_test multiplex-1.12.6 { db close } {}
+do_test multiplex-1.12.7 { sqlite3_multiplex_shutdown } {SQLITE_OK}
#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback
#
# multiplex-2.3.*: Open and close a second db.
#
-# multiplex-2.4.*: Try to shutdown the multiplex system befor e closing the db
+# multiplex-2.4.*: Try to shutdown the multiplex system before closing the db
# file. Check that this fails and the multiplex system still works
# afterwards. Then close the database and successfully shut
# down the multiplex system.
#
# multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
# well as varying journal mode.
+#
+# multiplex-2.7.*: Disable/enable tests.
+#
sqlite3_multiplex_initialize "" 1
multiplex_set db main 32768 16
}
}
+do_test multiplex-2.7.1 { multiplex_delete test.db } {}
+do_test multiplex-2.7.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK}
+do_test multiplex-2.7.3 { sqlite3 db test.db } {}
+do_test multiplex-2.7.4 { execsql { SELECT multiplex_control(2, 65536); } } {SQLITE_OK}
+do_test multiplex-2.7.5 { execsql { SELECT multiplex_control(1, 0); } } {SQLITE_OK}
+do_test multiplex-2.7.6 {
+ execsql {
+ CREATE TABLE t1(a PRIMARY KEY, b);
+ INSERT INTO t1 VALUES(1, randomblob(1000));
+ }
+} {}
+# verify only one file, and file size is less than chunks size
+do_test multiplex-2.7.7 { expr ([file size [multiplex_name test.db 0]] < 65536) } {1}
+do_test multiplex-2.7.8 { file exists [multiplex_name test.db 1] } {0}
+do_test multiplex-2.7.9 {
+ execsql {
+ INSERT INTO t1 VALUES(1, randomblob(65536));
+ }
+} {}
+# verify only one file, and file size exceeds chunks size
+do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1}
+do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] } {0}
+do_test multiplex-2.7.12 { db close } {}
+do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown } {SQLITE_OK}
+
+
#-------------------------------------------------------------------------
# Try some tests with more than one connection to a database file. Still
# in rollback mode.