Index: ext/session/session1.test ================================================================== --- ext/session/session1.test +++ ext/session/session1.test @@ -652,10 +652,17 @@ execsql BEGIN catch { sqlite3changeset_apply_v2 -nosavepoint db $C xConflict } msg execsql { SELECT * FROM x1 } } {1 one 2 two 3 iii} execsql ROLLBACK + +do_test $tn.14.1 { sqlite3session_config strm_size -1 } 64 +do_test $tn.14.2 { sqlite3session_config strm_size 65536 } 65536 +do_test $tn.14.3 { sqlite3session_config strm_size 64 } 64 +do_test $tn.14.4 { + list [catch {sqlite3session_config invalid 123} msg] $msg +} {1 SQLITE_MISUSE} }] } Index: ext/session/sqlite3session.c ================================================================== --- ext/session/sqlite3session.c +++ ext/session/sqlite3session.c @@ -23,10 +23,12 @@ # else # define SESSIONS_STRM_CHUNK_SIZE 1024 # endif #endif +static int sessions_strm_chunk_size = SESSIONS_STRM_CHUNK_SIZE; + typedef struct SessionHook SessionHook; struct SessionHook { void *pCtx; int (*xOld)(void*,int,sqlite3_value**); int (*xNew)(void*,int,sqlite3_value**); @@ -2395,16 +2397,16 @@ } if( rc==SQLITE_OK ){ rc = sqlite3_reset(pSel); } - /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass + /* If the buffer is now larger than sessions_strm_chunk_size, pass ** its contents to the xOutput() callback. */ if( xOutput && rc==SQLITE_OK && buf.nBuf>nNoop - && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE + && buf.nBuf>sessions_strm_chunk_size ){ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf); nNoop = -1; buf.nBuf = 0; } @@ -2612,11 +2614,11 @@ /* ** If the SessionInput object passed as the only argument is a streaming ** object and the buffer is full, discard some data to free up space. */ static void sessionDiscardData(SessionInput *pIn){ - if( pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){ + if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){ int nMove = pIn->buf.nBuf - pIn->iNext; assert( nMove>=0 ); if( nMove>0 ){ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove); } @@ -2635,11 +2637,11 @@ */ static int sessionInputBuffer(SessionInput *pIn, int nByte){ int rc = SQLITE_OK; if( pIn->xInput ){ while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){ - int nNew = SESSIONS_STRM_CHUNK_SIZE; + int nNew = sessions_strm_chunk_size; if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn); if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){ rc = pIn->xInput(pIn->pIn, &pIn->buf.aBuf[pIn->buf.nBuf], &nNew); if( nNew==0 ){ @@ -3360,11 +3362,11 @@ rc = SQLITE_CORRUPT_BKPT; goto finished_invert; } assert( rc==SQLITE_OK ); - if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){ + if( xOutput && sOut.nBuf>=sessions_strm_chunk_size ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); sOut.nBuf = 0; if( rc!=SQLITE_OK ) goto finished_invert; } } @@ -4893,11 +4895,11 @@ SessionChange *p; for(p=pTab->apChange[i]; p; p=p->pNext){ sessionAppendByte(&buf, p->op, &rc); sessionAppendByte(&buf, p->bIndirect, &rc); sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc); - if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){ + if( rc==SQLITE_OK && xOutput && buf.nBuf>=sessions_strm_chunk_size ){ rc = xOutput(pOut, buf.aBuf, buf.nBuf); buf.nBuf = 0; } } } @@ -5289,11 +5291,11 @@ if( bDone==0 ){ sessionAppendByte(&sOut, pIter->op, &rc); sessionAppendByte(&sOut, pIter->bIndirect, &rc); sessionAppendBlob(&sOut, aRec, nRec, &rc); } - if( rc==SQLITE_OK && xOutput && sOut.nBuf>SESSIONS_STRM_CHUNK_SIZE ){ + if( rc==SQLITE_OK && xOutput && sOut.nBuf>sessions_strm_chunk_size ){ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf); sOut.nBuf = 0; } if( rc ) break; } @@ -5399,7 +5401,28 @@ if( p ){ sessionDeleteTable(p->grp.pList); sqlite3_free(p); } } + +/* +** Global configuration +*/ +int sqlite3session_config(int op, void *pArg){ + int rc = SQLITE_OK; + switch( op ){ + case SQLITE_SESSION_CONFIG_STRMSIZE: { + int *pInt = (int*)pArg; + if( *pInt>0 ){ + sessions_strm_chunk_size = *pInt; + } + *pInt = sessions_strm_chunk_size; + break; + } + default: + rc = SQLITE_MISUSE; + break; + } + return rc; +} #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ Index: ext/session/sqlite3session.h ================================================================== --- ext/session/sqlite3session.h +++ ext/session/sqlite3session.h @@ -1608,10 +1608,49 @@ void *pIn, int (*xOutput)(void *pOut, const void *pData, int nData), void *pOut ); +/* +** CAPI3REF: Configure global parameters +** +** The sqlite3session_config() interface is used to make global configuration +** changes to the sessions module in order to tune it to the specific needs +** of the application. +** +** The sqlite3session_config() interface is not threadsafe. If it is invoked +** while any other thread is inside any other sessions method then the +** results are undefined. Furthermore, if it is invoked after any sessions +** related objects have been created, the results are also undefined. +** +** The first argument to the sqlite3session_config() function must be one +** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The +** interpretation of the (void*) value passed as the second parameter and +** the effect of calling this function depends on the value of the first +** parameter. +** +**
+**
SQLITE_SESSION_CONFIG_STRMSIZE
+** By default, the sessions module streaming interfaces attempt to input +** and output data in approximately 1 KiB chunks. This operand may be used +** to set and query the value of this configuration setting. The pointer +** passed as the second argument must point to a value of type (int). +** If this value is greater than 0, it is used as the new streaming data +** chunk size for both input and output. Before returning, the (int) value +** pointed to by pArg is set to the final value of the streaming interface +** chunk size. +**
+** +** This function returns SQLITE_OK if successful, or an SQLite error code +** otherwise. +*/ +int sqlite3session_config(int op, void *pArg); + +/* +** CAPI3REF: Values for sqlite3session_config(). +*/ +#define SQLITE_SESSION_CONFIG_STRMSIZE 1 /* ** Make sure we can call this stuff from C++. */ #ifdef __cplusplus Index: ext/session/test_session.c ================================================================== --- ext/session/test_session.c +++ ext/session/test_session.c @@ -1236,10 +1236,49 @@ (ClientData)pNew, test_rebaser_del ); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } + +/* +** tclcmd: sqlite3rebaser_configure OP VALUE +*/ +static int SQLITE_TCLAPI test_sqlite3session_config( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + struct ConfigOpt { + const char *zSub; + int op; + } aSub[] = { + { "strm_size", SQLITE_SESSION_CONFIG_STRMSIZE }, + { "invalid", 0 }, + { 0 } + }; + int rc; + int iSub; + int iVal; + + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "OP VALUE"); + return SQLITE_ERROR; + } + rc = Tcl_GetIndexFromObjStruct(interp, + objv[1], aSub, sizeof(aSub[0]), "sub-command", 0, &iSub + ); + if( rc!=TCL_OK ) return rc; + if( Tcl_GetIntFromObj(interp, objv[2], &iVal) ) return TCL_ERROR; + + rc = sqlite3session_config(aSub[iSub].op, (void*)&iVal); + if( rc!=SQLITE_OK ){ + return test_session_error(interp, rc, 0); + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(iVal)); + return TCL_OK; +} int TestSession_Init(Tcl_Interp *interp){ struct Cmd { const char *zCmd; Tcl_ObjCmdProc *xProc; @@ -1252,10 +1291,11 @@ { "sqlite3changeset_apply_v2", test_sqlite3changeset_apply_v2 }, { "sqlite3changeset_apply_replace_all", test_sqlite3changeset_apply_replace_all }, { "sql_exec_changeset", test_sql_exec_changeset }, { "sqlite3rebaser_create", test_sqlite3rebaser_create }, + { "sqlite3session_config", test_sqlite3session_config }, }; int i; for(i=0; i